diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1415433 --- /dev/null +++ b/.gitignore @@ -0,0 +1,350 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# OS generated files # +###################### +.DS_Store +.DS_Store? + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Bb]uild/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ +# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true +**/wwwroot/lib/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# User CMake presets +CMakeUserPresets.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6ee7c99 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,195 @@ +# Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +cmake_minimum_required (VERSION 3.21.0) +project(iris VERSION 1.0.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake/) + +# --------------------------------------------------------------------------------------- +# Options +# --------------------------------------------------------------------------------------- +option(BUILD_TESTS "Build library tests" OFF) +option(BUILD_EXAMPLE_APP "Build Iris example console application" OFF) +option(EXPORT_IRIS "Export and install library" ON) +option(BUILD_SHARED_LIBS "Build iris as a shared library" OFF) +option(BUILD_COVERAGE "Builds code coverage target" OFF) + +if (NOT UNIX) + set(BUILD_COVERAGE OFF) + message("Code coverage can only be built in Linux systems") +endif() + +# --------------------------------------------------------------------------------------- +# Iris Library +# --------------------------------------------------------------------------------------- + +set(PUBLIC_HEADERS + "include/iris/VideoAnalyser.h" + "include/iris/Configuration.h" + "include/iris/Log.h" + "include/iris/Result.h" + "include/iris/TotalFlashIncidents.h" +) + +source_group("Public header files" FILES ${PUBLIC_HEADERS}) + +set(SOURCE_FILES + "src/VideoAnalyser.cpp" + "src/FrameRgbConverter.cpp" + "src/Flash.cpp" + "src/RelativeLuminance.cpp" + "src/RedSaturation.h" + "src/RedSaturation.cpp" + "src/CDLuminance.cpp" + "src/TransitionEvaluator.cpp" + "src/Configuration.cpp" + "src/ConfigurationParams.h" + "src/Flash.h" + "src/RelativeLuminance.h" + "src/CDLuminance.h" + "src/FrameRgbConverter.h" + "src/TransitionEvaluator.h" + "src/FrameData.h" + "src/FlashDetection.h" + "src/FlashDetection.cpp" + "src/IrisFrame.h" + "src/Log.cpp" + "src/PatternDetection.h" + "src/PatternDetection.cpp" + "src/PhotosensitivityDetector.h" +) + +source_group("Source files" FILES ${SOURCE_FILES}) + +# Dependencies +find_package(OpenCV CONFIG REQUIRED) +add_subdirectory ("utils") + +if(BUILD_SHARED_LIBS) + message("BUILD SHARED LIBRARIES") + if(WIN32) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + message("Export symbols") + endif() +endif() + +if(BUILD_COVERAGE) + message("BUILD CODE COVERAGE") + SET(GCC_COVERAGE_COMPILE_FLAGS "-g -O0 -coverage -fprofile-arcs -ftest-coverage") + SET(GCC_COVERAGE_LINK_FLAGS "-coverage -lgcov") + SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}" ) + SET( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}" ) +endif() + +# Library definition +add_library(${PROJECT_NAME} ${PUBLIC_HEADERS} ${SOURCE_FILES}) + +if(BUILD_SHARED_LIBS) + if(WIN32) + target_compile_definitions(${PROJECT_NAME} PUBLIC IRIS_SHARED PRIVATE IRIS_EXPORT) + endif() +endif() + +set_target_properties(${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX "d") + +target_include_directories(${PROJECT_NAME} + PUBLIC + # where the top-level project will look for the library's public headers + "$" + # where external projects will look for the library's public headers + "$" +) + +target_compile_options(${PROJECT_NAME} + PRIVATE $<$:-Wall> +) + +set_target_properties(${PROJECT_NAME} PROPERTIES + PUBLIC_HEADER "${PUBLIC_HEADERS}" +) + +target_link_libraries(${PROJECT_NAME} + PUBLIC + utils + ${OpenCV_LIBS} +) + +# --------------------------------------------------------------------------------------- +# Build Example App +# --------------------------------------------------------------------------------------- + +if(BUILD_EXAMPLE_APP) + message("Build Iris example app") + add_subdirectory ("example") +endif() + +# --------------------------------------------------------------------------------------- +# Build Tests +# --------------------------------------------------------------------------------------- +if(BUILD_TESTS) + message("Build tests") + enable_testing() + add_subdirectory("test/Iris.Tests") + add_subdirectory("test/AddressSanitizer.Tests") +endif() + +# --------------------------------------------------------------------------------------- +# Install +# --------------------------------------------------------------------------------------- + +if(EXPORT_IRIS) +message("Export Iris") + +include(GNUInstallDirs) +set(namespace ${PROJECT_NAME}) + +# install the target and create export-set +install(TARGETS ${PROJECT_NAME} + EXPORT "${PROJECT_NAME}Targets" + # these variable get default values from GNUInstallDirs + #RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # bin + #LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # lib + #ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # lib + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # include +) + +# generate and install export file +install(EXPORT "${PROJECT_NAME}Targets" + FILE "${PROJECT_NAME}Targets.cmake" + NAMESPACE ${namespace}:: + DESTINATION cmake +) + +include(CMakePackageConfigHelpers) + +# generate the version file for the config file +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + VERSION "${version}" + COMPATIBILITY AnyNewerVersion +) + +# create config file +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION cmake +) + +# install config files +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION cmake +) + +# generate the export targets for the build tree +export(EXPORT "${PROJECT_NAME}Targets" + FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}Targets.cmake" + NAMESPACE ${namespace}:: +) + +endif() \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..825f8c6 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,180 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "base", + "description": "sets build and install directory", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/bin/build/${presetName}", + "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "cacheVariables": { + "CMAKE_INSTALL_PREFIX": "${sourceDir}/bin/install/${presetName}", + "WINDOWS_PIPELINE": false + } + }, + { + "name": "linux-local", + "displayName": "Linux Local", + "description": "Target the Windows Subsystem for Linux (WSL) or a remote Linux system.", + "inherits": [ "base" ], + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++", + "CMAKE_BUILD_TYPE": "Debug" + }, + "environment": { + "VCPKG_ROOT": "/home/cmake-local/vcpkg" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "Linux" ] }, + "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" } + } + }, + { + "name": "linux-debug", + "displayName": "Linux Debug", + "description": "Target the Windows Subsystem for Linux (WSL) or a remote Linux system.", + "inherits": [ "base" ], + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++", + "CMAKE_BUILD_TYPE": "Debug" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "Linux" ] }, + "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" } + } + }, + { + "name": "linux-release", + "displayName": "Linux Release", + "description": "Target the Windows Subsystem for Linux (WSL) or a remote Linux system.", + "inherits": [ "linux-debug" ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "macos-default", + "displayName": "macOS Debug", + "description": "Target a remote macOS system with Ninja", + "inherits": [ "base" ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_C_COMPILER": "/usr/bin/clang", + "CMAKE_CXX_COMPILER": "/usr/bin/clang++" + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "macOS" ] }, + "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" } + } + }, + { + "name": "windows-debug", + "displayName": "Windows x64 Debug", + "description": "Target Windows with the Visual Studio development environment.", + "inherits": [ "base" ], + "architecture": { + "value": "x64", + "strategy": "external" + }, + "cacheVariables": { + "CMAKE_C_COMPILER": "cl.exe", + "CMAKE_CXX_COMPILER": "cl.exe", + "CMAKE_BUILD_TYPE": "Debug" + }, + "vendor": { "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "Windows" ] } } + }, + { + "name": "windows-release", + "displayName": "Windows x64 Release", + "description": "Target Windows with the Visual Studio development environment.", + "inherits": [ "windows-debug" ], + "cacheVariables": { + "WINDOWS_PIPELINE": true, + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "windows-releaseWithDebInfo", + "displayName": "Windows x64 Release", + "description": "Target Windows with the Visual Studio development environment.", + "inherits": [ "windows-debug" ], + "cacheVariables": { + "WINDOWS_PIPELINE": true, + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + } + ], + "buildPresets": [ + { + "name": "core-build", + "description": "Inherits environment from base configurePreset", + "configurePreset": "base", + "hidden": true, + "inheritConfigureEnvironment": true + }, + { + "name": "windows-debug", + "description": "Windows debug build", + "inherits": "core-build", + "configurePreset": "windows-debug" + }, + { + "name": "windows-release", + "description": "Windows release build", + "inherits": "core-build", + "configurePreset": "windows-release" + }, + { + "name": "windows-releaseWithDebInfo", + "description": "Windows release with deb info build", + "inherits": "core-build", + "configurePreset": "windows-releaseWithDebInfo" + }, + { + "name": "linux-debug", + "configurePreset": "linux-debug", + "inherits": "core-build" + }, + { + "name": "code-coverage", + "configurePreset": "linux-debug", + "inherits": "core-build", + "targets": "coverage" + }, + { + "name": "linux-release", + "configurePreset": "linux-release", + "inherits": "core-build" + }, + { + "name": "macos-debug", + "description": "MacOS debug build", + "inherits": "core-build", + "configurePreset": "macos-default" + } + ], + "testPresets": [ + { + "name": "videolib-linux", + "configurePreset": "linux-release", + "environment": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + { + "name": "videolib-windows", + "configurePreset": "windows-release", + "environment": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + ] +} \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..f6b4c7b --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,13 @@ +Copyright (c) 2023 Electronic Arts Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of Electronic Arts, Inc. ("EA") nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 0000000..bfefd39 --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,629 @@ +IRIS utilizes the following open source software. Copies of the open source licenses are provided. + + +For use of FFMPEG +Copyright (c) FFMPEG Authors and Maintainers + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +For use of nlohmann-json +Copyright (c) 2013-2022 Niels Lohmann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +For use of GIFLIB +The GIFLIB distribution is Copyright (c) 1997 Eric S. Raymond + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +For use of gtest +Copyright 2008, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +For use of spdlog +Copyright (c) 2016 Gabi Melman. + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +-- NOTE: Third party dependency used by this software -- +This software depends on the fmt lib (MIT License), +and users must comply to its license: https://github.com/fmtlib/fmt/blob/master/LICENSE.rst + + +For use of OpenCV +Copyright (C) 2000-2022, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015-2022, OpenCV Foundation, all rights reserved. +Copyright (C) 2008-2016, Itseez Inc., all rights reserved. +Copyright (C) 2019-2022, Xperience AI, all rights reserved. +Copyright (C) 2019-2022, Shenzhen Institute of Artificial Intelligence and + Robotics for Society, all rights reserved. + + +Third party copyrights are property of their respective owners. + + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + + + http://www.apache.org/licenses/LICENSE-2.0 + + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + + + + +--- OPEN SOURCE LICENSES --- + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + + 1. Definitions. + + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + + END OF TERMS AND CONDITIONS + + + APPENDIX: How to apply the Apache License to your work. + + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + + Copyright [yyyy] [name of copyright owner] + + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + + http://www.apache.org/licenses/LICENSE-2.0 + + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + + +GNU LESSER GENERAL PUBLIC LICENSE +Version 2.1, February 1999 + + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + +Preamble +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. + + +This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. + + +When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. + + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. + + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. + + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. + + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. + + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. + + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. + + +When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. + + +We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. + + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. + + +In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. + + +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. + + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. + + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + + +A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. + + +The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) + + +"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. + + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. + + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. + + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + +a) The modified work must itself be a software library. +b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. +c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. +d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. +(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) + + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. + + +In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. + + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. + + +This option is useful when you wish to copy part of the code of the Library into a program that is not a library. + + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. + + +If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. + + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + + +However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. + + +When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. + + +If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) + + +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. + + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. + + +You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: + + +a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) +b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. +c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. +d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. +e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. +For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + + +It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. + + +7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: + + +a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. +b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + + +9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. + + +10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. + + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. + + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + + +Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. + + +14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + + +NO WARRANTY + + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + +END OF TERMS AND CONDITIONS +How to Apply These Terms to Your New Libraries +If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). + + +To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + +one line to give the library's name and an idea of what it does. +Copyright (C) year name of author + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Also add information on how to contact you by electronic and paper mail. + + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: + + +Yoyodyne, Inc., hereby disclaims all copyright interest in +the library `Frob' (a library for tweaking knobs) written +by James Random Hacker. + + +signature of Ty Coon, 1 April 1990 +Ty Coon, President of Vice +That's all there is to it! + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..7e4fab3 --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# IRIS + +IRIS is a solution created by EA to identify video footage that could potentially cause photosensitive epileptic risks. IRIS is not intended to guarantee, certify or otherwise validate visual content's compliance with legal, regulatory or other requirements. + +The cross-platform library analyses video content and checks for flashes and spatial patterns that could be harmful for people with photosensitive epilepsy, or cause discomfort in viewers. + +IRIS detects the following issues: +- Luminance flashes. +- Red saturation flashes. +- Spatial patterns (with clear contrast and regularity between pattern components). + +According to IRIS' photosensitive epilepsy criteria: +- For luminance and red saturation, whenever there are more than 3 flashes per second in any given second of the video, it is marked as a flash failure. +- For luminance and red saturation, whenever there are between 2-3 flashes per second in any given 5 consecutive seconds, it is marked as an extended flash failure. +- For spatial patterns, whenever there is a harmful pattern present for half a second or more, it is marked as a pattern failure. + +The library outputs the results of the analysis in a CSV file with the following columns: +- Frame: the frame index at this moment of the video. +- Time Stamp: video time stamp. +- Flash Area Luminance: the area proportion of the luminance change. +- Average Luminance Difference Accumulation: this is the value that’s used to determine whether a new luminance transition has occurred or not. If it’s equal or greater than 0.1 (or equal or lower than -0.1) a new transition would have occurred. +- Flash Area Red: the area proportion of the red change between this frame and the one before. +- Average Red Difference Accumulation: this is the value that’s used to determine whether a new red transition has occurred or not. If it’s equal or greater than 20 (or equal or lower than -20) a new transition would have occurred. +- Luminance Transitions: the amount of luminance transitions that have occurred from this moment up to one second before. +- Red Transitions: the amount of red transitions that have occurred from this moment up to one second before. +- Luminance Extended Fail Count: the amount of frames that were luminance flashing between 4 and 6 transitions from this moment up to 5 seconds before. +- Red Extended Fail Count: the amount of frames that were red flashing between 4 and 6 transitions from this moment up to 5 seconds before. +- Pattern Area: area of the bounding rect. of the pattern. +- Pattern Detected Lines: number of detected pattern lines. +- Luminance Frame Result: frame luminance status. + - 0: pass. + - 1: flash warning (transitions have surpassed the 2 flashes, 4 transitions, per second threshold). + - 2: extended flash failure. + - 3: flash failure. +- Red Frame Result: frame red status. + - 0: pass. + - 1: flash warning (transitions have surpassed the 2 flashes, 4 transitions, per second threshold). + - 2: extended flash failure. + - 3: flash failure. +- Pattern Frame Result: pattern frame status. + - 0: pass. + - 1: pattern failure. + + +## Example app +The example app project provides a console application in which to run IRIS. The application expects videos to be set in a "TestVideos" directory to analyse and generate the results in the "Results" directory. Both these directories can be automatically generated by the example app by executing it. + +### Optional arguments +When running IRIS in the example app, the following command line arguments can be passed: +- `-j`: when passing true/1 generates the results in a json file. +- `-v`: the path to a video can be specified as to. +- `-p`: enabled/disable the pattern detection (true/1 or false/0). +- `-l`: specify the luminance type for the luminance calculations (CD || RELATIVE), relative luminance is the default and standard. + + +## Configuration +The [appsettings.json](config/appsettings.json) is a file where values used by IRIS are defined and can be modified to alter the execution of the analysis. These default values are configured to detect photosensitive content based on publicly available guidelines. IRIS is not intended to guarantee, certify or otherwise validate video content’s photosensitivity compliance. Modifying IRIS’s default values should be done at your own risk, understanding that doing so may impact IRIS’s results and its ability to detect photosensitivity issues. + + +## How to build +IRIS uses by default CMake, Ninja and the vcpkg package manager for handling dependencies. All the dependencies are specified in the [vcpk.json](vcpkg.json) file. If you want CMake to automatically run vcpkg and compile all dependencies for you, you must set your VCPKG root folder as an environment variable called "VCPKG_ROOT" or add it as a variable in the CMakePresets.json. + +IRIS uses cmake presets to build with different configurations e.g.: + +`>cmake --preset windows-release` +`>cmake --build --preset windows-release` + +More presets can be added to the CMakePresets.json file or defined in a CMakeUserPresets.json file. + +Build options: +- BUILD_EXAMPLE_APP: build Iris example console application +- BUILD_SHARED_LIBS: build iris as a shared library +- EXPORT_IRIS: export and install library +- BUILD_TESTS: build library unit tests +- BUILD_COVERAGE: build code coverage (only available for Linux) + + + +## CONTRIBUTING +Before you can contribute, EA must have a Contributor License Agreement (CLA) on file that has been signed by each contributor. You can sign [here](https://electronicarts.na1.echosign.com/public/esignWidget?wid=CBFCIBAA3AAABLblqZhByHRvZqmltGtliuExmuV-WNzlaJGPhbSRg2ufuPsM3P0QmILZjLpkGslg24-UJtek*). diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in new file mode 100644 index 0000000..7cb8364 --- /dev/null +++ b/cmake/Config.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") + +check_required_components(@PROJECT_NAME@) \ No newline at end of file diff --git a/cmake/Coverage.cmake b/cmake/Coverage.cmake new file mode 100644 index 0000000..8e8717d --- /dev/null +++ b/cmake/Coverage.cmake @@ -0,0 +1,19 @@ +function(AddCoverage target) + find_program(LCOV_PATH lcov REQUIRED) + find_program(GENHTML_PATH genhtml REQUIRED) + message("Add coverage to: " ${target}) + message(${LCOV_PATH}) + message("BIN DIR: " ${CMAKE_BINARY_DIR}) + message("SRC DIR: " ${CMAKE_CURRENT_SOURCE_DIR}) + add_custom_target(coverage + COMMENT "Running coverage for ${target}..." + COMMAND ${LCOV_PATH} -d . --zerocounters + COMMAND $ + COMMAND ${LCOV_PATH} -d . --capture -o coverage.info + COMMAND ${LCOV_PATH} -r coverage.info '/usr/include/*' '${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/include/*' '${CMAKE_CURRENT_SOURCE_DIR}/*' '/usr/local/include/*' + -o filtered.info + COMMAND ${GENHTML_PATH} -o coverage filtered.info --legend + COMMAND rm -rf coverage.info filtered.info + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +endfunction() \ No newline at end of file diff --git a/cmake/gcov-llvm-wrapper.sh b/cmake/gcov-llvm-wrapper.sh new file mode 100644 index 0000000..b319995 --- /dev/null +++ b/cmake/gcov-llvm-wrapper.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +exec llvm-cov gcov "$@" \ No newline at end of file diff --git a/config/appsettings.json b/config/appsettings.json new file mode 100644 index 0000000..ea4cee6 --- /dev/null +++ b/config/appsettings.json @@ -0,0 +1,575 @@ +{ + "Luminance": { + "FormulaYval1": 413.435, //CD luminance formula values + "FormulaYval2": 0.002745, + "FormulaYval3": 0.0189623, + "FormulaYvalpow": 2.2, + "RelativeLuminanceFlashThreshold": 0.1, //flash transition + "RelativeDarkLuminanceThreshold": 0.8, + "CdLuminanceFlashThreshold": 20, //flash transition + "CdDarkLuminanceThreshold": 160, + //possible CD luminanve values for look up table + "CdLuminanceValues": [ + 0.07, + 0.09, + 0.12, + 0.15, + 0.18, + 0.22, + 0.27, + 0.31, + 0.37, + 0.42, + 0.48, + 0.55, + 0.62, + 0.69, + 0.77, + 0.85, + 0.94, + 1.03, + 1.13, + 1.23, + 1.34, + 1.45, + 1.57, + 1.69, + 1.82, + 1.95, + 2.09, + 2.23, + 2.37, + 2.53, + 2.68, + 2.85, + 3.01, + 3.19, + 3.37, + 3.55, + 3.74, + 3.93, + 4.13, + 4.34, + 4.55, + 4.77, + 4.99, + 5.21, + 5.45, + 5.68, + 5.93, + 6.18, + 6.43, + 6.69, + 6.96, + 7.23, + 7.51, + 7.79, + 8.08, + 8.38, + 8.68, + 8.98, + 9.3, + 9.61, + 9.94, + 10.27, + 10.6, + 10.94, + 11.29, + 11.64, + 12, + 12.37, + 12.74, + 13.12, + 13.5, + 13.89, + 14.28, + 14.69, + 15.09, + 15.51, + 15.93, + 16.35, + 16.78, + 17.22, + 17.67, + 18.12, + 18.57, + 19.04, + 19.5, + 19.98, + 20.46, + 20.95, + 21.44, + 21.94, + 22.45, + 22.96, + 23.48, + 24.01, + 24.54, + 25.08, + 25.62, + 26.17, + 26.73, + 27.29, + 27.86, + 28.44, + 29.02, + 29.61, + 30.21, + 30.81, + 31.42, + 32.03, + 32.66, + 33.29, + 33.92, + 34.56, + 35.21, + 35.86, + 36.53, + 37.19, + 37.87, + 38.55, + 39.24, + 39.93, + 40.63, + 41.34, + 42.05, + 42.78, + 43.5, + 44.24, + 44.98, + 45.73, + 46.48, + 47.24, + 48.01, + 48.79, + 49.57, + 50.36, + 51.15, + 51.95, + 52.76, + 53.58, + 54.4, + 55.23, + 56.07, + 56.91, + 57.76, + 58.62, + 59.48, + 60.35, + 61.23, + 62.11, + 63, + 63.9, + 64.81, + 65.72, + 66.64, + 67.56, + 68.5, + 69.44, + 70.38, + 71.34, + 72.3, + 73.27, + 74.24, + 75.22, + 76.21, + 77.21, + 78.21, + 79.22, + 80.24, + 81.26, + 82.3, + 83.34, + 84.38, + 85.43, + 86.49, + 87.56, + 88.64, + 89.72, + 90.81, + 91.9, + 93, + 94.11, + 95.23, + 96.36, + 97.49, + 98.63, + 99.77, + 100.93, + 102.09, + 103.25, + 104.43, + 105.61, + 106.8, + 108, + 109.2, + 110.41, + 111.63, + 112.86, + 114.09, + 115.33, + 116.58, + 117.84, + 119.1, + 120.37, + 121.65, + 122.93, + 124.22, + 125.52, + 126.83, + 128.14, + 129.47, + 130.8, + 132.13, + 133.48, + 134.83, + 136.19, + 137.55, + 138.93, + 140.31, + 141.69, + 143.09, + 144.49, + 145.9, + 147.32, + 148.75, + 150.18, + 151.62, + 153.07, + 154.53, + 155.99, + 157.46, + 158.94, + 160.43, + 161.92, + 163.42, + 164.93, + 166.45, + 167.97, + 169.5, + 171.04, + 172.59, + 174.14, + 175.7, + 177.27, + 178.85, + 180.43, + 182.03, + 183.63, + 185.23, + 186.85, + 188.47, + 190.1, + 191.74, + 193.38, + 195.04, + 196.7, + 198.37, + 200 + ] + }, + + "RedSaturation": { + "FlashThreshold": 20, //threshold for red saturation transitions + "RedDarkThreshold": 321 + }, + + "PatternDetection": { + "MinStripes": 6, //min stripes for harmful patterns + "TimeThreshold": 0.5, //max seconds for harmful patterns until failure + "CDDarkLuminanceThreshold": 160, + "RelativeDarkLuminanceThreshold": 0.8, + "AreaProportion": 0.25 + }, + + + "FilePersistence": { + "MaxDataStored": 10 //max stored frame data in memory until persistence + }, + + "TransitionEvaluator": { + "MaxTransitions": 6, //max allowed transitions and max transitions to count for extended fail + "MinTransitions": 4, //amount of min transitions to add to extended fail count + "ExtendedFailSeconds": 4, //max seconds until the start of extended failure + "ExtendedFailWindow": 5 //seconds in extended fail count window + }, + + "FlashDetection": { + "AreaProportion": 0.25, //screen display flashing area + //sRGB possible values for look up table + "sRGBValues": [ + 0, + 0.0003035269835488375, + 0.000607053967097675, + 0.0009105809506465125, + 0.00121410793419535, + 0.0015176349177441874, + 0.001821161901293025, + 0.0021246888848418626, + 0.0024282158683907, + 0.0027317428519395373, + 0.003035269835488375, + 0.003346535763899161, + 0.003676507324047436, + 0.004024717018496307, + 0.004391442037410293, + 0.004776953480693729, + 0.005181516702338386, + 0.005605391624202723, + 0.006048833022857054, + 0.006512090792594475, + 0.006995410187265387, + 0.007499032043226175, + 0.008023192985384994, + 0.008568125618069307, + 0.009134058702220787, + 0.00972121732023785, + 0.010329823029626936, + 0.010960094006488246, + 0.011612245179743885, + 0.012286488356915872, + 0.012983032342173012, + 0.013702083047289686, + 0.014443843596092545, + 0.01520851442291271, + 0.01599629336550963, + 0.016807375752887384, + 0.017641954488384078, + 0.018500220128379697, + 0.019382360956935723, + 0.0202885630566524, + 0.021219010376003555, + 0.022173884793387385, + 0.02315336617811041, + 0.024157632448504756, + 0.02518685962736163, + 0.026241221894849898, + 0.027320891639074894, + 0.028426039504420793, + 0.0295568344378088, + 0.030713443732993635, + 0.03189603307301153, + 0.033104766570885055, + 0.03433980680868217, + 0.03560131487502034, + 0.03688945040110004, + 0.0382043715953465, + 0.03954623527673284, + 0.04091519690685319, + 0.042311410620809675, + 0.043735029256973465, + 0.04518620438567554, + 0.046665086336880095, + 0.04817182422688942, + 0.04970656598412723, + 0.05126945837404324, + 0.052860647023180246, + 0.05448027644244237, + 0.05612849004960009, + 0.05780543019106723, + 0.0595112381629812, + 0.06124605423161761, + 0.06301001765316767, + 0.06480326669290577, + 0.06662593864377289, + 0.06847816984440017, + 0.07036009569659588, + 0.07227185068231748, + 0.07421356838014963, + 0.07618538148130785, + 0.07818742180518633, + 0.08021982031446832, + 0.0822827071298148, + 0.08437621154414882, + 0.08650046203654976, + 0.08865558628577294, + 0.09084171118340768, + 0.09305896284668745, + 0.0953074666309647, + 0.09758734714186246, + 0.09989872824711389, + 0.10224173308810132, + 0.10461648409110419, + 0.10702310297826761, + 0.10946171077829933, + 0.1119324278369056, + 0.11443537382697373, + 0.11697066775851084, + 0.11953842798834562, + 0.12213877222960187, + 0.12477181756095049, + 0.12743768043564743, + 0.1301364766903643, + 0.13286832155381798, + 0.13563332965520566, + 0.13843161503245183, + 0.14126329114027164, + 0.14412847085805777, + 0.14702726649759498, + 0.14995978981060856, + 0.15292615199615017, + 0.1559264637078274, + 0.1589608350608804, + 0.162029375639111, + 0.1651321945016676, + 0.16826940018969075, + 0.1714411007328226, + 0.17464740365558504, + 0.17788841598362912, + 0.18116424424986022, + 0.184474994500441, + 0.18782077230067787, + 0.19120168274079138, + 0.1946178304415758, + 0.19806931955994886, + 0.20155625379439707, + 0.20507873639031693, + 0.20863687014525575, + 0.21223075741405523, + 0.21586050011389926, + 0.2195261997292692, + 0.2232279573168085, + 0.22696587351009836, + 0.23074004852434915, + 0.23455058216100522, + 0.238397573812271, + 0.24228112246555486, + 0.24620132670783548, + 0.25015828472995344, + 0.25415209433082675, + 0.2581828529215958, + 0.26225065752969623, + 0.26635560480286247, + 0.2704977910130658, + 0.27467731206038465, + 0.2788942634768104, + 0.2831487404299921, + 0.2874408377269175, + 0.29177064981753587, + 0.2961382707983211, + 0.3005437944157765, + 0.3049873140698863, + 0.30946892281750854, + 0.31398871337571754, + 0.31854677812509186, + 0.32314320911295075, + 0.3277780980565422, + 0.33245153634617935, + 0.33716361504833037, + 0.3419144249086609, + 0.3467040563550296, + 0.35153259950043936, + 0.3564001441459435, + 0.3613067797835095, + 0.3662525955988395, + 0.3712376804741491, + 0.3762621229909065, + 0.38132601143253014, + 0.386429433787049, + 0.39157247774972326, + 0.39675523072562685, + 0.4019777798321958, + 0.4072402119017367, + 0.41254261348390375, + 0.4178850708481375, + 0.4232676699860717, + 0.4286904966139066, + 0.43415363617474895, + 0.4396571738409188, + 0.44520119451622786, + 0.45078578283822346, + 0.45641102318040466, + 0.4620769996544071, + 0.467783796112159, + 0.47353149614800955, + 0.4793201831008268, + 0.4851499400560704, + 0.4910208498478356, + 0.4969329950608704, + 0.5028864580325687, + 0.5088813208549338, + 0.5149176653765214, + 0.5209955732043543, + 0.5271151257058131, + 0.5332764040105052, + 0.5394794890121072, + 0.5457244613701866, + 0.5520114015120001, + 0.5583403896342679, + 0.5647115057049292, + 0.5711248294648731, + 0.5775804404296506, + 0.5840784178911641, + 0.5906188409193369, + 0.5972017883637634, + 0.6038273388553378, + 0.6104955708078648, + 0.6172065624196511, + 0.6239603916750761, + 0.6307571363461468, + 0.6375968739940326, + 0.6444796819705821, + 0.6514056374198242, + 0.6583748172794485, + 0.665387298282272, + 0.6724431569576875, + 0.6795424696330938, + 0.6866853124353135, + 0.6938717612919899, + 0.7011018919329731, + 0.7083757798916868, + 0.7156935005064807, + 0.7230551289219693, + 0.7304607400903537, + 0.7379104087727308, + 0.7454042095403874, + 0.7529422167760779, + 0.7605245046752924, + 0.768151147247507, + 0.7758222183174236, + 0.7835377915261935, + 0.7912979403326302, + 0.799102738014409, + 0.8069522576692516, + 0.8148465722161012, + 0.8227857543962835, + 0.8307698767746546, + 0.83879901174074, + 0.846873231509858, + 0.8549926081242338, + 0.8631572134541023, + 0.8713671191987972, + 0.8796223968878317, + 0.8879231178819663, + 0.8962693533742664, + 0.9046611743911496, + 0.9130986517934192, + 0.9215818562772946, + 0.9301108583754237, + 0.938685728457888, + 0.9473065367331999, + 0.9559733532492861, + 0.9646862478944651, + 0.9734452903984125, + 0.9822505503331171, + 0.9911020971138298, + 1 + ] + }, + + "VideoAnalyser": { + "LuminanceType": "RELATIVE", //CD || RELATIVE + "PatternDetectionEnabled": false + }, + + "Logging": { + "Console": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + }, + "IncludeScopes": true + } + } +} \ No newline at end of file diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..81f5eb9 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + + +project(IrisApp) + +# Add source to this project's executable. +add_executable (${PROJECT_NAME} "main.cpp") + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/appsettings.json ${CMAKE_CURRENT_BINARY_DIR}/appsettings.json COPYONLY) + +# Included libraries +target_link_libraries(${PROJECT_NAME} iris) + +# add the binary tree to the search path for include files +# so that we will find library headers +target_include_directories(${PROJECT_NAME} PUBLIC + "${PROJECT_SOURCE_DIR}/include" + ) + +# Copy needed dll files into executable when building iris as dll +if(BUILD_SHARED_LIBS) + message("Copy dynamic libraries into IrisApp directory") + file(GLOB_RECURSE DLL_FILES ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/*.dll) + file(COPY ${DLL_FILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + set(IRIS_DLL iris.dll) + + if (CMAKE_BUILD_TYPE STREQUAL "Debug") # set DEBUG_POSTFIX + set(IRIS_DLL irisd.dll) + endif() + + add_custom_command(TARGET ${PROJECT_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_BINARY_DIR}/${IRIS_DLL} + ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Copying Iris DLL to build directory" + ) +endif() diff --git a/example/appsettings.json b/example/appsettings.json new file mode 100644 index 0000000..d54f77a --- /dev/null +++ b/example/appsettings.json @@ -0,0 +1,574 @@ +{ + "Luminance": { + "FormulaYval1": 413.435, //CD luminance formula values + "FormulaYval2": 0.002745, + "FormulaYval3": 0.0189623, + "FormulaYvalpow": 2.2, + "RelativeLuminanceFlashThreshold": 0.1, //flash transition + "RelativeDarkLuminanceThreshold": 0.8, + "CdLuminanceFlashThreshold": 20, //flash transition + "CdDarkLuminanceThreshold": 160, + //possible CD luminanve values for look up table + "CdLuminanceValues": [ + 0.07, + 0.09, + 0.12, + 0.15, + 0.18, + 0.22, + 0.27, + 0.31, + 0.37, + 0.42, + 0.48, + 0.55, + 0.62, + 0.69, + 0.77, + 0.85, + 0.94, + 1.03, + 1.13, + 1.23, + 1.34, + 1.45, + 1.57, + 1.69, + 1.82, + 1.95, + 2.09, + 2.23, + 2.37, + 2.53, + 2.68, + 2.85, + 3.01, + 3.19, + 3.37, + 3.55, + 3.74, + 3.93, + 4.13, + 4.34, + 4.55, + 4.77, + 4.99, + 5.21, + 5.45, + 5.68, + 5.93, + 6.18, + 6.43, + 6.69, + 6.96, + 7.23, + 7.51, + 7.79, + 8.08, + 8.38, + 8.68, + 8.98, + 9.3, + 9.61, + 9.94, + 10.27, + 10.6, + 10.94, + 11.29, + 11.64, + 12, + 12.37, + 12.74, + 13.12, + 13.5, + 13.89, + 14.28, + 14.69, + 15.09, + 15.51, + 15.93, + 16.35, + 16.78, + 17.22, + 17.67, + 18.12, + 18.57, + 19.04, + 19.5, + 19.98, + 20.46, + 20.95, + 21.44, + 21.94, + 22.45, + 22.96, + 23.48, + 24.01, + 24.54, + 25.08, + 25.62, + 26.17, + 26.73, + 27.29, + 27.86, + 28.44, + 29.02, + 29.61, + 30.21, + 30.81, + 31.42, + 32.03, + 32.66, + 33.29, + 33.92, + 34.56, + 35.21, + 35.86, + 36.53, + 37.19, + 37.87, + 38.55, + 39.24, + 39.93, + 40.63, + 41.34, + 42.05, + 42.78, + 43.5, + 44.24, + 44.98, + 45.73, + 46.48, + 47.24, + 48.01, + 48.79, + 49.57, + 50.36, + 51.15, + 51.95, + 52.76, + 53.58, + 54.4, + 55.23, + 56.07, + 56.91, + 57.76, + 58.62, + 59.48, + 60.35, + 61.23, + 62.11, + 63, + 63.9, + 64.81, + 65.72, + 66.64, + 67.56, + 68.5, + 69.44, + 70.38, + 71.34, + 72.3, + 73.27, + 74.24, + 75.22, + 76.21, + 77.21, + 78.21, + 79.22, + 80.24, + 81.26, + 82.3, + 83.34, + 84.38, + 85.43, + 86.49, + 87.56, + 88.64, + 89.72, + 90.81, + 91.9, + 93, + 94.11, + 95.23, + 96.36, + 97.49, + 98.63, + 99.77, + 100.93, + 102.09, + 103.25, + 104.43, + 105.61, + 106.8, + 108, + 109.2, + 110.41, + 111.63, + 112.86, + 114.09, + 115.33, + 116.58, + 117.84, + 119.1, + 120.37, + 121.65, + 122.93, + 124.22, + 125.52, + 126.83, + 128.14, + 129.47, + 130.8, + 132.13, + 133.48, + 134.83, + 136.19, + 137.55, + 138.93, + 140.31, + 141.69, + 143.09, + 144.49, + 145.9, + 147.32, + 148.75, + 150.18, + 151.62, + 153.07, + 154.53, + 155.99, + 157.46, + 158.94, + 160.43, + 161.92, + 163.42, + 164.93, + 166.45, + 167.97, + 169.5, + 171.04, + 172.59, + 174.14, + 175.7, + 177.27, + 178.85, + 180.43, + 182.03, + 183.63, + 185.23, + 186.85, + 188.47, + 190.1, + 191.74, + 193.38, + 195.04, + 196.7, + 198.37, + 200 + ] + }, + + "RedSaturation": { + "FlashThreshold": 20, //threshold for red saturation transitions + "RedDarkThreshold": 321 + }, + + "PatternDetection": { + "MinStripes": 6, //min stripes for harmful patterns + "TimeThreshold": 0.5, //max seconds for harmful patterns until failure + "CDDarkLuminanceThreshold": 160, + "RelativeDarkLuminanceThreshold": 0.8, + "AreaProportion": 0.25 + }, + + "FilePersistence": { + "MaxDataStored": 10 //max stored frame data in memory until persistence + }, + + "TransitionEvaluator": { + "MaxTransitions": 6, //max allowed transitions and max transitions to count for extended fail + "MinTransitions": 4, //amount of min transitions to add to extended fail count + "ExtendedFailSeconds": 4, //max seconds until the start of extended failure + "ExtendedFailWindow": 5 //seconds in extended fail count window + }, + + "FlashDetection": { + "AreaProportion": 0.25, //screen display flashing area + //sRGB possible values for look up table + "sRGBValues": [ + 0, + 0.0003035269835488375, + 0.000607053967097675, + 0.0009105809506465125, + 0.00121410793419535, + 0.0015176349177441874, + 0.001821161901293025, + 0.0021246888848418626, + 0.0024282158683907, + 0.0027317428519395373, + 0.003035269835488375, + 0.003346535763899161, + 0.003676507324047436, + 0.004024717018496307, + 0.004391442037410293, + 0.004776953480693729, + 0.005181516702338386, + 0.005605391624202723, + 0.006048833022857054, + 0.006512090792594475, + 0.006995410187265387, + 0.007499032043226175, + 0.008023192985384994, + 0.008568125618069307, + 0.009134058702220787, + 0.00972121732023785, + 0.010329823029626936, + 0.010960094006488246, + 0.011612245179743885, + 0.012286488356915872, + 0.012983032342173012, + 0.013702083047289686, + 0.014443843596092545, + 0.01520851442291271, + 0.01599629336550963, + 0.016807375752887384, + 0.017641954488384078, + 0.018500220128379697, + 0.019382360956935723, + 0.0202885630566524, + 0.021219010376003555, + 0.022173884793387385, + 0.02315336617811041, + 0.024157632448504756, + 0.02518685962736163, + 0.026241221894849898, + 0.027320891639074894, + 0.028426039504420793, + 0.0295568344378088, + 0.030713443732993635, + 0.03189603307301153, + 0.033104766570885055, + 0.03433980680868217, + 0.03560131487502034, + 0.03688945040110004, + 0.0382043715953465, + 0.03954623527673284, + 0.04091519690685319, + 0.042311410620809675, + 0.043735029256973465, + 0.04518620438567554, + 0.046665086336880095, + 0.04817182422688942, + 0.04970656598412723, + 0.05126945837404324, + 0.052860647023180246, + 0.05448027644244237, + 0.05612849004960009, + 0.05780543019106723, + 0.0595112381629812, + 0.06124605423161761, + 0.06301001765316767, + 0.06480326669290577, + 0.06662593864377289, + 0.06847816984440017, + 0.07036009569659588, + 0.07227185068231748, + 0.07421356838014963, + 0.07618538148130785, + 0.07818742180518633, + 0.08021982031446832, + 0.0822827071298148, + 0.08437621154414882, + 0.08650046203654976, + 0.08865558628577294, + 0.09084171118340768, + 0.09305896284668745, + 0.0953074666309647, + 0.09758734714186246, + 0.09989872824711389, + 0.10224173308810132, + 0.10461648409110419, + 0.10702310297826761, + 0.10946171077829933, + 0.1119324278369056, + 0.11443537382697373, + 0.11697066775851084, + 0.11953842798834562, + 0.12213877222960187, + 0.12477181756095049, + 0.12743768043564743, + 0.1301364766903643, + 0.13286832155381798, + 0.13563332965520566, + 0.13843161503245183, + 0.14126329114027164, + 0.14412847085805777, + 0.14702726649759498, + 0.14995978981060856, + 0.15292615199615017, + 0.1559264637078274, + 0.1589608350608804, + 0.162029375639111, + 0.1651321945016676, + 0.16826940018969075, + 0.1714411007328226, + 0.17464740365558504, + 0.17788841598362912, + 0.18116424424986022, + 0.184474994500441, + 0.18782077230067787, + 0.19120168274079138, + 0.1946178304415758, + 0.19806931955994886, + 0.20155625379439707, + 0.20507873639031693, + 0.20863687014525575, + 0.21223075741405523, + 0.21586050011389926, + 0.2195261997292692, + 0.2232279573168085, + 0.22696587351009836, + 0.23074004852434915, + 0.23455058216100522, + 0.238397573812271, + 0.24228112246555486, + 0.24620132670783548, + 0.25015828472995344, + 0.25415209433082675, + 0.2581828529215958, + 0.26225065752969623, + 0.26635560480286247, + 0.2704977910130658, + 0.27467731206038465, + 0.2788942634768104, + 0.2831487404299921, + 0.2874408377269175, + 0.29177064981753587, + 0.2961382707983211, + 0.3005437944157765, + 0.3049873140698863, + 0.30946892281750854, + 0.31398871337571754, + 0.31854677812509186, + 0.32314320911295075, + 0.3277780980565422, + 0.33245153634617935, + 0.33716361504833037, + 0.3419144249086609, + 0.3467040563550296, + 0.35153259950043936, + 0.3564001441459435, + 0.3613067797835095, + 0.3662525955988395, + 0.3712376804741491, + 0.3762621229909065, + 0.38132601143253014, + 0.386429433787049, + 0.39157247774972326, + 0.39675523072562685, + 0.4019777798321958, + 0.4072402119017367, + 0.41254261348390375, + 0.4178850708481375, + 0.4232676699860717, + 0.4286904966139066, + 0.43415363617474895, + 0.4396571738409188, + 0.44520119451622786, + 0.45078578283822346, + 0.45641102318040466, + 0.4620769996544071, + 0.467783796112159, + 0.47353149614800955, + 0.4793201831008268, + 0.4851499400560704, + 0.4910208498478356, + 0.4969329950608704, + 0.5028864580325687, + 0.5088813208549338, + 0.5149176653765214, + 0.5209955732043543, + 0.5271151257058131, + 0.5332764040105052, + 0.5394794890121072, + 0.5457244613701866, + 0.5520114015120001, + 0.5583403896342679, + 0.5647115057049292, + 0.5711248294648731, + 0.5775804404296506, + 0.5840784178911641, + 0.5906188409193369, + 0.5972017883637634, + 0.6038273388553378, + 0.6104955708078648, + 0.6172065624196511, + 0.6239603916750761, + 0.6307571363461468, + 0.6375968739940326, + 0.6444796819705821, + 0.6514056374198242, + 0.6583748172794485, + 0.665387298282272, + 0.6724431569576875, + 0.6795424696330938, + 0.6866853124353135, + 0.6938717612919899, + 0.7011018919329731, + 0.7083757798916868, + 0.7156935005064807, + 0.7230551289219693, + 0.7304607400903537, + 0.7379104087727308, + 0.7454042095403874, + 0.7529422167760779, + 0.7605245046752924, + 0.768151147247507, + 0.7758222183174236, + 0.7835377915261935, + 0.7912979403326302, + 0.799102738014409, + 0.8069522576692516, + 0.8148465722161012, + 0.8227857543962835, + 0.8307698767746546, + 0.83879901174074, + 0.846873231509858, + 0.8549926081242338, + 0.8631572134541023, + 0.8713671191987972, + 0.8796223968878317, + 0.8879231178819663, + 0.8962693533742664, + 0.9046611743911496, + 0.9130986517934192, + 0.9215818562772946, + 0.9301108583754237, + 0.938685728457888, + 0.9473065367331999, + 0.9559733532492861, + 0.9646862478944651, + 0.9734452903984125, + 0.9822505503331171, + 0.9911020971138298, + 1 + ] + }, + + "VideoAnalyser": { + "LuminanceType": "RELATIVE", //CD || RELATIVE + "PatternDetectionEnabled": false + }, + + "Logging": { + "Console": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + }, + "IncludeScopes": true + } + } +} \ No newline at end of file diff --git a/example/main.cpp b/example/main.cpp new file mode 100644 index 0000000..76cab56 --- /dev/null +++ b/example/main.cpp @@ -0,0 +1,153 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + + +#include +#include +#include +#include +#include "iris/Configuration.h" +#include "iris/Log.h" +#include "iris/VideoAnalyser.h" +#include +#include + + + +char* getCmdOption(char** begin, char** end, const std::string& option) +{ + char** itr = std::find(begin, end, option); + if (itr != end && ++itr != end) + { + return *itr; + } + return 0; +} + +bool cmdOptionExists(char** begin, char** end, const std::string& option) +{ + return std::find(begin, end, option) != end; +} + +void CreateResultsDir(iris::Configuration& config) +{ + if (!std::filesystem::exists(config.GetResultsPath())) + { + try + { + std::filesystem::create_directories(config.GetResultsPath()); + LOG_CORE_TRACE("Created results dir at {}", config.GetResultsPath()); + } + catch (std::filesystem::filesystem_error const& ex) + { + LOG_CORE_ERROR(ex.what()); + } + } +} + +bool GetVideoFiles(std::vector& videoFiles) +{ + if (std::filesystem::exists("TestVideos")) + { + if (std::filesystem::is_empty("TestVideos")) + { + LOG_CORE_TRACE("Dir TestVideos is empty"); + LOG_CORE_ERROR("Directory TestVideos is empty. Specify a video or move them to this directory to be analyzed."); + return false; + } + else + { + std::filesystem::directory_iterator dirIterator("TestVideos"); + videoFiles.reserve(std::distance(dirIterator, std::filesystem::directory_iterator{})); + + for (const auto& entry : std::filesystem::directory_iterator{ "TestVideos" }) + { + videoFiles.emplace_back(entry.path().string()); + } + return true; + } + } + else + { + std::filesystem::create_directory("TestVideos"); + LOG_CORE_TRACE("TestVideos directory created"); + return false; + } +} + +int main(int argc, char* argv[]) +{ + iris::Log::Init(true, true); + + iris::Configuration configuration; + + bool flagJson = false; + const char* sourceVideo = nullptr; + + if (cmdOptionExists(argv, argv + argc, "-j")) + { + std::string fJson = getCmdOption(argv, argv + argc, "-j"); + flagJson = (fJson == "true" || fJson == "1"); + } + + if (cmdOptionExists(argv, argv + argc, "-v")) + { + sourceVideo = getCmdOption(argv, argv + argc, "-v"); + } + + if (cmdOptionExists(argv, argv + argc, "-l")) + { + std::string lumType = getCmdOption(argv, argv + argc, "-l"); + configuration.SetLuminanceType(lumType); + } + + //load configuration + configuration.Init(); + + //Overwrite configuration + if (cmdOptionExists(argv, argv + argc, "-p")) + { + std::string patternDetection = getCmdOption(argv, argv + argc, "-p"); + if (patternDetection == "true" || patternDetection == "1") + { + configuration.SetPatternDetectionStatus(true); + } + else if(patternDetection == "false" || patternDetection == "0") + { + configuration.SetPatternDetectionStatus(false); + } + } + + if (cmdOptionExists(argv, argv + argc, "-a")) + { + float areaProportion = atof(getCmdOption(argv, argv + argc, "-a")); + configuration.SetSafeArea(areaProportion); + } + + //Run video analysis + CreateResultsDir(configuration); + + + if (sourceVideo != nullptr) //Run specific video + { + iris::VideoAnalyser vA(&configuration); + vA.AnalyseVideo(flagJson, sourceVideo); + } + else + { + std::vector videoFiles; + bool filesExist = GetVideoFiles(videoFiles); + if (filesExist) + { + iris::VideoAnalyser vA(&configuration); + + for (int i = 0; i < videoFiles.size(); ++i) + { + vA.AnalyseVideo(flagJson, videoFiles[i].c_str()); + } + } + } + + iris::Log::ShutDown(); + + return 0; +} diff --git a/include/iris/Configuration.h b/include/iris/Configuration.h new file mode 100644 index 0000000..b553f68 --- /dev/null +++ b/include/iris/Configuration.h @@ -0,0 +1,68 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include "utils/JsonWrapper.h" + +namespace EA::EACC::Utils +{ + struct FrameConverterParams; +} + +namespace iris +{ + struct FlashParams; + struct TransitionEvaluatorParams; + struct PatternDetectionParams; + struct PatternAnalyserParams; + struct StraightLineDetectorParams; + struct CircularLineDetectorParams; + struct RiskEstimationParams; + + class Configuration + { + public: + Configuration(); + ~Configuration(); + + /// + /// Initializes configuration parameters + /// + /// path to appsettings files + void Init(const char* path = ""); + + enum class LuminanceType {UN_SET = 0, CD, RELATIVE}; + + inline void SetLuminanceType(LuminanceType luminance) { m_luminanceType = luminance; } + inline void SetLuminanceType(const std::string& luminance) { m_luminanceType = luminance == "CD" ?LuminanceType::CD : LuminanceType::RELATIVE; } + LuminanceType GetLuminanceType() { return m_luminanceType; } + + float GetSafeAreaProportion(); + + inline FlashParams* GetLuminanceFlashParams() { return m_luminanceFlashParams; } + inline FlashParams* GetRedSaturationFlashParams() { return m_redSaturationFlashParams; } + inline EA::EACC::Utils::FrameConverterParams* GetFrameSrgbConverterParams() { return m_frameSrgbConverterParams; } + inline EA::EACC::Utils::FrameConverterParams* GetFrameCDLuminanceConverterParams() { return m_frameCDLuminanceConverterParams; } + inline TransitionEvaluatorParams* GetTransitionEvaluatorParams() { return m_transitionEvaluatorParams; } + inline PatternDetectionParams* GetPatternDetectionParams() { return m_patternDetectionParams; } + + inline bool PatternDetectionEnabled() { return m_patternDetectionEnabled; } + inline void SetPatternDetectionStatus(bool status) { m_patternDetectionEnabled = status; } + + void SetSafeArea(float areaProportion); + + inline const std::string& GetResultsPath() { return m_resultsPath; } + void SetResultsPath(const std::string& resultsPath) { m_resultsPath = resultsPath; }; + private: + + FlashParams* m_luminanceFlashParams = nullptr; + FlashParams* m_redSaturationFlashParams = nullptr; + EA::EACC::Utils::FrameConverterParams* m_frameSrgbConverterParams = nullptr; + EA::EACC::Utils::FrameConverterParams* m_frameCDLuminanceConverterParams = nullptr; + TransitionEvaluatorParams* m_transitionEvaluatorParams = nullptr; + PatternDetectionParams* m_patternDetectionParams = nullptr; + + LuminanceType m_luminanceType = LuminanceType::UN_SET; + bool m_patternDetectionEnabled = true; + std::string m_resultsPath; + }; +} \ No newline at end of file diff --git a/include/iris/FrameData.h b/include/iris/FrameData.h new file mode 100644 index 0000000..5043641 --- /dev/null +++ b/include/iris/FrameData.h @@ -0,0 +1,305 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include +#include /* fmod */ +#include "utils/JsonWrapper" +#include "iris/TotalFlashIncidents.h" + +namespace iris +{ + static std::string msToTimeSpan(int ms) + { + float seconds = fmodf((ms / 1000.0), 60); + int minutes = floor((ms / (1000 * 60)) % 60); + int hours = floor((ms / (1000 * 60 * 60)) % 24); + + std::string h = (hours < 10) ? "0" + std::to_string(hours) : std::to_string(hours); + std::string m = (minutes < 10) ? "0" + std::to_string(minutes) : std::to_string(minutes); + std::string s = (seconds < 10) ? "0" + std::to_string(seconds) : std::to_string(seconds); + + return h + ":" + m + ":" + s; + } + + class FrameData + { + public: + + FrameData() {}; + FrameData(int frame, long timeMs) : Frame(frame) + { + TimeStampMs = msToTimeSpan(timeMs); + }; + + //To convert the area proportion into a percentage + std::string proportionToPercentage(float proportion) + { + std::string str = std::to_string(proportion * 100); + return str.substr(0, str.find('.') + 3) + '%'; //truncate to two decimal points + } + + std::string ToCSV() + { + std::string csvOutput = std::to_string(Frame) + + ',' + TimeStampMs + + ',' + std::to_string(LuminanceAverage) + + ',' + LuminanceFlashArea + + ',' + std::to_string(AverageLuminanceDiff) + + ',' + std::to_string(AverageLuminanceDiffAcc) + + ',' + std::to_string(RedAverage) + + ',' + RedFlashArea + + ',' + std::to_string(AverageRedDiff) + + ',' + std::to_string(AverageRedDiffAcc) + + ',' + std::to_string(LuminanceTransitions) + + ',' + std::to_string(RedTransitions) + + ',' + std::to_string(LuminanceExtendedFailCount) + + ',' + std::to_string(RedExtendedFailCount) + + ',' + std::to_string((int)luminanceFrameResult) + + ',' + std::to_string((int)redFrameResult) + + ',' + patternArea + + ',' + std::to_string(patternDetectedLines) + + ',' + std::to_string((int)patternFrameResult) + '\0'; + return csvOutput; + } + + std::string CsvColumns() + { + std::string propertyNames [] = { + "Frame", + "TimeStamp", + "AverageLuminance", + "FlashAreaLuminance", + "AverageLuminanceDiff", + "AverageLuminanceDiffAcc", + "AverageRed", + "FlashAreaRed", + "AverageRedDiff", + "AverageRedDiffAcc", + "LuminanceTransitions", + "RedTransitions", + "LuminanceExtendedFailCount", + "RedExtendedFailCount", + "LuminanceFrameResult", + "RedFrameResult", + "PatternArea", + "PatternDetectedLines", + "PatternFrameResult" + }; + + std::string columns; + + bool first = true; + for (auto property : propertyNames) + { + if (!first) + { + columns += ',' + property; + } + else + { + columns += property; + first = false; + } + } + + return columns; + } + + /// + /// Frame index + /// + int Frame = 0; + + /// + /// frame timestamp in milliseconds + /// + std::string TimeStampMs = "00:00"; + std::string LuminanceFlashArea = "0.00%"; + float LuminanceAverage = 0; + float AverageLuminanceDiff = 0; + float AverageLuminanceDiffAcc = 0; + std::string RedFlashArea = "0.00%"; + float RedAverage = 0; + float AverageRedDiff = 0; + float AverageRedDiffAcc = 0; + float PatternRisk = 0; + int LuminanceTransitions = 0; + int RedTransitions = 0; + int LuminanceExtendedFailCount = 0; + int RedExtendedFailCount = 0; + FlashResult luminanceFrameResult = FlashResult::Pass; + FlashResult redFrameResult = FlashResult::Pass; + std::string patternArea = "0.00%"; + int patternDetectedLines = 0; + PatternResult patternFrameResult = PatternResult::Pass; + }; + + //Serializes FrameData to Json object + static void to_json(json& j, const FrameData& data) + { + j = json + { {"Frame", data.Frame}, + {"TimeStampString", data.TimeStampMs}, + {"AverageLuminance", data.LuminanceAverage}, + {"FlashAreaLuminance", data.LuminanceFlashArea}, + {"AverageLuminanceDiff", data.AverageLuminanceDiff}, + {"AverageLuminanceDiffAcc", data.AverageLuminanceDiffAcc}, + {"AverageRed", data.RedAverage}, + {"FlashAreaRed", data.RedFlashArea}, + {"AverageRedDiff", data.AverageRedDiff}, + {"AverageRedDiffAcc", data.AverageRedDiffAcc}, + {"LuminanceTransitions", data.LuminanceTransitions}, + {"RedTransitions", data.RedTransitions}, + {"LuminanceExtendedFailCount", data.LuminanceExtendedFailCount}, + {"RedExtendedFailCount", data.RedExtendedFailCount}, + {"LuminanceFrameResult", data.luminanceFrameResult}, + {"RedFrameResult", data.redFrameResult}, + {"PatternArea", data.patternArea}, + {"PatternDetectedLines", data.patternDetectedLines}, + {"PatternFrameResult", data.patternFrameResult} }; + }; + + struct FrameDataJson + { + void reserve(const int& size) + { + frame.reserve(size); + timeStampMs.reserve(size); + + luminanceFlashArea.reserve(size); + luminanceAverage.reserve(size); + averageLuminanceDiff.reserve(size); + averageLuminanceDiffAcc.reserve(size); + + redFlashArea.reserve(size); + redAverage.reserve(size); + averageRedDiff.reserve(size); + averageRedDiffAcc.reserve(size); + + luminanceTransitions.reserve(size); + redTransitions.reserve(size); + + luminanceExtendedFailCount.reserve(size); + redExtendedFailCount.reserve(size); + luminanceFrameResult.reserve(size); + redFrameResult.reserve(size); + + patternArea.reserve(size); + patternDetectedLines.reserve(size); + patternFrameResult.reserve(size); + + } + + void reserveLineGraphData(const int& size) + { + timeStampMs.reserve(size); + luminanceTransitions.reserve(size); + redTransitions.reserve(size); + luminanceFrameResult.reserve(size); + redFrameResult.reserve(size); + patternFrameResult.reserve(size); + } + + void push_back(const FrameData& data) + { + frame.push_back(data.Frame); + timeStampMs.push_back(data.TimeStampMs); + + luminanceFlashArea.push_back(data.LuminanceFlashArea); + luminanceAverage.push_back(data.LuminanceAverage); + averageLuminanceDiff.push_back(data.AverageLuminanceDiff); + averageLuminanceDiffAcc.push_back(data.AverageLuminanceDiffAcc); + + redFlashArea.push_back(data.RedFlashArea); + redAverage.push_back(data.RedAverage); + averageRedDiff.push_back(data.AverageRedDiff); + averageRedDiffAcc.push_back(data.AverageRedDiffAcc); + + luminanceTransitions.push_back(data.LuminanceTransitions); + redTransitions.push_back(data.RedTransitions); + + luminanceExtendedFailCount.push_back(data.LuminanceExtendedFailCount); + redExtendedFailCount.push_back(data.RedExtendedFailCount); + luminanceFrameResult.push_back((int)data.luminanceFrameResult); + redFrameResult.push_back((int)data.redFrameResult); + + patternArea.push_back(data.patternArea); + patternDetectedLines.push_back(data.patternDetectedLines); + patternFrameResult.push_back((int)data.patternFrameResult); + } + + void push_back_lineGraphData(const FrameData& data) + { + timeStampMs.push_back(data.TimeStampMs); + luminanceTransitions.push_back(data.LuminanceTransitions); + redTransitions.push_back(data.RedTransitions); + luminanceFrameResult.push_back((int)data.luminanceFrameResult); + redFrameResult.push_back((int)data.redFrameResult); + patternFrameResult.push_back((int)data.patternFrameResult); + } + + std::vector frame; + std::vector timeStampMs; + std::vector luminanceFlashArea; + std::vector luminanceAverage; + std::vector averageLuminanceDiff; + std::vector averageLuminanceDiffAcc; + std::vector redFlashArea; + std::vector redAverage; + std::vector averageRedDiff; + std::vector averageRedDiffAcc; + std::vector luminanceTransitions; + std::vector redTransitions; + std::vector luminanceExtendedFailCount; + std::vector redExtendedFailCount; + std::vector luminanceFrameResult; + std::vector redFrameResult; + std::vector patternArea; + std::vector patternDetectedLines; + std::vector patternFrameResult; + }; + + //Serializes FrameData to Json object + static void to_json(json& j, const FrameDataJson& data) + { + if (data.frame.capacity() == 0) //if frame vector is empty, line graph data serialization + { + j = json + { + {"TimeStampString", data.timeStampMs}, + {"LuminanceTransitions", data.luminanceTransitions}, + {"RedTransitions", data.redTransitions}, + {"LuminanceFrameResult", data.luminanceFrameResult}, + {"RedFrameResult", data.redFrameResult}, + {"PatternFrameResult", data.patternFrameResult} + }; + } + else + { + j = json + { {"Frame", data.frame}, + {"TimeStampString", data.timeStampMs}, + {"AverageLuminance", data.luminanceAverage}, + {"FlashAreaLuminance", data.luminanceFlashArea}, + {"AverageLuminanceDiff", data.averageLuminanceDiff}, + {"AverageLuminanceDiffAcc", data.averageLuminanceDiffAcc}, + {"AverageRed", data.redAverage}, + {"FlashAreaRed", data.redFlashArea}, + {"AverageRedDiff", data.averageRedDiff}, + {"AverageRedDiffAcc", data.averageRedDiffAcc}, + {"LuminanceTransitions", data.luminanceTransitions}, + {"RedTransitions", data.redTransitions}, + {"LuminanceExtendedFailCount", data.luminanceExtendedFailCount}, + {"RedExtendedFailCount", data.redExtendedFailCount}, + {"LuminanceFrameResult", data.luminanceFrameResult}, + {"RedFrameResult", data.redFrameResult}, + {"PatternArea", data.patternArea}, + {"PatternDetectedLines", data.patternDetectedLines}, + {"PatternFrameResult", data.patternFrameResult} }; + } + }; + +} + + + diff --git a/include/iris/Log.h b/include/iris/Log.h new file mode 100644 index 0000000..cf5ff06 --- /dev/null +++ b/include/iris/Log.h @@ -0,0 +1,47 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include "utils/BaseLog.h" +#include + +#ifdef IRIS_SHARED +#ifdef IRIS_EXPORT +#define IRIS_API __declspec(dllexport) +#else +#define IRIS_API __declspec(dllimport) +#endif +#else +#define IRIS_API +#endif + +namespace iris +{ + class Log : public EA::EACC::Utils::BaseLog + { + public: + + /// + /// Initilizes application loggers + /// + /// Enable/Disable logging to console + /// Enable/Disable logging to file + /// level to filter log messages in Core Logger + /// file in which to store persisted data + static void Init(bool console, bool file, int coreLevel = 0, const char* dataFile = nullptr); + + /// + /// Removes current file sink from logger (if it exists) and adds a new one to persist the + /// frame data generated by the video analysis of a video + /// + /// file name and path to log.csv file + static void SetDataLoggerFile(const char* fileName); + + inline static std::shared_ptr& GetDataLogger() { return m_DataLogger; } + + private: + IRIS_API static std::shared_ptr m_DataLogger; + }; +} + +//Data Logger macro - only used for data persistence +#define LOG_DATA_INFO(...) iris::Log::GetDataLogger()->info(__VA_ARGS__) \ No newline at end of file diff --git a/include/iris/Result.h b/include/iris/Result.h new file mode 100644 index 0000000..ebf7226 --- /dev/null +++ b/include/iris/Result.h @@ -0,0 +1,79 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include +#include "../src/FrameData.h" +#include "iris/TotalFlashIncidents.h" + +namespace iris +{ + enum class AnalysisResult + { + /// + /// Failed test + /// + Fail, + /// + /// Passed test + /// + Pass, + /// + /// Passed test but there was flashing between 2-3Hz + /// + PassWithWarning, + /// + /// Luminance flash failure + /// + LuminanceFlashFailure, + /// + /// Luminance extended flash failure + /// + LuminanceExtendedFlashFailure, + /// + /// Red saturation flash failure + /// + RedFlashFailure, + /// + /// Red saturation extended flash failure + /// + RedExtendedFlashFailure, + /// + /// Pattern failure + /// + PatternFailure + }; + + struct Result + { + int VideoLen = 0; + int AnalysisTime = 0; + int TotalFrame = 0; + int patternFailFrames = 0; + AnalysisResult OverallResult = AnalysisResult::Pass; + std::vector Results; + //total amount of frames that were counted that belonged to each incident type + TotalFlashIncidents totalLuminanceIncidents; + TotalFlashIncidents totalRedIncidents; + }; + + + //Serializes FrameData to Json object + static void to_json(json& j, const AnalysisResult& result) + { + std::string analysisRes; + + switch (result) + { + case AnalysisResult::Pass: analysisRes = "Pass"; break; + case AnalysisResult::Fail: analysisRes = "Fail"; break; + case AnalysisResult::PassWithWarning: analysisRes = "PassWithWarning"; break; + case AnalysisResult::LuminanceFlashFailure: analysisRes = "LuminanceFlashFailure"; break; + case AnalysisResult::LuminanceExtendedFlashFailure: analysisRes = "LuminanceExtendedFlashFailure"; break; + case AnalysisResult::RedFlashFailure: analysisRes = "RedFlashFailure"; break; + case AnalysisResult::RedExtendedFlashFailure: analysisRes = "RedExtendedFlashFailure"; break; + case AnalysisResult::PatternFailure: analysisRes = "PatternFailure"; break; + } + + j = analysisRes; + }; +} \ No newline at end of file diff --git a/include/iris/TotalFlashIncidents.h b/include/iris/TotalFlashIncidents.h new file mode 100644 index 0000000..070e158 --- /dev/null +++ b/include/iris/TotalFlashIncidents.h @@ -0,0 +1,38 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include "utils/JsonWrapper.h" + +namespace iris +{ + +enum class FlashResult +{ + Pass = 0, PassWithWarning, ExtendedFail, FlashFail +}; + +enum class PatternResult +{ + Pass = 0, Fail +}; + + +struct TotalFlashIncidents //amount of frames of each incident type +{ + int extendedFailFrames = 0; + int flashFailFrames = 0; + int passWithWarningFrames = 0; + + int getTotalFailedFrames() const { return extendedFailFrames + flashFailFrames; } +}; + +static void to_json(json& j, const TotalFlashIncidents& totalFlashIncidents) +{ + j = json{ {"ExtendedFailFrames", totalFlashIncidents.extendedFailFrames}, + {"FlashFailFrames", totalFlashIncidents.flashFailFrames}, + {"PassWithWarningFrames", totalFlashIncidents.passWithWarningFrames}, + {"TotalFailedFrames", totalFlashIncidents.getTotalFailedFrames()} + }; +}; + +} \ No newline at end of file diff --git a/include/iris/VideoAnalyser.h b/include/iris/VideoAnalyser.h new file mode 100644 index 0000000..42da8c1 --- /dev/null +++ b/include/iris/VideoAnalyser.h @@ -0,0 +1,95 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +///////////////////////////////////////////////////////////////////////////// +// VideoAnalyser is the main object to with which to run a photosensitivity +// compliance check on a video, it analizes frames of a video running both +// flash and pattern detection to check the guidelines for compliance issues. +// When calling AnalyseVideo, initialization, video analysis, logging and +// deinitialization is managed by VideoAnalyser. +// If not, all these steps will have to be manually managed. +///////////////////////////////////////////////////////////////////////////// + +//#if defined BUILD_SHARED_LIBS && WIN32 +//#define IRIS_API __declspec(dllexport) +//#else +//#define IRIS_API __declspec(dllimport) +//#endif + + +#pragma once +#include + +namespace cv +{ + class VideoCapture; + class Mat; +} + +namespace EA::EACC::Utils +{ + class FrameConverter; +} + +namespace iris +{ + class Configuration; + class FlashDetection; + class PatternDetection; + class PhotosensitivityDetector; + class FrameData; + struct FrameDataJson; + struct Result; + + class VideoAnalyser + { + public: + VideoAnalyser(Configuration* configuration); + ~VideoAnalyser(); + + /// + /// Initializes FlashDetection and PatternDetection + /// + void Init(const short& fps, const cv::Size& size, const std::string& videoName, bool flagJson = false); + + /// + /// Release FlashDetection and PatternDection + /// + void DeInit(); + + /// + /// Checks if the video file can be opened and read + /// + bool VideoIsOpen(cv::VideoCapture& video, const char* videoName); + + /// + /// Anlyses video to check photosensitivity + /// + /// If true, saves Result to json file + /// video file path + void AnalyseVideo(bool flagJson, const char* sourceVideo); + + /// + /// Frame analysis for checking for photosensitivity for tracked issues (flashes/patterns) + /// + void AnalyseFrame(cv::Mat& frame, int& frameIndex, FrameData& data); + + private: + + void SerializeResults(const Result& result, const FrameDataJson& lineGraphData, const FrameDataJson& nonPassData); + + /// + /// Updates the current analysis progress + /// + void UpdateProgress(int& numFrames, const long& totalFrames, int& lastPercentage); + + Configuration* m_configuration = nullptr; + FlashDetection* m_flashDetection = nullptr; + PatternDetection* m_patternDetection = nullptr; + std::vector m_photosensitivityDetector; + + EA::EACC::Utils::FrameConverter* m_frameSrgbConverter = nullptr; + + std::string m_resultJsonPath; + std::string m_frameDataJsonPath; + }; +} \ No newline at end of file diff --git a/ports/aws-lambda-cpp/portfile.cmake b/ports/aws-lambda-cpp/portfile.cmake new file mode 100644 index 0000000..64e30ea --- /dev/null +++ b/ports/aws-lambda-cpp/portfile.cmake @@ -0,0 +1,25 @@ +vcpkg_from_git( + OUT_SOURCE_PATH SOURCE_PATH + URL https://gitlab.ea.com/eacc-tech/aws-lambda-cpp + REF e4f4c092dc7cd57c5d22a95959987a3770ef57ff # v0.2.7 + HEAD_REF master +) + +vcpkg_cmake_configure( + SOURCE_PATH ${SOURCE_PATH} +) + +vcpkg_cmake_install() + +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) + +vcpkg_copy_pdbs() + +vcpkg_cmake_config_fixup(PACKAGE_NAME aws-lambda-runtime CONFIG_PATH lib/aws-lambda-runtime/cmake) + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share") +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/lib/aws-lambda-runtime") +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/lib/aws-lambda-runtime") + +# Handle copyright +file(INSTALL ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT} RENAME copyright) diff --git a/ports/aws-lambda-cpp/portfile.cmake.template b/ports/aws-lambda-cpp/portfile.cmake.template new file mode 100644 index 0000000..15dfdaf --- /dev/null +++ b/ports/aws-lambda-cpp/portfile.cmake.template @@ -0,0 +1,25 @@ +vcpkg_from_git( + OUT_SOURCE_PATH SOURCE_PATH + URL https://lambda:TOKEN@gitlab.ea.com/eacc-tech/aws-lambda-cpp.git + REF e4f4c092dc7cd57c5d22a95959987a3770ef57ff # v0.2.7 + HEAD_REF master +) + +vcpkg_cmake_configure( + SOURCE_PATH ${SOURCE_PATH} +) + +vcpkg_cmake_install() + +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) + +vcpkg_copy_pdbs() + +vcpkg_cmake_config_fixup(PACKAGE_NAME aws-lambda-runtime CONFIG_PATH lib/aws-lambda-runtime/cmake) + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share") +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/lib/aws-lambda-runtime") +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/lib/aws-lambda-runtime") + +# Handle copyright +file(INSTALL ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT} RENAME copyright) diff --git a/ports/aws-lambda-cpp/vcpkg.json b/ports/aws-lambda-cpp/vcpkg.json new file mode 100644 index 0000000..72b7d99 --- /dev/null +++ b/ports/aws-lambda-cpp/vcpkg.json @@ -0,0 +1,18 @@ +{ + "name": "aws-lambda-cpp", + "version": "0.2.7", + "port-version": 1, + "description": "EACC fork of C++ Runtime for AWS Lambda.", + "supports": "linux", + "dependencies": [ + "curl", + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ] +} diff --git a/ports/cpputils/portfile.cmake b/ports/cpputils/portfile.cmake new file mode 100644 index 0000000..46d352b --- /dev/null +++ b/ports/cpputils/portfile.cmake @@ -0,0 +1,24 @@ +vcpkg_from_git( + OUT_SOURCE_PATH SOURCE_PATH + URL C:\\dev\\cpputils #https://gitlab.ea.com/eacc-tech/cpputils + REF c3c2276e60ea77c6c2abcfb2c73572ed3849f0c2 + HEAD_REF dev-blanca/cmake-refactor +) + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + -DEXPORT_CPPUTILS=1 +) + +vcpkg_cmake_install() + +vcpkg_cmake_config_fixup( + CONFIG_PATH cmake/ +) + +vcpkg_copy_pdbs() + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") + +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/Copyright.txt" COMMENT "Install copyright") diff --git a/ports/cpputils/portfile.cmake.template b/ports/cpputils/portfile.cmake.template new file mode 100644 index 0000000..9655804 --- /dev/null +++ b/ports/cpputils/portfile.cmake.template @@ -0,0 +1,19 @@ +vcpkg_check_linkage(ONLY_STATIC_LIBRARY) + +vcpkg_from_git( + OUT_SOURCE_PATH SOURCE_PATH + URL https://cpputils:TOKEN@gitlab.ea.com/eacc-tech/cpputils.git + REF 4cf4008bcc1b0512e68166a78d3bb7e378e131cf + HEAD_REF main +) + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" +) + +vcpkg_cmake_install() +vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/cpputils) +vcpkg_copy_pdbs() + +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/Copyright.txt" COMMENT "Install copyright") +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") \ No newline at end of file diff --git a/ports/cpputils/usage b/ports/cpputils/usage new file mode 100644 index 0000000..006c71d --- /dev/null +++ b/ports/cpputils/usage @@ -0,0 +1,4 @@ +The package CppUtils provides CMake targets + + find_package(cpputils CONFIG REQUIRED) + target_link_libraries(main PRIVATE cpputils::cpputils) \ No newline at end of file diff --git a/ports/cpputils/vcpkg.json b/ports/cpputils/vcpkg.json new file mode 100644 index 0000000..b6ec239 --- /dev/null +++ b/ports/cpputils/vcpkg.json @@ -0,0 +1,19 @@ +{ + "name": "cpputils", + "version-string": "1.0.0", + "description": "Library to share common C++ code", + "license": null, + "dependencies": [ + "nlohmann-json", + "spdlog", + "opencv", + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ] + } \ No newline at end of file diff --git a/src/CDLuminance.cpp b/src/CDLuminance.cpp new file mode 100644 index 0000000..2f789ab --- /dev/null +++ b/src/CDLuminance.cpp @@ -0,0 +1,57 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +/* +**** RelativeLuminance **** +Abstract class for Flash detection +*/ + +#include "CDLuminance.h" +#include +#include +#include "ConfigurationParams.h" +#include "IrisFrame.h" +#include "utils/FrameConverter.h" + +namespace iris +{ + CDLuminance::CDLuminance(EA::EACC::Utils::FrameConverter *converter, const short& videoFPS,const cv::Size& frameSize, FlashParams* params) + : Flash(videoFPS, frameSize, params) + { + m_converter = converter; + } + + CDLuminance::~CDLuminance() + { + ReleaseLastFrame(); + ReleaseCurrentFrame(); + } + + /// + /// Set the new current frame and move the previous one as the last frame. + /// in RelativeLuminance this method and class are responsible to release memory for the frame created + /// + /// + void CDLuminance::SetCurrentFrame(const IrisFrame& irisFrame) + { + cv::Mat YCrCb; + cv::cvtColor(*irisFrame.originalFrame, YCrCb, cv::COLOR_BGR2YCrCb); //luma-chroma colour space + cv::Mat channelY; + cv::extractChannel(YCrCb, channelY, 0); + cv::Mat *frame = m_converter->Convert(channelY); + + ReleaseLastFrame(); + Flash::SetCurrentFrame(frame); + } + + void CDLuminance::SetCurrentFrame(cv::Mat* bgrFrame) + { + cv::Mat YCrCb; + cv::cvtColor(*bgrFrame, YCrCb, cv::COLOR_BGR2YCrCb); //luma-chroma colour space + cv::Mat channelY; + cv::extractChannel(YCrCb, channelY, 0); + cv::Mat* frame = m_converter->Convert(channelY); + + ReleaseLastFrame(); + Flash::SetCurrentFrame(frame); + } +} diff --git a/src/CDLuminance.h b/src/CDLuminance.h new file mode 100644 index 0000000..2ae0b60 --- /dev/null +++ b/src/CDLuminance.h @@ -0,0 +1,40 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include "Flash.h" +#include "opencv2/core/types.hpp" + +namespace cv +{ + class Mat; +} + +namespace EA::EACC::Utils +{ + class FrameConverter; +} + + +namespace iris +{ + struct FlashParams; + struct IrisFrame; + + class CDLuminance : public Flash + { + public: + /// + /// + /// + /// + /// + CDLuminance(EA::EACC::Utils::FrameConverter* converter, const short& fps, const cv::Size& frameSize, FlashParams* params); + void SetCurrentFrame(const IrisFrame& irisFrame) override; + void SetCurrentFrame(cv::Mat* bgrFrame); + + ~CDLuminance(); + protected: + private: + EA::EACC::Utils::FrameConverter* m_converter; + }; +} \ No newline at end of file diff --git a/src/Configuration.cpp b/src/Configuration.cpp new file mode 100644 index 0000000..57a2270 --- /dev/null +++ b/src/Configuration.cpp @@ -0,0 +1,134 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include "iris/Configuration.h" +#include "iris/Log.h" +#include "ConfigurationParams.h" +#include "utils/FrameConverter.h" +#include + +namespace iris +{ + Configuration::Configuration() + { + } + + Configuration::~Configuration() + { + if (m_luminanceFlashParams != nullptr) + { + delete m_luminanceFlashParams; m_luminanceFlashParams = nullptr; + } + + if (m_redSaturationFlashParams != nullptr) + { + delete m_redSaturationFlashParams; m_redSaturationFlashParams = nullptr; + } + + if (m_frameSrgbConverterParams != nullptr) + { + delete m_frameSrgbConverterParams; m_frameSrgbConverterParams = nullptr; + } + + if (m_frameCDLuminanceConverterParams != nullptr) + { + delete m_frameCDLuminanceConverterParams; m_frameCDLuminanceConverterParams = nullptr; + } + + if (m_transitionEvaluatorParams != nullptr) + { + delete m_transitionEvaluatorParams; m_transitionEvaluatorParams = nullptr; + } + + if (m_patternDetectionParams != nullptr) + { + delete m_patternDetectionParams; m_patternDetectionParams = nullptr; + } + } + + void Configuration::Init(const char* path) + { + EA::EACC::Utils::JsonWrapper jsonFile; + + jsonFile.OpenFile((std::string(path) + "appsettings.json").c_str()); //load json configuration file + + m_resultsPath = jsonFile.ContainsParam("ResultsPath") ? jsonFile.GetParam("ResultsPath") : "Results/"; + + if (m_luminanceType == LuminanceType::UN_SET) + { + std::string lumType = jsonFile.GetParam("VideoAnalyser", "LuminanceType"); + m_luminanceType = lumType == "CD" ? LuminanceType::CD : LuminanceType::RELATIVE; + } + + m_patternDetectionEnabled = jsonFile.GetParam("VideoAnalyser", "PatternDetectionEnabled"); + + //Luminance + if (m_luminanceType == LuminanceType::CD) + { + m_luminanceFlashParams = new FlashParams( + jsonFile.GetParam("Luminance", "CdLuminanceFlashThreshold"), + jsonFile.GetParam("FlashDetection", "AreaProportion"), + jsonFile.GetParam("Luminance", "CdDarkLuminanceThreshold")); + } + else + { + m_luminanceFlashParams = new FlashParams( + jsonFile.GetParam("Luminance", "RelativeLuminanceFlashThreshold"), + jsonFile.GetParam("FlashDetection", "AreaProportion"), + jsonFile.GetParam("Luminance", "RelativeDarkLuminanceThreshold")); + } + + //Red Saturation + m_redSaturationFlashParams = new FlashParams( + jsonFile.GetParam("RedSaturation", "FlashThreshold"), + jsonFile.GetParam("FlashDetection", "AreaProportion"), + jsonFile.GetParam("RedSaturation", "RedDarkThreshold")); + + //FrameRgbConverter Params + m_frameSrgbConverterParams = new EA::EACC::Utils::FrameConverterParams( + jsonFile.GetVector("FlashDetection", "sRGBValues")); + + //FrameRgbConverter Params for CD Luminance conversion + if (m_luminanceType == LuminanceType::CD) + { + m_frameCDLuminanceConverterParams = new EA::EACC::Utils::FrameConverterParams( + jsonFile.GetVector("Luminance", "CdLuminanceValues")); + } + + //Transition Evaluetor Params + m_transitionEvaluatorParams = new TransitionEvaluatorParams( + jsonFile.GetParam("TransitionEvaluator", "MaxTransitions"), + jsonFile.GetParam("TransitionEvaluator", "MinTransitions"), + jsonFile.GetParam("TransitionEvaluator", "ExtendedFailSeconds"), + jsonFile.GetParam("TransitionEvaluator", "ExtendedFailWindow")); + + //Pattern Detection + int minStripes = jsonFile.GetParam("PatternDetection", "MinStripes"); + float darkLumThreshold = 0.0f; + + if (m_luminanceType == LuminanceType::CD) + { + darkLumThreshold = jsonFile.GetParam("PatternDetection", "CDDarkLuminanceThreshold"); + } + else + { + darkLumThreshold = jsonFile.GetParam("PatternDetection", "RelativeDarkLuminanceThreshold"); + } + + m_patternDetectionParams = new PatternDetectionParams( + minStripes, + darkLumThreshold, + jsonFile.GetParam("PatternDetection", "TimeThreshold"), + jsonFile.GetParam("PatternDetection", "AreaProportion")); + } + + void Configuration::SetSafeArea(float areaProportion) + { + m_luminanceFlashParams->areaProportion = areaProportion; + m_redSaturationFlashParams->areaProportion = areaProportion; + } + + float Configuration::GetSafeAreaProportion() + { + return m_luminanceFlashParams->areaProportion; + } +} \ No newline at end of file diff --git a/src/ConfigurationParams.h b/src/ConfigurationParams.h new file mode 100644 index 0000000..e3c17cf --- /dev/null +++ b/src/ConfigurationParams.h @@ -0,0 +1,49 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include +#include + +namespace iris +{ + struct FlashParams + { + FlashParams(float flashThresh, float areaP, float darkThresh) : flashThreshold(flashThresh), areaProportion(areaP), darkThreshold(darkThresh) {}; + + //threshold of Red Saturation or Luminace + float flashThreshold; //if flash values overpass threshold, a transition has occurred + float areaProportion; //minimum area of the screen display for a transition to have occurred + float darkThreshold; //if darker image is above this threshold, a transitions has not occurred + }; + + struct FrameRgbConverterParams + { + FrameRgbConverterParams(std::vector sRgbValues) : sRgbValues(sRgbValues) {}; + + std::vector sRgbValues; //The input array containing the decimal values for the sRgb convertion + }; + + struct TransitionEvaluatorParams + { + TransitionEvaluatorParams(int maxTransition, int minTransitions, int extendedFailSeconds, int extendedFailWindow) + : maxTransitions(maxTransition), minTransitions(minTransitions), extendedFailSeconds(extendedFailSeconds), extendedFailWindow (extendedFailWindow) {}; + + int maxTransitions; //max allowed transitions and max transitions to count for extended fail + int minTransitions; //amount of min transitions to add to extended fail count + int extendedFailSeconds; //if extendedFailFramesIS reach this value, extended failure has occured + int extendedFailWindow; //seconds in extended fail count window + }; + + struct PatternDetectionParams + { + PatternDetectionParams(int minStripes, float darkLuminanceThreshold, float timeThreshold, float areaP) + : minStripes(minStripes), darkLuminanceThreshold(darkLuminanceThreshold), timeThreshold(timeThreshold), + areaProportion(areaP) {}; + + int minStripes; //min number of light and dark pattern stripes + float darkLuminanceThreshold; //max luminance of the darker part of a flash/pattern + float timeThreshold; + float areaProportion; //max size a harmful pattern can occupy + }; + +} \ No newline at end of file diff --git a/src/Flash.cpp b/src/Flash.cpp new file mode 100644 index 0000000..635adae --- /dev/null +++ b/src/Flash.cpp @@ -0,0 +1,198 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +/* +**** Flash **** +Abstract class for Flash detection +*/ + +#include "Flash.h" +#include +#include +#include "ConfigurationParams.h" +#include +#include +#include "iris/Log.h" + +namespace iris +{ + short Flash::fps = 0; + + Flash::Flash(short videoFPS, const cv::Size& frameSize, FlashParams* flashParams) + { + Flash::fps = videoFPS; + m_params = flashParams; + m_avgDiffInSecond.reserve(fps); + m_avgDiffInSecond.emplace_back(0); //initial frame + m_frameSize = frameSize.area(); + m_safeArea = (int)(frameSize.area() * m_params->areaProportion); + + LOG_CORE_INFO("Flash Area in pixels: {0}", m_safeArea); + LOG_CORE_INFO("Number of pixels in frame: {0}", frameSize.area()); + } + + Flash::~Flash(){} + + void Flash::ReleaseLastFrame() + { + if (lastFrame != nullptr) { + lastFrame->release(); + delete lastFrame; + lastFrame = nullptr; + } + } + + void Flash::ReleaseCurrentFrame() + { + if (currentFrame != nullptr) { + currentFrame->release(); + delete currentFrame; + currentFrame = nullptr; + } + } + + cv::Mat* Flash::FrameDifference() + { + if (currentFrame == nullptr || lastFrame == nullptr) { + return nullptr; + } + cv::Mat* ptrFrameDiff = new cv::Mat(); + cv::subtract(*currentFrame, *lastFrame, *ptrFrameDiff); + return ptrFrameDiff; + } + + float Flash::FrameMean() + { + return (float)cv::mean(*currentFrame)[0]; + } + + void Flash::SetCurrentFrame(cv::Mat* flashValuesFrame) + { + lastFrame = currentFrame; + currentFrame = flashValuesFrame; + + m_avgLastFrame = m_avgCurrentFrame; + m_avgCurrentFrame = FrameMean(); + } + + float Flash::CheckSafeArea(cv::Mat* frameDifference) + { + int variation = cv::countNonZero(*frameDifference); + m_flashArea = variation / (float)m_frameSize; + + if (variation >= m_safeArea) + { + return m_avgCurrentFrame - m_avgLastFrame; + } + + return 0; + } + + bool Flash::IsFlashTransition(const float& lastAvgDiffAcc, const float& avgDiffAcc, const float& threshold) + { + //if the luminance of the darker image is not below 0.8, no transition occurs (not applicable to red saturation) + float darkerDiff = m_avgLastFrame < m_avgCurrentFrame ? m_avgLastFrame : m_avgCurrentFrame; + + //if tendency hasn't changed, check if last avg was a transition or part of + if (SameSign(lastAvgDiffAcc, avgDiffAcc) && std::abs(lastAvgDiffAcc) >= threshold) + { + return false; + } + else if (std::abs(avgDiffAcc) >= threshold && darkerDiff < m_params->darkThreshold) //check if current value is transition + { + return true; + } + return false; //no flash + } + + Flash::CheckTransitionResult Flash::CheckTransition(float avgDiff, float lastAvgDiffAcc) + { + CheckTransitionResult result; + result.lastAvgDiffAcc = lastAvgDiffAcc; + + if (SameSign(lastAvgDiffAcc, avgDiff)) //accumulate increase/decrease (positive/negative) + { + if (m_avgDiffInSecond.size() == m_avgDiffInSecond.capacity()) + { + lastAvgDiffAcc -= m_avgDiffInSecond[0]; + m_avgDiffInSecond.erase(m_avgDiffInSecond.begin()); + } + + m_avgDiffInSecond.emplace_back(avgDiff); + avgDiff += lastAvgDiffAcc; //accumulate value + } + else + { + m_avgDiffInSecond.clear(); + m_avgDiffInSecond.emplace_back(avgDiff); + } + + result.checkResult = IsFlashTransition(result.lastAvgDiffAcc, avgDiff, m_params->flashThreshold); + result.lastAvgDiffAcc = avgDiff; //new start acc value + + return result; + } + + float Flash::roundoff(float value, char prec) + { + float pow_10 = std::pow(10.0f, (float)prec); + return std::round(value * pow_10) / pow_10; + } + + void Flash::CalculateSrgbValues() + { + for (int i = 0; i < 256; i++) + { + double color = (double)i / 255.0; //normalised value + //if RsRGB <= 0.04045 then R = RsRGB/12.92 else R = ((RsRGB+0.055)/1.055) ^ 2.4 + if (color <= 0.04045) + { + color = color / 12.92; + } + else + { + color = pow((color + 0.055) / 1.055, 2.4); + } + + LOG_CORE_TRACE("{0},", color); + } + } + + + ///Old calculation + //float Flash::CheckSafeArea(cv::Mat* frameDifference) + //{ + // cv::Mat positiveValues(frameDifference->size(), CV_32FC1); + // cv::Mat negativeValues(frameDifference->size(), CV_32FC1); + + // cv::threshold(*frameDifference, positiveValues, 0, 320, cv::ThresholdTypes::THRESH_TOZERO); + // cv::threshold(*frameDifference, negativeValues, 0, 320, cv::ThresholdTypes::THRESH_TOZERO_INV); + // + // int posVariationArea = cv::countNonZero(positiveValues); + // int negVariationArea = cv::countNonZero(negativeValues); + + // float posVariationAverage = 0; + // float negVariationAverage = 0; + + // if (posVariationArea + negVariationArea >= m_safeArea) //minimum variation size + // { + // if (posVariationArea > 0) + // { + // posVariationAverage = (float)cv::sum(positiveValues)[0] / posVariationArea; + // } + // if (negVariationArea > 0) + // { + // negVariationAverage = (float)cv::sum(negativeValues)[0] / negVariationArea; + // } + // } + + // // Get highest abs average diff to mark tendency + // if (abs(posVariationAverage) > abs(negVariationAverage)) + // { + // return posVariationAverage; + // } + // else + // { + // return negVariationAverage; + // } + //} +} diff --git a/src/Flash.h b/src/Flash.h new file mode 100644 index 0000000..1974cc7 --- /dev/null +++ b/src/Flash.h @@ -0,0 +1,139 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include +#include +#include "iris/Configuration.h" +#include + +namespace cv +{ + class Mat; +} + +namespace iris +{ + struct FlashParams; + struct CheckTransitionResult; + struct IrisFrame; + + class Flash + { + public: + + /// + /// struct with config parameters + /// method to use to check the flashing area + Flash(short fps, const cv::Size& frameSize, FlashParams* flashParams); + + virtual ~Flash(); + + struct CheckTransitionResult + { + bool checkResult = false; + float lastAvgDiffAcc = 0; + + void operator = (const CheckTransitionResult& data) + { + checkResult = data.checkResult; + lastAvgDiffAcc = data.lastAvgDiffAcc; + } + }; + + /// + /// Calculates the difference between two consecutive frames + /// + /// difference values + cv::Mat* FrameDifference(); + + /// + /// Change the current frame and move the previous one to last frame, to be ready for the next calculation + /// + /// The new frame with the calculated flash values + void virtual SetCurrentFrame(cv::Mat* flashValuesFrame); + + void virtual SetCurrentFrame(const IrisFrame& irisFrame) {}; + + /// + /// Checks if there has been enough variation from one frame to the next, if there is, the + /// positive and negative averages are calculated (to ensure only positive/negative values are + /// used to calculate the average, a threshold operation is used to separate the values) + /// + /// difference of flash values as frame(n) - frame(n-1) + /// average frame difference + float CheckSafeArea(cv::Mat* frameDifference); + + /// + /// Accumulates the average difference and returns true if a new transition is detected + /// + /// average difference of last two frames + /// accumulated average difference + /// + CheckTransitionResult CheckTransition(float avgDiff, float lastAvgDiffAcc); + + /// + /// Calculates the average frame luminance + /// + /// average frame + float FrameMean(); + inline float GetFrameMean() { return m_avgCurrentFrame; } + + float GetFlashArea() { return m_flashArea; } + + + cv::Mat* getCurrentFrame() { + return currentFrame; + } + + /// + /// + /// + /// + /// + /// + static float roundoff(float value, char prec); + + /// + /// Calculates sRGB values in the log file to later + /// This method is not called as the values are stored in a json file and the conversion is done with a look up table for performance + /// + void CalculateSrgbValues(); + + void ReleaseLastFrame(); + + void ReleaseCurrentFrame(); + + protected: + + /// + /// Determines if a transition has occurred + /// Returns true if a new transition occurrs + /// + /// last accumulated average difference + /// new accumulated average difference + bool IsFlashTransition(const float& lastAvgDiffAcc, const float& avgDiffAcc, const float& threshold); + + cv::Mat* lastFrame = nullptr; + cv::Mat* currentFrame = nullptr; + + private: + + /// Returns true if both numbers are positive or negative + /// 0 is both positive and negative as it means flash trend has not changed + inline bool SameSign(float num1, float num2) + { + return (num1 <= 0 && num2 <= 0) || (num1 >= 0 && num2 >= 0); + } + + FlashParams* m_params; + std::vector m_avgDiffInSecond; //all avg diff values in the current second (by increase or decrease) + int m_safeArea = 0; //area size in pixels that, if surpassed, indicates a transition may have occurred + static short fps; + + float m_avgCurrentFrame = 0; + float m_avgLastFrame = 0; + float m_flashArea = 0; + int m_frameSize = 0; //frame width * frame height + }; + +} \ No newline at end of file diff --git a/src/FlashDetection.cpp b/src/FlashDetection.cpp new file mode 100644 index 0000000..ccedae0 --- /dev/null +++ b/src/FlashDetection.cpp @@ -0,0 +1,169 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include "FlashDetection.h" +#include "iris/Configuration.h" +#include "utils/FrameConverter.h" +#include "RelativeLuminance.h" +#include "CDLuminance.h" +#include "RedSaturation.h" +#include +#include "FrameData.h" +#include "IrisFrame.h" +#include "iris/Log.h" +#include "iris/Result.h" + +namespace iris +{ + FlashDetection::FlashDetection(Configuration* configuration, const short& fps, const cv::Size& frameSize) + : m_transitionEvaluator(fps, configuration->GetTransitionEvaluatorParams()), m_sRgbConverter(configuration->GetFrameSrgbConverterParams()), m_fps(fps) + + { + if (configuration->GetLuminanceType() == Configuration::LuminanceType::RELATIVE) + { + m_luminance = new RelativeLuminance(fps, frameSize, configuration->GetLuminanceFlashParams()); + } + else + { + m_cdLuminanceConverter = new EA::EACC::Utils::FrameConverter(configuration->GetFrameCDLuminanceConverterParams()); + m_luminance = new CDLuminance(m_cdLuminanceConverter, fps, frameSize, configuration->GetLuminanceFlashParams()); + } + + m_redSaturation = new RedSaturation(fps, frameSize, configuration->GetRedSaturationFlashParams()); + //m_luminance->CalculateSrgbValues(); + } + + FlashDetection::~FlashDetection() + { + if (m_luminance != nullptr) + { + delete m_luminance; m_luminance = nullptr; + } + if (m_redSaturation != nullptr) + { + delete m_redSaturation; m_redSaturation = nullptr; + } + if (m_cdLuminanceConverter != nullptr) + { + delete m_cdLuminanceConverter; m_cdLuminanceConverter = nullptr; + } + } + + void FlashDetection::setLuminance(IrisFrame& irisFrame) + { + m_luminance->SetCurrentFrame(irisFrame); + irisFrame.luminanceFrame = m_luminance->getCurrentFrame(); + } + + void FlashDetection::checkFrame(const IrisFrame& irisFrame, const int& framePos, FrameData& data) + { + //red saturarion + m_redSaturation->SetCurrentFrame(irisFrame.sRgbFrame); + + data.LuminanceAverage = m_luminance->GetFrameMean(); + data.RedAverage = m_redSaturation->GetFrameMean(); + + if (framePos != 0) //check difference between frame(n) and frame (n - 1) + { + frameDifference(data); + m_transitionEvaluator.EvaluateSecond(framePos, m_fps, data); + } + + data.AverageLuminanceDiffAcc = m_lastAvgLumDiffAcc; + data.AverageRedDiffAcc = m_lastAvgRedDiffAcc; + } + + void FlashDetection::frameDifference(FrameData& data) + { + //Red Saturation difference + cv::Mat* redSaturationDiff = m_redSaturation->FrameDifference(); + + //Luminance difference + cv::Mat* luminanceDiff = m_luminance->FrameDifference(); + + float averageLuminaceDiff = m_luminance->CheckSafeArea(luminanceDiff); + float averageRedDiff = m_redSaturation->CheckSafeArea(redSaturationDiff); + + Flash::CheckTransitionResult redTranstion = m_redSaturation->CheckTransition(averageRedDiff, m_lastAvgRedDiffAcc); + m_lastAvgRedDiffAcc = redTranstion.lastAvgDiffAcc; + + Flash::CheckTransitionResult luminanceTransition = m_luminance->CheckTransition(averageLuminaceDiff, m_lastAvgLumDiffAcc); + m_lastAvgLumDiffAcc = luminanceTransition.lastAvgDiffAcc; + + //Evaluate and count new transitions + m_transitionEvaluator.SetTransitions(luminanceTransition.checkResult, redTranstion.checkResult, data); + + data.LuminanceFlashArea = data.proportionToPercentage(m_luminance->GetFlashArea()); + data.RedFlashArea = data.proportionToPercentage(m_redSaturation->GetFlashArea()); + + data.AverageLuminanceDiff = averageLuminaceDiff; + data.AverageRedDiff = averageRedDiff; + + delete redSaturationDiff; + delete luminanceDiff; + } + + bool FlashDetection::isFail() + { + if (m_transitionEvaluator.getFlashFail()) + { + LOG_CORE_INFO("Flash FAIL"); + } + + if (m_transitionEvaluator.getExtendedFailure()) + { + LOG_CORE_INFO("Extended Failure"); + } + + return m_transitionEvaluator.getFlashFail() || m_transitionEvaluator.getExtendedFailure(); + } + + cv::Mat* FlashDetection::getLuminanceFrame() + { + return m_luminance->getCurrentFrame(); + } + + void FlashDetection::setResult(Result& result) + { + //set incident counters + result.totalLuminanceIncidents = m_transitionEvaluator.getLuminanceIncidents(); + result.totalRedIncidents = m_transitionEvaluator.getRedIncidents(); + + //set overall result and results vector + if (m_transitionEvaluator.getLumFlashFail()) + { + result.OverallResult = AnalysisResult::Fail; + result.Results.emplace_back(AnalysisResult::LuminanceFlashFailure); + + LOG_CORE_INFO("Luminance Flash FAIL"); + } + + if (m_transitionEvaluator.getLumExtendedFailure()) + { + result.OverallResult = AnalysisResult::Fail; + result.Results.emplace_back(AnalysisResult::LuminanceExtendedFlashFailure); + LOG_CORE_INFO("Luminance Extended Failure"); + } + + if (m_transitionEvaluator.getRedFlashFail()) + { + result.OverallResult = AnalysisResult::Fail; + result.Results.emplace_back(AnalysisResult::RedFlashFailure); + + LOG_CORE_INFO("Red Flash FAIL"); + } + + if (m_transitionEvaluator.getRedExtendedFailure()) + { + result.OverallResult = AnalysisResult::Fail; + result.Results.emplace_back(AnalysisResult::RedExtendedFlashFailure); + LOG_CORE_INFO("Red Extended Failure"); + } + + if (result.OverallResult != AnalysisResult::Fail && ( m_transitionEvaluator.getLumPassWithWarning() || m_transitionEvaluator.getRedPassWithWarning() )) + { + result.OverallResult = AnalysisResult::PassWithWarning; + LOG_CORE_INFO("Pass with Warning"); + } + + } +} \ No newline at end of file diff --git a/src/FlashDetection.h b/src/FlashDetection.h new file mode 100644 index 0000000..4d564d9 --- /dev/null +++ b/src/FlashDetection.h @@ -0,0 +1,81 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +///////////////////////////////////////////////////////////////////////////// +// Implements the flash detection flow. The frames of a video are compared +// against the previous one to obtain the luminance or red saturation variation +// in order to check for possible flash transitions. The transitions are +// counted and evaluated at every moment of the video to flag all frames that +// do not comply with the guidelines and fail. +///////////////////////////////////////////////////////////////////////////// + +#pragma once +#include "TransitionEvaluator.h" +#include "utils/FrameConverter.h" +#include "opencv2/core/types.hpp" +#include "iris/Configuration.h" +#include "PhotosensitivityDetector.h" + +namespace cv +{ + class Mat; +} + +namespace iris +{ + class Flash; + class FrameData; + struct IrisFrame; + struct Result; + + class FlashDetection : public PhotosensitivityDetector + { + public: + FlashDetection(Configuration* configuration, const short& fps, const cv::Size& frameSize); + ~FlashDetection(); + + /// + /// Calculates the current frame's luminance values + /// + /// struct with video frame, frame in sRgb and current FrameData + void setLuminance(IrisFrame& irisFrame); + + /// + /// Compares the frame against the previous one to detect luminance and red saturation flashes + /// + /// struct with video frame, frame in sRgb and current FrameData + /// current frame position in video + /// FrameData to persist + void checkFrame(const IrisFrame& irisFrame, const int& framePos, FrameData& data) override; + + /// + /// Returns the flash analysis result + /// + bool isFail() override; + + /// + /// Sets the flash detection results for a chunk result object + /// + void setResult(Result& result) override; + + cv::Mat* getLuminanceFrame(); + + private: + + /// + /// Calculates red and luminance variations and checks for new transitions + /// + /// current frame position + /// FrameData to persist + void frameDifference(FrameData& data); + + TransitionEvaluator m_transitionEvaluator; + Flash* m_luminance = nullptr; + Flash* m_redSaturation = nullptr; + EA::EACC::Utils::FrameConverter m_sRgbConverter; + EA::EACC::Utils::FrameConverter* m_cdLuminanceConverter = nullptr; + + float m_lastAvgLumDiffAcc = 0; //first frame has 0 variation + float m_lastAvgRedDiffAcc = 0; //first frame has 0 variation + short m_fps = 0; + }; +} \ No newline at end of file diff --git a/src/FrameData.h b/src/FrameData.h new file mode 100644 index 0000000..f07e47f --- /dev/null +++ b/src/FrameData.h @@ -0,0 +1,305 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include +#include /* fmod */ +#include "utils/JsonWrapper.h" +#include "iris/TotalFlashIncidents.h" + +namespace iris +{ + static std::string msToTimeSpan(int ms) + { + float seconds = fmodf((ms / 1000.0f), 60); + int minutes = (int)floor((ms / (1000 * 60)) % 60); + int hours = (int)floor((ms / (1000 * 60 * 60)) % 24); + + std::string h = (hours < 10) ? "0" + std::to_string(hours) : std::to_string(hours); + std::string m = (minutes < 10) ? "0" + std::to_string(minutes) : std::to_string(minutes); + std::string s = (seconds < 10) ? "0" + std::to_string(seconds) : std::to_string(seconds); + + return h + ":" + m + ":" + s; + } + + class FrameData + { + public: + + FrameData() {}; + FrameData(int frame, long timeMs) : Frame(frame) + { + TimeStampMs = msToTimeSpan(timeMs); + }; + + //To convert the area proportion into a percentage + std::string proportionToPercentage(float proportion) + { + std::string str = std::to_string(proportion * 100); + return str.substr(0, str.find('.') + 3) + '%'; //truncate to two decimal points + } + + std::string ToCSV() + { + std::string csvOutput = std::to_string(Frame) + + ',' + TimeStampMs + + ',' + std::to_string(LuminanceAverage) + + ',' + LuminanceFlashArea + + ',' + std::to_string(AverageLuminanceDiff) + + ',' + std::to_string(AverageLuminanceDiffAcc) + + ',' + std::to_string(RedAverage) + + ',' + RedFlashArea + + ',' + std::to_string(AverageRedDiff) + + ',' + std::to_string(AverageRedDiffAcc) + + ',' + std::to_string(LuminanceTransitions) + + ',' + std::to_string(RedTransitions) + + ',' + std::to_string(LuminanceExtendedFailCount) + + ',' + std::to_string(RedExtendedFailCount) + + ',' + std::to_string((short)luminanceFrameResult) + + ',' + std::to_string((short)redFrameResult) + + ',' + patternArea + + ',' + std::to_string(patternDetectedLines) + + ',' + std::to_string((short)patternFrameResult) + '\0'; + return csvOutput; + } + + std::string CsvColumns() + { + std::string propertyNames [] = { + "Frame", + "TimeStamp", + "AverageLuminance", + "FlashAreaLuminance", + "AverageLuminanceDiff", + "AverageLuminanceDiffAcc", + "AverageRed", + "FlashAreaRed", + "AverageRedDiff", + "AverageRedDiffAcc", + "LuminanceTransitions", + "RedTransitions", + "LuminanceExtendedFailCount", + "RedExtendedFailCount", + "LuminanceFrameResult", + "RedFrameResult", + "PatternArea", + "PatternDetectedLines", + "PatternFrameResult" + }; + + std::string columns; + + bool first = true; + for (auto property : propertyNames) + { + if (!first) + { + columns += ',' + property; + } + else + { + columns += property; + first = false; + } + } + + return columns; + } + + /// + /// Frame index + /// + int Frame = 0; + + /// + /// frame timestamp in milliseconds + /// + std::string TimeStampMs = "00:00"; + std::string LuminanceFlashArea = "0.00%"; + float LuminanceAverage = 0; + float AverageLuminanceDiff = 0; + float AverageLuminanceDiffAcc = 0; + std::string RedFlashArea = "0.00%"; + float RedAverage = 0; + float AverageRedDiff = 0; + float AverageRedDiffAcc = 0; + float PatternRisk = 0; + int LuminanceTransitions = 0; + int RedTransitions = 0; + int LuminanceExtendedFailCount = 0; + int RedExtendedFailCount = 0; + FlashResult luminanceFrameResult = FlashResult::Pass; + FlashResult redFrameResult = FlashResult::Pass; + std::string patternArea = "0.00%"; + int patternDetectedLines = 0; + PatternResult patternFrameResult = PatternResult::Pass; + }; + + //Serializes FrameData to Json object + static void to_json(json& j, const FrameData& data) + { + j = json + { {"Frame", data.Frame}, + {"TimeStampString", data.TimeStampMs}, + {"AverageLuminance", data.LuminanceAverage}, + {"FlashAreaLuminance", data.LuminanceFlashArea}, + {"AverageLuminanceDiff", data.AverageLuminanceDiff}, + {"AverageLuminanceDiffAcc", data.AverageLuminanceDiffAcc}, + {"AverageRed", data.RedAverage}, + {"FlashAreaRed", data.RedFlashArea}, + {"AverageRedDiff", data.AverageRedDiff}, + {"AverageRedDiffAcc", data.AverageRedDiffAcc}, + {"LuminanceTransitions", data.LuminanceTransitions}, + {"RedTransitions", data.RedTransitions}, + {"LuminanceExtendedFailCount", data.LuminanceExtendedFailCount}, + {"RedExtendedFailCount", data.RedExtendedFailCount}, + {"LuminanceFrameResult", data.luminanceFrameResult}, + {"RedFrameResult", data.redFrameResult}, + {"PatternArea", data.patternArea}, + {"PatternDetectedLines", data.patternDetectedLines}, + {"PatternFrameResult", data.patternFrameResult} }; + }; + + struct FrameDataJson + { + void reserve(const int& size) + { + frame.reserve(size); + timeStampMs.reserve(size); + + luminanceFlashArea.reserve(size); + luminanceAverage.reserve(size); + averageLuminanceDiff.reserve(size); + averageLuminanceDiffAcc.reserve(size); + + redFlashArea.reserve(size); + redAverage.reserve(size); + averageRedDiff.reserve(size); + averageRedDiffAcc.reserve(size); + + luminanceTransitions.reserve(size); + redTransitions.reserve(size); + + luminanceExtendedFailCount.reserve(size); + redExtendedFailCount.reserve(size); + luminanceFrameResult.reserve(size); + redFrameResult.reserve(size); + + patternArea.reserve(size); + patternDetectedLines.reserve(size); + patternFrameResult.reserve(size); + + } + + void reserveLineGraphData(const int& size) + { + timeStampMs.reserve(size); + luminanceTransitions.reserve(size); + redTransitions.reserve(size); + luminanceFrameResult.reserve(size); + redFrameResult.reserve(size); + patternFrameResult.reserve(size); + } + + void push_back(const FrameData& data) + { + frame.push_back(data.Frame); + timeStampMs.push_back(data.TimeStampMs); + + luminanceFlashArea.push_back(data.LuminanceFlashArea); + luminanceAverage.push_back(data.LuminanceAverage); + averageLuminanceDiff.push_back(data.AverageLuminanceDiff); + averageLuminanceDiffAcc.push_back(data.AverageLuminanceDiffAcc); + + redFlashArea.push_back(data.RedFlashArea); + redAverage.push_back(data.RedAverage); + averageRedDiff.push_back(data.AverageRedDiff); + averageRedDiffAcc.push_back(data.AverageRedDiffAcc); + + luminanceTransitions.push_back(data.LuminanceTransitions); + redTransitions.push_back(data.RedTransitions); + + luminanceExtendedFailCount.push_back(data.LuminanceExtendedFailCount); + redExtendedFailCount.push_back(data.RedExtendedFailCount); + luminanceFrameResult.push_back((short)data.luminanceFrameResult); + redFrameResult.push_back((short)data.redFrameResult); + + patternArea.push_back(data.patternArea); + patternDetectedLines.push_back(data.patternDetectedLines); + patternFrameResult.push_back((short)data.patternFrameResult); + } + + void push_back_lineGraphData(const FrameData& data) + { + timeStampMs.push_back(data.TimeStampMs); + luminanceTransitions.push_back(data.LuminanceTransitions); + redTransitions.push_back(data.RedTransitions); + luminanceFrameResult.push_back((short)data.luminanceFrameResult); + redFrameResult.push_back((short)data.redFrameResult); + patternFrameResult.push_back((short)data.patternFrameResult); + } + + std::vector frame; + std::vector timeStampMs; + std::vector luminanceFlashArea; + std::vector luminanceAverage; + std::vector averageLuminanceDiff; + std::vector averageLuminanceDiffAcc; + std::vector < std::string> redFlashArea; + std::vector redAverage; + std::vector averageRedDiff; + std::vector averageRedDiffAcc; + std::vector luminanceTransitions; + std::vector redTransitions; + std::vector luminanceExtendedFailCount; + std::vector redExtendedFailCount; + std::vector luminanceFrameResult; + std::vector redFrameResult; + std::vector patternArea; + std::vector patternDetectedLines; + std::vector patternFrameResult; + }; + + //Serializes FrameData to Json object + static void to_json(json& j, const FrameDataJson& data) + { + if (data.frame.capacity() == 0) //if frame vector is empty, line graph data serialization + { + j = json + { + {"TimeStampString", data.timeStampMs}, + {"LuminanceTransitions", data.luminanceTransitions}, + {"RedTransitions", data.redTransitions}, + {"LuminanceFrameResult", data.luminanceFrameResult}, + {"RedFrameResult", data.redFrameResult}, + {"PatternFrameResult", data.patternFrameResult} + }; + } + else + { + j = json + { {"Frame", data.frame}, + {"TimeStampString", data.timeStampMs}, + {"AverageLuminance", data.luminanceAverage}, + {"FlashAreaLuminance", data.luminanceFlashArea}, + {"AverageLuminanceDiff", data.averageLuminanceDiff}, + {"AverageLuminanceDiffAcc", data.averageLuminanceDiffAcc}, + {"AverageRed", data.redAverage}, + {"FlashAreaRed", data.redFlashArea}, + {"AverageRedDiff", data.averageRedDiff}, + {"AverageRedDiffAcc", data.averageRedDiffAcc}, + {"LuminanceTransitions", data.luminanceTransitions}, + {"RedTransitions", data.redTransitions}, + {"LuminanceExtendedFailCount", data.luminanceExtendedFailCount}, + {"RedExtendedFailCount", data.redExtendedFailCount}, + {"LuminanceFrameResult", data.luminanceFrameResult}, + {"RedFrameResult", data.redFrameResult}, + {"PatternArea", data.patternArea}, + {"PatternDetectedLines", data.patternDetectedLines}, + {"PatternFrameResult", data.patternFrameResult} }; + } + }; + +} + + + diff --git a/src/FrameRgbConverter.cpp b/src/FrameRgbConverter.cpp new file mode 100644 index 0000000..b99c6b4 --- /dev/null +++ b/src/FrameRgbConverter.cpp @@ -0,0 +1,39 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +/* +**** FramesRgbConverter **** +This class converts a frame - an openCV's Mat object - from 8 bits RGB (0 - 255) to sRGB (decimal values from 0 to 1) +*/ + +#include "FrameRgbConverter.h" +#include +#include "ConfigurationParams.h" + +namespace iris +{ + FrameRgbConverter::FrameRgbConverter(std::vector &sRgbValues) + { + m_pMatSrgbValues = new cv::Mat(sRgbValues, true); + } + + FrameRgbConverter::FrameRgbConverter(FrameRgbConverterParams* params) : params(params) + { + m_pMatSrgbValues = new cv::Mat(params->sRgbValues, true); + } + + FrameRgbConverter::~FrameRgbConverter() + { + if (m_pMatSrgbValues != nullptr) { + m_pMatSrgbValues->release(); + delete m_pMatSrgbValues; + } + } + + cv::Mat* FrameRgbConverter::ConvertBgrFrameToSrgb(cv::Mat& bgrMat) + { + cv::Mat* sRgbMat = new cv::Mat(); + cv::LUT(bgrMat, *m_pMatSrgbValues, *sRgbMat); + return sRgbMat; + } +} + diff --git a/src/FrameRgbConverter.h b/src/FrameRgbConverter.h new file mode 100644 index 0000000..00a140d --- /dev/null +++ b/src/FrameRgbConverter.h @@ -0,0 +1,66 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +///////////////////////////////////////////////////////////////////////////// +// Implements a small wrapper of a logging library to use logging in C++ projects. +// To use ILog, implement a Log class that inherits from ILog and define the sinks +// needed for the loggers. +///////////////////////////////////////////////////////////////////////////// + +#pragma once +#include + +namespace cv +{ + class Mat; +} + +namespace iris +{ + struct FrameRgbConverterParams; + struct IrisFrame; + + class FrameRgbConverter + { + public: + /// + /// Create an instance of the Rgb converter + /// + /// The array of decimal values for Bgr to sRgb convertion + FrameRgbConverter(std::vector& c); + + /// + /// Create an instance of the Rgb converter + /// + /// A FrameRgbConverterParams struct containing the vector of decimal values for Bgr to sRgb convertion + FrameRgbConverter(FrameRgbConverterParams* params); + + /// + /// + /// + /// + /// + cv::Mat* ConvertBgrFrameToSrgb(cv::Mat& mat); + + /// + /// + /// + /// + cv::Mat* GetSrgbTable() + { + return m_pMatSrgbValues; + }; + + /// + /// Destructor for Rgb converter + /// + ~FrameRgbConverter(); + private: + + /// + /// The input array containing the decimal values for the sRgb convertion + /// + cv::Mat* m_pMatSrgbValues = nullptr; + + FrameRgbConverterParams* params = nullptr; //config params + }; +} \ No newline at end of file diff --git a/src/IrisFrame.h b/src/IrisFrame.h new file mode 100644 index 0000000..6294ce9 --- /dev/null +++ b/src/IrisFrame.h @@ -0,0 +1,29 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include +#include "FrameData.h" + +namespace iris +{ + struct IrisFrame + { + IrisFrame() {}; + IrisFrame(cv::Mat* originalFrame, FrameData frameData) : originalFrame(originalFrame), frameData(frameData) {}; + IrisFrame(cv::Mat* originalFrame, cv::Mat* sRgbFrame, FrameData frameData) : originalFrame(originalFrame), sRgbFrame(sRgbFrame), frameData(frameData) {}; + + void Release() + { + //Release only sRgbFrame as originalFrame is a reference, no malloc is done for it + if (sRgbFrame != nullptr) + { + delete sRgbFrame; sRgbFrame = nullptr; + } + } + + cv::Mat* originalFrame = nullptr; //Video frame in BGR color space + cv::Mat* sRgbFrame = nullptr; //Converted video frame to sRGB color space + cv::Mat* luminanceFrame = nullptr; //Converted video frame to luminance + FrameData frameData; //Frame info + }; +} \ No newline at end of file diff --git a/src/Log.cpp b/src/Log.cpp new file mode 100644 index 0000000..3ec547a --- /dev/null +++ b/src/Log.cpp @@ -0,0 +1,44 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include "iris/Log.h" +#include +#include +#include +#include + +namespace iris +{ + std::shared_ptr Log::m_DataLogger; + + void Log::Init(bool console, bool file, int coreLevel, const char* dataFile) + { + try + { + //Init Core Logger + InitCoreLogger(console, file, coreLevel, RotatingFileSinkParams("logs/core.log"), "%^[%T] [%t] [%l]: %v%$"); + + //Init Data Logger + if (dataFile != nullptr) //can be set by video + { + auto dataFileSink = RotatingFileSink(RotatingFileSinkParams(dataFile)); + m_DataLogger = std::make_shared("DataLogger", dataFileSink); + } + else + { + m_DataLogger = std::make_shared("DataLogger"); + } + + m_DataLogger->set_pattern("%v"); + + } + catch (const spdlog::spdlog_ex& ex) + { + std::cout << "Core Log initialization failed: " << ex.what() << std::endl; + } + } + + void Log::SetDataLoggerFile(const char* fileName) + { + SetLoggerFile(m_DataLogger, RotatingFileSinkParams(fileName), "%v"); + } +} \ No newline at end of file diff --git a/src/PatternDetection.cpp b/src/PatternDetection.cpp new file mode 100644 index 0000000..9391749 --- /dev/null +++ b/src/PatternDetection.cpp @@ -0,0 +1,565 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include "PatternDetection.h" +#include "FrameData.h" +#include "IrisFrame.h" +#include "iris/Result.h" +#include "iris/Log.h" +#include "iris/Configuration.h" +#include "ConfigurationParams.h" +#include "iris/TotalFlashIncidents.h" + + +#include +#include +#include +#include +#include + +#ifdef DEBUG_PATTERN_DETECTION +#include +#endif + +namespace iris +{ + +PatternDetection::PatternDetection(Configuration* configuration, const short& fps, const cv::Size& frameSize) + : m_params(configuration->GetPatternDetectionParams()), m_fps(fps), m_isFail(false), m_patternFailFrames(0) + +{ + if (frameSize.width > 480) + { + int scale_percent = 50; // percent of original size + int width = int(frameSize.width * scale_percent / 100); + int height = int(frameSize.height * scale_percent / 100); + scaleSize = { width, height }; + } + else + { + scaleSize = frameSize; + } + + m_safeArea = (int)(scaleSize.area() * m_params->areaProportion); + m_thresholdArea = (int)(scaleSize.area() * 0.20f); //20% of the screen display + m_frameTimeThresh = (int)(fps * m_params->timeThreshold); + m_contourThreshArea = (int)(scaleSize.area() * 0.00155); + m_frameSize = scaleSize.area(); + + m_patternFrameCount = {}; + m_patternFrameCount.count.reserve(m_frameTimeThresh); + m_patternFrameCount.count.push_back(0); + + //TODO:: CACULATE CROPPED IMAGE + centerPoint = cv::Point(scaleSize.width / 2, scaleSize.height / 2); + + int dilation_size = 1; + m_dilationElement = cv::getStructuringElement(cv::MORPH_RECT, + cv::Size(2 * dilation_size + 1, 2 * dilation_size + 1), + cv::Point(dilation_size, dilation_size)); + + int erosion_size = 2; + m_erosionElement = cv::getStructuringElement(cv::MORPH_RECT, + cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1), + cv::Point(erosion_size, erosion_size)); +} + +void PatternDetection::checkFrame(const IrisFrame& irisFrame, const int& framePos, FrameData& data) +{ + auto pattern = detectPattern(irisFrame); + +#ifdef DEBUG_PATTERN_DETECTION + cv::destroyAllWindows(); +#endif // DEBUG_PATTERN_DETECTION + + bool harmful = false; + if (pattern.area >= m_safeArea && pattern.nComponents>= m_params->minStripes && pattern.avgLightLuminance >= 0.25) + { + harmful = true; + data.patternArea = data.proportionToPercentage(pattern.area / (float)m_frameSize); + data.patternDetectedLines = pattern.nComponents; + } + + m_patternFrameCount.updateCurrent(harmful); + checkFrameCount(data); +} + +PatternDetection::Pattern PatternDetection::detectPattern(const IrisFrame& irisFrame) +{ + cv::Mat luminance, luminance_8UC, iftThresh; + cv::resize(*irisFrame.luminanceFrame, luminance, scaleSize); + + //normalize luminance values (ensures proper contrast if existing pattern) + cv::normalize(luminance, luminance_8UC, 0, 255, cv::NORM_MINMAX); + luminance_8UC.convertTo(luminance_8UC, CV_8UC1); + + SHOW_IMG(luminance_8UC, "8bit Luminance Frame"); + + if (hasPattern(luminance_8UC, iftThresh)) + { + Pattern pattern; + auto [patternRegionMask, nComponents] = getPatternRegion(iftThresh, luminance_8UC); + + if (nComponents != -1) + { + pattern.nComponents = nComponents; + pattern.area = cv::countNonZero(patternRegionMask); + setPatternLuminance(pattern, patternRegionMask, luminance_8UC, luminance); + return pattern; + } + } + + return { }; +} + +void PatternDetection::checkFrameCount(FrameData& data) +{ + if (m_patternFrameCount.current >= m_frameTimeThresh) + { + data.patternFrameResult = PatternResult::Fail; + m_isFail = true; + m_patternFailFrames += 1; + } + else + { + data.patternFrameResult = PatternResult::Pass; + } + + if (m_patternFrameCount.count.size() == m_frameTimeThresh) + { + m_patternFrameCount.updatePassed(); + } +} + +bool PatternDetection::hasPattern(const cv::Mat& luminanceFrame, cv::Mat& iftThresh) +{ + //obtain the power spectrum then use it to filter the magnitude + FourierTransform ft(centerPoint); + FourierTransform::DftComponents dftComps = ft.getPSD(luminanceFrame); + cv::Mat peaks = ft.getPeaks(dftComps.powerSpectrum); + ft.filterMagnitude(peaks, dftComps.magnitude); + + //reconstruct image + cv::Mat ift = ft.getIFT(dftComps); + +#ifdef DEBUG_FFT + SHOW_IMG(ift, "IFT"); +#endif // DEBUG_FFT + + iftThresh = highlightPatternArea(ift, luminanceFrame); + + //if the area threshold has not been reached, no harmful pattern exists + if (cv::countNonZero(iftThresh) < m_thresholdArea) + { + return false; + } + return true; +} + +cv::Mat PatternDetection::highlightPatternArea(cv::Mat& ift, const cv::Mat& luminanceFrame) +{ + if (ift.size() != luminanceFrame.size()) + { + //resize IFT to match size of original frame + cv::resize(ift, ift, luminanceFrame.size()); + +#ifdef DEBUG_IFT + SHOW_IMG(ift, "IFT Resize"); +#endif // DEBUG_IFT + } + + cv::Mat absDiff; + cv::absdiff(ift, luminanceFrame, absDiff); +#ifdef DEBUG_IFT + SHOW_IMG(absDiff, "Absolute Difference"); +#endif // DEBUG_IFT + cv::Mat thresh; + cv::threshold(absDiff, thresh, 60, 255, cv::ThresholdTypes::THRESH_BINARY); +#ifdef DEBUG_IFT + SHOW_IMG(thresh, "IFT Abs Diff Binary Threshold"); +#endif // DEBUG_IFT + + return thresh; +} + +std::tuple PatternDetection::getPatternRegion(cv::Mat& threshIFT, cv::Mat& luminanceFrame) +{ + cv::erode(threshIFT, threshIFT, m_erosionElement); +#ifdef DEBUG_PATTERN_REGION + SHOW_IMG(threshIFT, "Erosion"); +#endif // DEBUG_PATTERN_REGION + + //remove elements that are not part of the pattern + auto threshContours = getContours(threshIFT); +#ifdef DEBUG_PATTERN_REGION + SHOW_IMG(threshIFT, "Erosion Contours"); +#endif // DEBUG_PATTERN_REGION + auto contoursMat = moveBiggerContours(threshContours, threshIFT); + +#ifdef DEBUG_PATTERN_REGION + SHOW_IMG(contoursMat, "Remaining Contours"); +#endif // DEBUG_PATTERN_REGION + +// cv::dilate(contoursMat, contoursMat, m_dilationElement); +//#ifdef DEBUG_PATTERN_REGION +// SHOW_IMG(contoursMat, "Dilation"); +//#endif // DEBUG_PATTERN_REGION + + auto dilationContours = getContours(contoursMat); +#ifdef DEBUG_PATTERN_REGION + SHOW_CONTOURS(dilationContours, contoursMat, "Dilation Contours"); +#endif // DEBUG_PATTERN_REGION + + if (dilationContours.empty()) + { + return { cv::Mat(), -1}; //return empty mat and error code + } + +#ifdef DEBUG_PATTERN_REGION + auto [patternContour, patternComponents] = getPatternContour(dilationContours, contoursMat); +#else + auto [patternContour, patternComponents] = getPatternContour(dilationContours); +#endif // DEBUG_PATTERN_REGION + + //get region rect + cv::RotatedRect boundingRect = cv::minAreaRect(patternContour); + std::vector rect_points2f; + rect_points2f.resize(4); + boundingRect.points(&rect_points2f[0]); + std::vector rect_points(rect_points2f.begin(), rect_points2f.end()); + std::vector> contourPoints = {rect_points}; +#ifdef DEBUG_PATTERN_REGION + SHOW_CONTOURS(contourPoints, contoursMat, "Pattern Region"); +#endif // DEBUG_PATTERN_REGION + + //separate pattern region from the rest of the frame image using a mask + cv::Mat patternRegionMask = cv::Mat(threshIFT.size(), CV_8UC1, cv::Scalar(0)); + cv::drawContours(patternRegionMask, contourPoints, -1, cv::Scalar(255), cv::FILLED); + + //isolate the pattern in the frame with the pattern mask + cv::bitwise_and(luminanceFrame, patternRegionMask, luminanceFrame); + +#ifdef DEBUG_PATTERN_REGION + SHOW_IMG(patternRegionMask, "Pattern Region Mask"); + SHOW_IMG(luminanceFrame, "Approximated Pattern Region"); +#endif // DEBUG_PATTERN_REGION + + return { patternRegionMask, patternComponents }; +} + +void PatternDetection::setPatternLuminance(Pattern& pattern, cv::Mat& patternRegion, const cv::Mat& luminance8UC, const cv::Mat& luminanceFrame) +{ + cv::Mat lightComponents, darkComponents; + cv::threshold(luminance8UC, lightComponents, 0, 255, cv::THRESH_OTSU); + SHOW_IMG(lightComponents, "Light Components"); + + cv::bitwise_not(lightComponents, darkComponents, patternRegion); + SHOW_IMG(darkComponents, "Dark Components"); + + pattern.avgLightLuminance = (float)cv::mean(luminanceFrame, lightComponents)[0]; + pattern.avgDarkLuminance = (float)cv::mean(luminanceFrame, darkComponents)[0]; +} + +std::vector> PatternDetection::getContours(const cv::Mat& src) +{ + std::vector> contours; + cv::findContours( // Detect contours of input image + src, // 8bit edges input frame + contours, // Set of points defining a contour + cv::RETR_EXTERNAL, // Contour retrieval mode + cv::ContourApproximationModes::CHAIN_APPROX_SIMPLE // Contour approximation mode + ); + + return contours; +} + +cv::Mat PatternDetection::moveBiggerContours(std::vector>& contours, cv::Mat& src) +{ + cv::Mat contoursMat = cv::Mat::zeros(src.size(), src.type()); + //remove small contours + for (int i = 0; i < contours.size(); i++) + { + if (cv::contourArea(contours[i]) > m_contourThreshArea) + { + cv::fillConvexPoly(contoursMat, contours[i], cv::Scalar(255), cv::LineTypes::LINE_8); + } + } + + return contoursMat; +} + +std::vector PatternDetection::getBiggestContour(const std::vector>& contours) +{ + std::vector biggestContour = contours[0]; + double biggestArea = cv::contourArea(contours[0]); + + for (const auto& contour : contours) //biggest area should be the pattern + { + double area = cv::contourArea(contour); + + if (area > biggestArea) + { + biggestArea = area; + biggestContour = contour; + } + } + + return biggestContour; +} + +#ifdef DEBUG_PATTERN_REGION +std::tuple, int> PatternDetection::getPatternContour(const std::vector>& contours, const cv::Mat& contoursMat) +#else +std::tuple, int> PatternDetection::getPatternContour(const std::vector>& contours) +#endif // DEBUG_PATTERN_REGION +{ +#ifdef DEBUG_PATTERN_REGION + if (contours.size() < 5) + { + return { getBiggestContour(contours), 0 }; + } + else + { + auto [contour, nContours] = getSimilarContours(contours, contoursMat); + return { contour, nContours }; + } +#else + if (contours.size() < 5) + { + return { getBiggestContour(contours), 0 }; + } + else + { + auto [contour, nContours] = getSimilarContours(contours); + return { contour, nContours }; + } +#endif // DEBUG_PATTERN_REGION +} + +#ifdef DEBUG_PATTERN_REGION +std::tuple, int> PatternDetection::getSimilarContours(const std::vector>& contours, const cv::Mat& contoursMat) +#else +std::tuple, int> PatternDetection::getSimilarContours(const std::vector>& contours) +#endif +{ + using ContourGroup = std::vector>; + std::vector similarContours; + + //group contours by shape similarity + for (int i = 1; i < contours.size(); i++) + { + ContourGroup group{ contours[i] }; + + for (int j = 0; j < contours.size(); j++) + { + if (j != i) + { + double similarity = cv::matchShapes(contours[i], contours[j], cv::ShapeMatchModes::CONTOURS_MATCH_I1, 0); + if (similarity < 0.7) + { + group.emplace_back(contours[j]); + } + } + } + + similarContours.emplace_back(group); + } + + //sort contour groups by group size + std::sort(similarContours.begin(), similarContours.end(), [](const auto& a, const auto& b) + { + return a.size() > b.size(); + }); + +#ifdef DEBUG_PATTERN_REGION + SHOW_CONTOURS(similarContours[0], contoursMat, "Grouped Contours"); +#endif + + //merge contours in one (to properly obtain the min area rect) + std::vector contour; + for (auto& group : similarContours[0]) + { + contour.insert(contour.end(), group.begin(), group.end()); + } + + return { contour, similarContours[0].size()}; +} + +bool PatternDetection::isFail() +{ + LOG_CORE_INFO("Pattern FAIL: {}", (m_isFail ? "true" : "false")); + return m_isFail; +} + +void PatternDetection::setResult(Result& result) +{ + if (isFail()) + { + result.OverallResult = AnalysisResult::Fail; + result.Results.push_back(AnalysisResult::PatternFailure); + result.patternFailFrames = m_patternFailFrames; + } + +} + + +#ifdef DEBUG_PATTERN_DETECTION +void showImg(const cv::Mat& src, const char* wName) +{ + cv::imshow(wName, src); + cv::waitKey(); +} +void showContours(const std::vector>& contours, const cv::Mat src, const char* winName) +{ + cv::Mat contourImg; + cv::cvtColor(src, contourImg, cv::COLOR_GRAY2BGR); + cv::drawContours(contourImg, contours, -1, cv::Scalar(0, 0, 255), 2); + SHOW_IMG(contourImg, winName); +} +#endif + + +FourierTransform::DftComponents FourierTransform::getPSD(const cv::Mat& src) +{ + cv::Mat dft = getDFT(src); + DftComponents dftComps = getDftComponents(dft); + + //compute PSD + dftComps.powerSpectrum = dftComps.magnitude.clone(); + normalize(dftComps.powerSpectrum, -1.0f, 1.0f); + dftComps.powerSpectrum = cv::abs(dftComps.powerSpectrum); + dftComps.powerSpectrum = 1 - dftComps.powerSpectrum; + cv::pow(dftComps.powerSpectrum, 2, dftComps.powerSpectrum); + log(dftComps.powerSpectrum); + normalize(dftComps.powerSpectrum, 0, 255); + +#ifdef DEBUG_FFT + fftShift(dftComps.powerSpectrum); + SHOW_IMG(dftComps.powerSpectrum , "PSD"); + fftShift(dftComps.powerSpectrum); +#endif // DEBUG_FFT + + return dftComps; +} + +cv::Mat FourierTransform::getPeaks(const cv::Mat& psd) +{ + //threshold peaks + cv::Mat threshPSD; + psd.convertTo(threshPSD, CV_8UC1); + cv::threshold(threshPSD, threshPSD, 7, 255, cv::ThresholdTypes::THRESH_OTSU); + +#ifdef DEBUG_FFT + fftShift(threshPSD); + SHOW_IMG(threshPSD, "PSD Binary Threshold"); + fftShift(threshPSD); +#endif // DEBUG_FFT + + return threshPSD; +} + +void FourierTransform::filterMagnitude(cv::Mat& peaks, cv::Mat& magnitude) +{ + //prepare mask in peaks mat + fftShift(peaks); + peaks.convertTo(peaks, CV_8UC1); + cv::circle(peaks, centerPoint, 5, 0, -1); + +#ifdef DEBUG_FFT + SHOW_IMG(peaks, "Magnitude mask"); +#endif // DEBUG_FFT + + fftShift(peaks); + + //remove peaks in magnitude with mask + magnitude = magnitude.setTo(0, peaks); +} + + +cv::Mat FourierTransform::getIFT(DftComponents dftComps) +{ + cv::Mat dft, ift; + cv::Mat planes[] = { cv::Mat(), cv::Mat() }; //planes[0] = real, planes[1] = imag + + //recover the complex dft from the magnitude and phase + cv::polarToCart(dftComps.magnitude, dftComps.phase, planes[0], planes[1]); + cv::merge(planes, 2, dft); + + //inverse the dft to reconstruct the image + cv::dft(dft, ift, cv::DFT_INVERSE | cv::DFT_REAL_OUTPUT); + ift.convertTo(ift, CV_8U, 255.0); // Back to 8-bits + return ift; +} + +cv::Mat FourierTransform::getDFT(const cv::Mat& src) +{ + cv::Mat padded; + int m = cv::getOptimalDFTSize(src.rows); + int n = cv::getOptimalDFTSize(src.cols); // on the border add zero values + cv::copyMakeBorder(src, padded, 0, m - src.rows, 0, n - src.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0)); + + //TODO:: needed if using original frame but not if it's the luminance frame + padded.convertTo(padded, CV_32F, 1.0 / 255.0); //this allows proper image reconstruction + + // Add to the expanded another plane with zeros this way the result may fit in the source matrix + cv::Mat planes[] = { cv::Mat_(padded), cv::Mat::zeros(padded.size(), CV_32F) }; + cv::Mat dft; + cv::merge(planes, 2, dft); + cv::dft(dft, dft, cv::DFT_SCALE | cv::DFT_COMPLEX_OUTPUT); + return dft; +} + +FourierTransform::DftComponents FourierTransform::getDftComponents(cv::Mat& dft) +{ + DftComponents dftComps(dft); + cv::Mat planes[2]; //plane[0] = real, plane[1] = imag + cv::split(dft, planes); + cv::cartToPolar(planes[0], planes[1], dftComps.magnitude, dftComps.phase); + return dftComps; +} + +void FourierTransform::fftShift(cv::Mat& src) +{ + // crop the spectrum, if it has an odd number of rows or columns + cv::Mat aux = src(cv::Rect(0, 0, src.cols & -2, src.rows & -2)); + + // rearrange the quadrants of Fourier image so that the origin is at the image center + int cx = aux.cols / 2; + int cy = aux.rows / 2; + + cv::Mat q0(aux, cv::Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant + cv::Mat q1(aux, cv::Rect(cx, 0, cx, cy)); // Top-Right + cv::Mat q2(aux, cv::Rect(0, cy, cx, cy)); // Bottom-Left + cv::Mat q3(aux, cv::Rect(cx, cy, cx, cy)); // Bottom-Right + + // swap quadrants (Top-Left with Bottom-Right) + cv::Mat tmp; + q0.copyTo(tmp); + q3.copyTo(q0); + tmp.copyTo(q3); + + // swap quadrant (Top-Right with Bottom-Left) + q1.copyTo(tmp); + q2.copyTo(q1); + tmp.copyTo(q2); + + //Assing the aut to the src, keeping the odd column/row as it was + src(cv::Rect(0, 0, src.cols & -2, src.rows & -2)) = aux; +} + +void FourierTransform::log(cv::Mat& src) +{ + // switch to logarithmic scale + src += cv::Scalar::all(1); + cv::log(src, src); +} + +void FourierTransform::normalize(cv::Mat& src, float min, float max) +{ + // viewable image form (float between values 0 and 1) transform the matrix with float values into a + cv::normalize(src, src, min, max, cv::NORM_MINMAX); +} + + + +} //namespace iris + diff --git a/src/PatternDetection.h b/src/PatternDetection.h new file mode 100644 index 0000000..89c2c78 --- /dev/null +++ b/src/PatternDetection.h @@ -0,0 +1,192 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include "PhotosensitivityDetector.h" +#include + +namespace iris +{ + class FrameData; + class Configuration; + struct IrisFrame; + struct Result; + struct PatternDetectionParams; + +#ifdef _DEBUG +//#define DEBUG_PATTERN_DETECTION +#endif // _DEBUG + +#ifdef DEBUG_PATTERN_DETECTION + static void showImg(const cv::Mat& src, const char* wName = ""); + static void showContours(const std::vector>& contours, const cv::Mat src, const char* winName = "Contours"); +#define SHOW_IMG(mat, wName) showImg(mat, wName) +#define SHOW_CONTOURS(contours, mat, wName) showContours(contours, mat, wName) +#define DEBUG_FFT +#define DEBUG_IFT +#define DEBUG_PATTERN_REGION +#else +#define SHOW_IMG(mat, wName) +#define SHOW_CONTOURS(contours, mat, wName) +#endif // !DEBUG_PATTERN_DETECTION + + +class PatternDetection : public PhotosensitivityDetector +{ +public: + PatternDetection(Configuration* configuration, const short& fps, const cv::Size& frameSize); + + //Checks a video frame for harmful patterns + void checkFrame(const IrisFrame& irisFrame, const int& framePos, FrameData& data) override; + + //returns true if the video is not compliant to the pattern guidelines + bool isFail() override; + + //sets the results of the pattern detection + void setResult(Result& result) override; + +private: + + struct Pattern + { + int area; + int nComponents; //stripes, circles, rectangles, etc + float avgDarkLuminance; //average luminance of pattern dark components + float avgLightLuminance; //average luminance of pattern light components + }; + + void checkFrameCount(FrameData& data); + //detects a pattern in a video frame and returns the pattern info + Pattern detectPattern(const IrisFrame& irisFrame); + + //determines whether a video frame has a pattern or not + bool hasPattern(const cv::Mat& luminanceFrame, cv::Mat& ift); + + //applies operations on the inverse Fourier transform to highlight the pattern area + cv::Mat highlightPatternArea(cv::Mat& ift, const cv::Mat& luminanceFrame); + + //obtains the pattern region mask and the number of pattern components + std::tuple getPatternRegion(cv::Mat& otsu, cv::Mat& luminanceFrame); + + //calculates the luminance of the pattern components + void setPatternLuminance(Pattern& pattern, cv::Mat& patternRegion, const cv::Mat& luminance8UC, const cv::Mat& luminanceFrame); + + //obtains the contours of matrix + std::vector> getContours(const cv::Mat& src); + + //contours that are bigger than the area threshold are moved to another Mat + //as to avoid having pixels of the smaller contours + cv::Mat moveBiggerContours(std::vector>& contours, cv::Mat& src); + + //returns the contour with the biggest area + std::vector getBiggestContour(const std::vector>& contours); + +#ifndef DEBUG_PATTERN_REGION + //obtains the contour of the pattern region and the number pattern components + std::tuple, int> getPatternContour(const std::vector>& contours); + + //groups contours by shape + std::tuple, int> getSimilarContours(const std::vector>& contours); +#else + //same methods but the contourMat img is passed to show debug images + std::tuple, int> getPatternContour(const std::vector>& contours, + const cv::Mat& contourMat); + std::tuple, int> getSimilarContours(const std::vector>& contours, + const cv::Mat& contourMat); +#endif // DEBUG_PATTERN_REGION true + + + + struct Counter + { + void updateCurrent(bool add) + { + if (add) + { + count.emplace_back(count.back() + 1); + } + else + { + count.emplace_back(count.back()); + } + + current = count.back() - passed; + } + + void updatePassed() + { + passed = count.front(); + count.erase(count.begin()); + } + + std::vector count; + int passed = 0; + int current = 0; + }; + + PatternDetectionParams* m_params; + + + Counter m_patternFrameCount; + + int m_frameTimeThresh; + int m_patternFailFrames; + + short m_fps; + int m_safeArea; + int m_frameSize; + int m_thresholdArea; //20% of the frame size to check if there is a possible pattern + bool m_isFail; + int areaThresh; + int m_contourThreshArea; + + cv::Mat m_dilationElement; + cv::Mat m_erosionElement; + cv::Point centerPoint; + cv::Size scaleSize; //downscale the video frame to this size if the resolutions is high enough +}; + +class FourierTransform +{ +public: + struct DftComponents; + struct Peak; + + FourierTransform(cv::Point& center) : centerPoint(center) {}; + + /// + /// + /// + /// + /// + DftComponents getPSD(const cv::Mat& psd); + + cv::Mat getPeaks(const cv::Mat& psd); + + void filterMagnitude(cv::Mat& peaks, cv::Mat& magnitude); + + cv::Mat getIFT(DftComponents dftComps); + + + struct DftComponents + { + DftComponents(const cv::Mat& src) + { + magnitude = cv::Mat(src.rows, src.cols, CV_32FC1); + phase = cv::Mat(src.rows, src.cols, CV_32FC1); + powerSpectrum = cv::Mat(src.rows, src.cols, CV_32FC1); + }; + + cv::Mat phase, magnitude, powerSpectrum; + }; + +private: + + cv::Mat getDFT(const cv::Mat& src); + DftComponents getDftComponents(cv::Mat& dft); + void fftShift(cv::Mat& src); + void log(cv::Mat& src); + void normalize(cv::Mat& src, float min, float max); + const cv::Point& centerPoint; +}; + +} \ No newline at end of file diff --git a/src/PhotosensitivityDetector.h b/src/PhotosensitivityDetector.h new file mode 100644 index 0000000..20f81a2 --- /dev/null +++ b/src/PhotosensitivityDetector.h @@ -0,0 +1,34 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once + +namespace iris +{ + struct Result; + class FrameData; + struct IrisFrame; + +class PhotosensitivityDetector +{ +public: + + /// + /// + /// + virtual void checkFrame(const IrisFrame& irisFrame, const int& framePos, FrameData& data) = 0; + + /// + /// Returns true if a photosensitivity issues have been found + /// + virtual bool isFail() = 0; + + /// + /// Sets the flash/pattern detection results for a result object + /// + virtual void setResult(Result& result) = 0; + +protected: + +}; + +} \ No newline at end of file diff --git a/src/RedSaturation.cpp b/src/RedSaturation.cpp new file mode 100644 index 0000000..a4d9c4b --- /dev/null +++ b/src/RedSaturation.cpp @@ -0,0 +1,58 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include "RedSaturation.h" +#include +#include "opencv2/imgproc.hpp" +#include "ConfigurationParams.h" + +namespace iris +{ + RedSaturation::RedSaturation(short videoFPS, const cv::Size& frameSize, FlashParams* params) + : Flash(videoFPS, frameSize, params) + { + + } + + RedSaturation::~RedSaturation() + { + ReleaseLastFrame(); + ReleaseCurrentFrame(); + } + + void RedSaturation::SetCurrentFrame(cv::Mat* sRgbFrame) + { + cv::Mat* frame = new cv::Mat(sRgbFrame->size(), CV_32FC1, cv::Scalar(0)); + sRgbFrame->forEach(CalculateRedSaturation(frame)); + + Flash::ReleaseLastFrame(); + Flash::SetCurrentFrame(frame); + } + + //// if R / (R + G + B) >= 0.8 => pixel is saturated red + //cv::Mat* RedSaturation::RedSaturationValue(cv::Mat channels[]) + //{ + // cv::Mat* redMask = new cv::Mat(); + + // //cv::transform(*sRgbFrame, *redMask, cv::Matx13f(1, 1, 1)); + // cv::add(channels[0], channels[1], *redMask); + // cv::add(channels[2], *redMask, *redMask); + // cv::divide(channels[2], *redMask, *redMask); + + // //if pixel >= 0.8 => pixel = 255 else pixel = 0 + // cv::threshold(*redMask, *redMask, 0.8f, 255, cv::THRESH_BINARY); + // redMask->convertTo(*redMask, CV_8UC1); + + // return redMask; + //} + + //void RedSaturation::RedSatCoefficient(cv::Mat* frame, cv::Mat channels[], cv::Mat* redMask) + //{ + // //if (R - G - B) * 320 > 20 => value to check for new transitions with frame diff + // cv::subtract(channels[2], channels[1], *frame, *redMask); + // cv::subtract(*frame, channels[0], *frame, *redMask); + // cv::multiply(*frame, 320, *frame); + + // //Negative values are set to 0 + // cv::threshold(*frame, *frame, 0, 320, cv::THRESH_TOZERO); + //} +} \ No newline at end of file diff --git a/src/RedSaturation.h b/src/RedSaturation.h new file mode 100644 index 0000000..d092628 --- /dev/null +++ b/src/RedSaturation.h @@ -0,0 +1,66 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once + +#include "Flash.h" +#include "opencv2/core/types.hpp" + +namespace cv +{ + class Mat; +} + +namespace iris +{ + struct FlashParams; + + class RedSaturation : public Flash + { + public: + + RedSaturation(short videoFPS, const cv::Size& frameSize, FlashParams* params); + ~RedSaturation(); + + void SetCurrentFrame(cv::Mat* sRgbFrame) override; + + private: + + struct CalculateRedSaturation + { + CalculateRedSaturation(cv::Mat* redSaturationMat) { redSat = redSaturationMat; }; + cv::Mat* redSat = nullptr; + + void operator()(cv::Vec3f& pixel, const int* position) const + { + //if R / (R + G + B) >= 0.8 = > pixel is saturated red + if (pixel[2] / (pixel[2] + pixel[1] + pixel[0]) >= 0.8f) + { + //if (R - G - B) * 320 > 20 => value to check for new transitions with frame diff + float red = (pixel[2] - pixel[1] - pixel[0]) * 320; + + //if negative set to 0 (default value is already 0) + if (red > 0) + { + redSat->ptr(position[0])[position[1]] = red; + } + + } //else pixel is not red saturated (default value is already 0) + } + }; + + /// + /// Calculates the red saturation in all the pixels of the frame + /// if R / (R + G + B) >= 0.8 => pixel is saturated red + /// + /// sRGB frame channel mats + //cv::Mat* RedSaturationValue(cv::Mat channels[]); + + /// + /// Calculates the red saturation coefficient (flash values) of the video frame in sRGB + /// + /// sRGB frame channel mats + //void RedSatCoefficient(cv::Mat* frame, cv::Mat channels[], cv::Mat* redMask); + + FlashParams* m_params = nullptr; //struct with config params + }; +} diff --git a/src/RelativeLuminance.cpp b/src/RelativeLuminance.cpp new file mode 100644 index 0000000..65c53d8 --- /dev/null +++ b/src/RelativeLuminance.cpp @@ -0,0 +1,51 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +/* +**** RelativeLuminance **** +Abstract class for Flash detection +*/ + +#include "RelativeLuminance.h" +#include +#include +#include "ConfigurationParams.h" +#include "IrisFrame.h" + +namespace iris +{ + cv::Scalar RelativeLuminance::rgbValues(0.0722f, 0.7152f, 0.2126f); + + RelativeLuminance::RelativeLuminance(short videoFPS, const cv::Size& frameSize, FlashParams* params) + : Flash(videoFPS, frameSize, params) + { + } + + RelativeLuminance::~RelativeLuminance() + { + ReleaseLastFrame(); + ReleaseCurrentFrame(); + } + + /// + /// Set the new current frame and move the previous one as the last frame. + /// in RelativeLuminance this method and class are responsible to release memory for the frame created + /// + /// + void RelativeLuminance::SetCurrentFrame(const IrisFrame& irisFrame) + { + cv::Mat* frame = new cv::Mat(irisFrame.sRgbFrame->size(), CV_32FC1); + irisFrame.sRgbFrame->forEach(ConvertToRelativeLuminance(frame)); + + ReleaseLastFrame(); + Flash::SetCurrentFrame(frame); + } + + void RelativeLuminance::SetCurrentFrame(cv::Mat* bgrFrame) + { + cv::Mat* frame = new cv::Mat(bgrFrame->size(), CV_32FC1); + bgrFrame->forEach(ConvertToRelativeLuminance(frame)); + + ReleaseLastFrame(); + Flash::SetCurrentFrame(frame); + } +} diff --git a/src/RelativeLuminance.h b/src/RelativeLuminance.h new file mode 100644 index 0000000..36bb878 --- /dev/null +++ b/src/RelativeLuminance.h @@ -0,0 +1,49 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include "Flash.h" +#include + +namespace cv +{ + class Mat; +} + +namespace iris +{ + struct FlashParams; + struct IrisFrame; + + class RelativeLuminance : public Flash + { + public: + /// + /// + /// + /// + /// + RelativeLuminance(short fps, const cv::Size& frameSize, FlashParams* params); + + + void SetCurrentFrame(const IrisFrame& irisFrame) override; + void SetCurrentFrame(cv::Mat* bgrFrame); + + ~RelativeLuminance(); + protected: + static cv::Scalar rgbValues; + + struct ConvertToRelativeLuminance + { + ConvertToRelativeLuminance(cv::Mat* luminanceMat) { luminance = luminanceMat; }; + cv::Mat* luminance = nullptr; + + void operator()(cv::Vec3f& pixel, const int* position) const + { + //Y = 0.0722 * B + 0.7152 * G + 0.2126 * R where B, G and R + luminance->ptr(position[0])[position[1]] = 0.0722f * pixel[0] + 0.7152f * pixel[1] + 0.2126f * pixel[2]; + } + }; + + private: + }; +} \ No newline at end of file diff --git a/src/TransitionEvaluator.cpp b/src/TransitionEvaluator.cpp new file mode 100644 index 0000000..78647a1 --- /dev/null +++ b/src/TransitionEvaluator.cpp @@ -0,0 +1,92 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include "TransitionEvaluator.h" +#include "ConfigurationParams.h" +#include "FrameData.h" + +namespace iris +{ + TransitionEvaluator::TransitionEvaluator(int fps, TransitionEvaluatorParams* params): + m_params(params) + { + m_luminanceTransitionCount.count.reserve(fps); m_luminanceTransitionCount.count.emplace_back(0); + m_redTransitionCount.count.reserve(fps); m_redTransitionCount.count.emplace_back(0); + m_luminanceExtendedCount.count.reserve(m_params->extendedFailWindow * fps); m_luminanceExtendedCount.count.emplace_back(0); + m_redExtendedCount.count.reserve(m_params->extendedFailWindow * fps); m_redExtendedCount.count.emplace_back(0); + } + + TransitionEvaluator::~TransitionEvaluator() + { + } + + void TransitionEvaluator::SetTransitions(bool lumTransition, bool redTransition, FrameData &data) + { + data.LuminanceTransitions = m_luminanceTransitionCount.updateCurrent(lumTransition); + data.RedTransitions = m_redTransitionCount.updateCurrent(redTransition); + + data.LuminanceExtendedFailCount = m_luminanceExtendedCount.updateCurrent(m_luminanceTransitionCount.current <= m_params->maxTransitions && m_luminanceTransitionCount.current >= m_params->minTransitions); + data.RedExtendedFailCount = m_redExtendedCount.updateCurrent(m_redTransitionCount.current <= m_params->maxTransitions && m_redTransitionCount.current >= m_params->minTransitions); + } + + /// + /// Checks if in the current frame (moment of the video) the video has + /// failed the flash criteria + /// + /// current frame index + /// video frame rate + /// data to persist + void TransitionEvaluator::EvaluateSecond(int framePos, int fps, FrameData &data) + { + if (m_luminanceTransitionCount.current > m_params->maxTransitions) //FAIL as max allowed transitions has been surpassed + { + m_luminanceResults.flashFail = true; + data.luminanceFrameResult = FlashResult::FlashFail; + m_luminanceIncidents.flashFailFrames += 1; + } + else if (m_luminanceExtendedCount.current >= m_params->extendedFailSeconds * fps && m_luminanceTransitionCount.current >= 4) //EXTENDED FAILURE + { + m_luminanceResults.extendedFail = true; + data.luminanceFrameResult = FlashResult::ExtendedFail; + m_luminanceIncidents.extendedFailFrames += 1; + } + else if (m_luminanceTransitionCount.current >= 4) + { + m_luminanceResults.passWithWarning = true; + data.luminanceFrameResult = FlashResult::PassWithWarning; + m_luminanceIncidents.passWithWarningFrames += 1; + } + + if (m_redTransitionCount.current > m_params->maxTransitions) //FAIL as max allowed transitions has been surpassed + { + m_redResults.flashFail = true; + data.redFrameResult = FlashResult::FlashFail; + m_redIncidents.flashFailFrames += 1; + } + else if (m_redExtendedCount.current >= m_params->extendedFailSeconds * fps && m_redTransitionCount.current >= 4) //EXTENDED FAILURE + { + m_redResults.extendedFail = true; + data.redFrameResult = FlashResult::ExtendedFail; + m_redIncidents.extendedFailFrames += 1; + } + else if (m_redTransitionCount.current >= 4) + { + m_redResults.passWithWarning = true; + data.redFrameResult = FlashResult::PassWithWarning; + m_redIncidents.passWithWarningFrames += 1; + } + + //update transition lists as 1s has passed + if (framePos >= fps - 1) + { + m_luminanceTransitionCount.updatePassed(); + m_redTransitionCount.updatePassed(); + + // update extended failure as 5s have passed + if(framePos >= m_params->extendedFailWindow * fps - 1) + { + m_luminanceExtendedCount.updatePassed(); + m_redExtendedCount.updatePassed(); + } + } + } +} diff --git a/src/TransitionEvaluator.h b/src/TransitionEvaluator.h new file mode 100644 index 0000000..c162309 --- /dev/null +++ b/src/TransitionEvaluator.h @@ -0,0 +1,111 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include +#include "iris/TotalFlashIncidents.h" + +namespace iris +{ + +struct TransitionEvaluatorParams; +class FrameData; + +class TransitionEvaluator +{ + +public: + TransitionEvaluator(int fps, TransitionEvaluatorParams* params); + ~TransitionEvaluator(); + + inline bool getLumPassWithWarning() { return m_luminanceResults.passWithWarning; }; + inline bool getRedPassWithWarning() { return m_redResults.passWithWarning; }; + + + inline bool getFlashFail() { return m_luminanceResults.flashFail || m_redResults.flashFail; }; + inline bool getLumFlashFail() { return m_luminanceResults.flashFail; }; + inline bool getRedFlashFail() { return m_redResults.flashFail; }; + + inline bool getExtendedFailure() { return m_luminanceResults.extendedFail || m_redResults.extendedFail; }; + inline bool getLumExtendedFailure() { return m_luminanceResults.extendedFail; }; + inline bool getRedExtendedFailure() { return m_redResults.extendedFail; }; + + const TotalFlashIncidents& getLuminanceIncidents() { return m_luminanceIncidents; }; + const TotalFlashIncidents& getRedIncidents() { return m_redIncidents; }; + + /// + /// Updates the transition count, extended fail count and the transitions in the last second + /// from the current frame + /// + /// true if there is a new luminance transition + /// true if there is a new red transition + /// data to persist + void SetTransitions(bool lumTransition, bool redTransition, FrameData& data); + + /// + /// Checks if in the current frame (moment of the video) the video has failed the flash criteria + /// + /// current frame index + /// video frame rate + /// data to persist + void EvaluateSecond(int framePos, int fps, FrameData& data); + +private: + + struct Counter + { + std::vector count; //transition count that surpass the luminance/red threshold from the last second + //or frames count where the transitions where between 4 and 6 + int current = 0; //current luminance/red transitions from this moment up to one second before + //or current frame count for extended failure + int passed = 0; //amount of luminance/red transitions that have passed the 1s window + //or amount of frames that have passed the 5s window + + // Get current frame's luminance or red transitions from the last second and update the transition count vector + // or get the current frame's luminance or red extended failure count + // return new current + int updateCurrent(const bool& newTransition) + { + //update the new transition count + if (newTransition) + { + count.emplace_back(count.back() + 1); + } + else + { + count.emplace_back(count.back()); + } + + current = count.back() - passed; //current transitions in second + return current; + } + + void updatePassed() + { + passed = count.front(); + count.erase(count.begin()); + } + }; + + struct FlashResults //possible flash results + { + bool passWithWarning = false; //flash pass with warning status + bool flashFail = false; //flash fail status + bool extendedFail = false; //extended flash failure status + }; + + Counter m_luminanceTransitionCount; + Counter m_redTransitionCount; + + Counter m_luminanceExtendedCount; + Counter m_redExtendedCount; + + FlashResults m_luminanceResults; + FlashResults m_redResults; + + TotalFlashIncidents m_luminanceIncidents; + TotalFlashIncidents m_redIncidents; + + TransitionEvaluatorParams* m_params = nullptr; +}; + +} \ No newline at end of file diff --git a/src/VideoAnalyser.cpp b/src/VideoAnalyser.cpp new file mode 100644 index 0000000..b39a200 --- /dev/null +++ b/src/VideoAnalyser.cpp @@ -0,0 +1,238 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include "iris/VideoAnalyser.h" +#include "iris/Configuration.h" +#include +#include +#include +#include +#include "IrisFrame.h" +#include "FlashDetection.h" +#include "PatternDetection.h" +#include "FrameRgbConverter.h" +#include "utils/FrameConverter.h" +#include "iris/Log.h" +#include +#include +#include "iris/Result.h" +#include +#include "utils/JsonWrapper.h" + +namespace iris +{ + VideoAnalyser::VideoAnalyser(Configuration* configuration) + { + LOG_CORE_WARNING("This output report is for informational purposes only and should not be used as certification or validation of compliance with any legal, regulatory or other requirements"); + + cv::utils::logging::setLogLevel(cv::utils::logging::LogLevel::LOG_LEVEL_ERROR); + m_configuration = configuration; +#ifdef _DEBUG + LOG_CORE_DEBUG(cv::getBuildInformation()); //OpenCV build info +#endif // DEBUG + } + + VideoAnalyser::~VideoAnalyser() + { + } + + void VideoAnalyser::Init(const short& fps, const cv::Size& size, const std::string& videoName, bool flagJson) + { + m_flashDetection = new FlashDetection(m_configuration, fps, size); + m_photosensitivityDetector.push_back(m_flashDetection); + m_frameSrgbConverter = new EA::EACC::Utils::FrameConverter(m_configuration->GetFrameSrgbConverterParams()); + m_patternDetection = new PatternDetection(m_configuration, fps, size); + + if (m_configuration->PatternDetectionEnabled()) + { + m_photosensitivityDetector.push_back(m_patternDetection); + } + + std::string frameDataFile = m_configuration->GetResultsPath() + videoName + "/framedata.csv"; + Log::SetDataLoggerFile(frameDataFile.c_str()); + + if (flagJson) + { + m_resultJsonPath = m_configuration->GetResultsPath() + videoName + "/" + "result" + ".json"; + m_frameDataJsonPath = m_configuration->GetResultsPath() + videoName + "/" + "frameData" + ".json"; + } + + LOG_CORE_INFO("Video analysis FPS: {}", fps); + + const char* luminanceType = m_configuration->GetLuminanceType() == Configuration::LuminanceType::CD ? "CD" : "RELATIVE"; + LOG_CORE_INFO("Luminance Type: {0}", luminanceType); + + LOG_CORE_INFO("Pattern Detection: {0}", m_configuration->PatternDetectionEnabled()); + + LOG_CORE_INFO("Safe Area Proportion: {0}", m_configuration->GetSafeAreaProportion()); + + LOG_CORE_INFO("Write json file: {0}", flagJson); + } + + void VideoAnalyser::DeInit() + { + if (m_flashDetection != nullptr) + { + delete m_flashDetection; m_flashDetection = nullptr; + } + if (m_patternDetection != nullptr) + { + delete m_patternDetection; m_patternDetection = nullptr; + } + if (m_frameSrgbConverter != nullptr) + { + delete m_frameSrgbConverter; m_frameSrgbConverter = nullptr; + } + + m_photosensitivityDetector.clear(); + } + + void VideoAnalyser::AnalyseVideo(bool flagJson, const char* sourceVideo) + { + cv::VideoCapture video(sourceVideo); + std::string videoName = std::filesystem::path(sourceVideo).filename().string(); + + if (VideoIsOpen(video, videoName.c_str())) + { + Init((short)round(video.get(cv::CAP_PROP_FPS)), cv::Size(video.get(cv::CAP_PROP_FRAME_WIDTH), video.get(cv::CAP_PROP_FRAME_HEIGHT)), videoName, flagJson); + cv::Mat frame; + video.read(frame); + + int numFrames = 0; + int lastPercentage = 0; + + LOG_DATA_INFO(FrameData().CsvColumns()); + LOG_CORE_INFO("Video analysis started"); + + auto start = std::chrono::steady_clock::now(); + + FrameDataJson lineGraphData; + FrameDataJson nonPassData; + + if (flagJson) + { + lineGraphData.reserveLineGraphData((int)video.get(cv::CAP_PROP_FRAME_COUNT)); + nonPassData.reserve((int)(video.get(cv::CAP_PROP_FRAME_COUNT) * 0.25)); //reserve at least one quarter of frames + } + + while (!frame.empty()) + { + FrameData data(numFrames + 1, 1000.0 * (double)numFrames / video.get(cv::CAP_PROP_FPS)); + AnalyseFrame(frame, numFrames, data); + + video.read(frame); //obtain new frame + + UpdateProgress(numFrames, video.get(cv::CAP_PROP_FRAME_COUNT), lastPercentage); + numFrames++; + + LOG_DATA_INFO(data.ToCSV()); + + if (flagJson) + { + lineGraphData.push_back_lineGraphData(data); + //save non-pass frame result frame data in json + if ((int)data.luminanceFrameResult > 0 || (int)data.redFrameResult > 0 || (int)data.patternFrameResult > 0) + { + nonPassData.push_back(data); + } + } + } + + auto end = std::chrono::steady_clock::now(); + LOG_CORE_INFO("Video analysis ended"); + int elapsedTime = std::chrono::duration_cast(end - start).count(); + LOG_CORE_INFO("Elapsed time: {0} ms", elapsedTime); + + if (m_flashDetection->isFail() || m_patternDetection->isFail()) + { + LOG_CORE_INFO("Video Result: FAIL"); + } + else + { + LOG_CORE_INFO("Video Result: PASS"); + } + + if (flagJson) + { + Result result; + m_flashDetection->setResult(result); + if (m_patternDetection != nullptr) { m_patternDetection->setResult(result); } + result.VideoLen = (int)(video.get(cv::CAP_PROP_FRAME_COUNT) / video.get(cv::CAP_PROP_FPS) * 1000); + result.AnalysisTime = elapsedTime; + result.TotalFrame = (int)video.get(cv::CAP_PROP_FRAME_COUNT); + SerializeResults(result, lineGraphData, nonPassData); + } + + DeInit(); + } + + video.release(); + } + + void VideoAnalyser::AnalyseFrame(cv::Mat& frame, int& frameIndex, FrameData& data) + { + IrisFrame irisFrame(&(frame), m_frameSrgbConverter->Convert(frame), data); + + m_flashDetection->setLuminance(irisFrame); + for (auto detector : m_photosensitivityDetector) + { + detector->checkFrame(irisFrame, frameIndex, data); + } + + irisFrame.Release(); + } + + bool VideoAnalyser::VideoIsOpen(cv::VideoCapture& video, const char* videoName) + { + if (video.isOpened()) + { + LOG_CORE_INFO("Video: {0} opened successful", videoName); + LOG_CORE_INFO("Total frames: {0}", video.get(cv::CAP_PROP_FRAME_COUNT)); + LOG_CORE_INFO("FPS: {0}", video.get(cv::CAP_PROP_FPS)); + LOG_CORE_INFO("Duration: {0}s", video.get(cv::CAP_PROP_FRAME_COUNT) / video.get(cv::CAP_PROP_FPS)); + LOG_CORE_INFO("Video resolution: {0}x{1}", video.get(cv::CAP_PROP_FRAME_WIDTH), video.get(cv::CAP_PROP_FRAME_HEIGHT)); + return true; + } + + LOG_CORE_ERROR("Video: {0} could not be opened\nInformation is missing or corrupt", videoName); + throw std::runtime_error("Video: "+ std::string(videoName) +"could not be opened\nInformation is missing or corrupt"); + return false; + } + + void VideoAnalyser::UpdateProgress(int& numFrames, const long& totalFrames, int& lastPercentage) + { + int progress = numFrames / (float)totalFrames * 100.0f; + + if (progress != 0 && progress % 10 == 0 && lastPercentage != progress) //display progress + { + lastPercentage = progress; + LOG_CORE_DEBUG("Analysed {0}%", progress); + } + } + + void VideoAnalyser::SerializeResults(const Result& result, const FrameDataJson& lineGraphData, const FrameDataJson& nonPassData) + { + EA::EACC::Utils::JsonWrapper resultJson; + + resultJson.SetParam("FrameDataResult", "FrameCsvRepositoryKey", "00000000-0000-0000-0000-000000000000"); //Value initialized in Lambda + resultJson.SetParam("FrameDataResult", "FrameResultJsonRepositoryKey", "00000000-0000-0000-0000-000000000000"); + resultJson.SetParam("FrameDataResult", "FrameDataJsonRepositoryKey", "00000000-0000-0000-0000-000000000000"); + resultJson.SetParam("TotalFrame", result.TotalFrame); + resultJson.SetParam("AnalyzeTimeString", msToTimeSpan(result.AnalysisTime)); + resultJson.SetParam("VideoLenString", msToTimeSpan(result.VideoLen)); + resultJson.SetParam("OverallResult", result.OverallResult); + resultJson.SetParam("Results", result.Results); + resultJson.SetParam("TotalLuminanceIncidents", result.totalLuminanceIncidents); + resultJson.SetParam("TotalRedIncidents", result.totalRedIncidents); + resultJson.SetParam("PatternFailFrames", result.patternFailFrames); + resultJson.WriteFile(m_resultJsonPath.c_str()); + LOG_CORE_INFO("Results Json written to {}", m_resultJsonPath); + + + EA::EACC::Utils::JsonWrapper frameDataJson; + frameDataJson.SetParam("NonPassFrameData", nonPassData); + frameDataJson.SetParam("LineGraphFrameData", lineGraphData); + + frameDataJson.WriteFile(m_frameDataJsonPath.c_str()); + LOG_CORE_INFO("Non Pass Json written to {}", m_frameDataJsonPath); + } +} \ No newline at end of file diff --git a/test/AddressSanitizer.Tests/CMakeLists.txt b/test/AddressSanitizer.Tests/CMakeLists.txt new file mode 100644 index 0000000..f3496ba --- /dev/null +++ b/test/AddressSanitizer.Tests/CMakeLists.txt @@ -0,0 +1,20 @@ +set(TEST_PROJECT AddressSanitizer.Tests) + +find_package(GTest CONFIG REQUIRED) + +add_executable(${TEST_PROJECT} "include/AddressSanitizerTest.h" "src/AddressSanitizerTest.cpp") + +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=address") +set (CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=address") + +target_link_libraries(${TEST_PROJECT} PUBLIC GTest::gtest_main PRIVATE -fsanitize=address) + +# add the binary tree to the search path for include files +# so that we will find library headers +target_include_directories(${TEST_PROJECT} PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/include" + ) + + +include(GoogleTest) +gtest_discover_tests(${TEST_PROJECT}) diff --git a/test/AddressSanitizer.Tests/include/AddressSanitizerTest.h b/test/AddressSanitizer.Tests/include/AddressSanitizerTest.h new file mode 100644 index 0000000..646c926 --- /dev/null +++ b/test/AddressSanitizer.Tests/include/AddressSanitizerTest.h @@ -0,0 +1,7 @@ +#include + + +namespace AddressSanitizer::Tests +{ + +} \ No newline at end of file diff --git a/test/AddressSanitizer.Tests/src/AddressSanitizerTest.cpp b/test/AddressSanitizer.Tests/src/AddressSanitizerTest.cpp new file mode 100644 index 0000000..de1b7ed --- /dev/null +++ b/test/AddressSanitizer.Tests/src/AddressSanitizerTest.cpp @@ -0,0 +1,14 @@ +#include +#include "AddressSanitizerTest.h" + +namespace AddressSanitizer::Tests +{ + TEST(AddressSanitizerTest, MemoryLeak_WhenFailed) + { + + int* ptr = new int[100]; + //delete[] ptr; + + EXPECT_EQ(1.0f, 1.0f); + } +} \ No newline at end of file diff --git a/test/Iris.Tests/CMakeLists.txt b/test/Iris.Tests/CMakeLists.txt new file mode 100644 index 0000000..db05d15 --- /dev/null +++ b/test/Iris.Tests/CMakeLists.txt @@ -0,0 +1,85 @@ +# Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +project(iris_tests) + +set(HEADER_FILES + "include/IrisLibTest.h" +) +source_group("Header Files" FILES ${HEADER_FILES}) + +set(SOURCE_FILES + "src/FrameRgbConverterTest.cpp" + "src/FlashTest.cpp" + "src/RedSaturationTests.cpp" + "src/RelativeLuminanceTest.cpp" + "src/CDLuminanceTest.cpp" + "src/TransitionEvaluatorTest.cpp" + "src/FlashDetectionTests.cpp" + "src/PatternDetectionTests.cpp" + "src/VideoAnalysisTests.cpp" +) + +source_group("Source Files" FILES ${SOURCE_FILES}) + +find_package(GTest CONFIG REQUIRED) + +add_executable(${PROJECT_NAME} ${HEADER_FILES} ${SOURCE_FILES}) + +target_link_libraries(${PROJECT_NAME} PUBLIC iris GTest::gtest_main) + +option(ASAN_ENABLED "Build this target with AddressSanitizer" ON) + + + +if(ASAN_ENABLED) + if(NOT MSVC) + target_compile_options(${PROJECT_NAME} PUBLIC -fsanitize=address -fno-omit-frame-pointer) + target_link_options(${PROJECT_NAME} PUBLIC -fsanitize=address) + endif() +endif() + + +# add the binary tree to the search path for include files +# so that we will find library headers +target_include_directories(${PROJECT_NAME} PRIVATE + "${CMAKE_SOURCE_DIR}/include" + "${CMAKE_SOURCE_DIR}/src" + "${PROJECT_SOURCE_DIR}/include" + ) + + +# Copies all appsettings files into the executable directory +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/appsettings.json ${CMAKE_CURRENT_BINARY_DIR}/appsettings.json COPYONLY) + + +# Copies all frame files into the executable directory +add_custom_target(copy_data ALL + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/data + ${CMAKE_CURRENT_BINARY_DIR}/data + COMMENT "Copying test data into current binary directory") + +add_dependencies(${PROJECT_NAME} copy_data) + +if(BUILD_SHARED_LIBS) + message("Copy dynamic libraries into iris tests directory") + file(GLOB_RECURSE DLL_FILES ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/*.dll) + file(COPY ${DLL_FILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +endif() + +if(BUILD_COVERAGE) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/appsettings.json ${CMAKE_BINARY_DIR}/appsettings.json COPYONLY) + add_custom_target(copy_data_bin_dir ALL + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/data + ${CMAKE_BINARY_DIR}/data + COMMENT "Copying test data into binary directory") + add_dependencies(${PROJECT_NAME} copy_data_bin_dir) + + include(Coverage) + AddCoverage(iris_tests) +endif() + +include(GoogleTest) +gtest_discover_tests(${PROJECT_NAME}) +#gtest_discover_tests(${PROJECT_NAME} PROPERTIES DISCOVERY_TIMEOUT 5000) diff --git a/test/Iris.Tests/appsettings.json b/test/Iris.Tests/appsettings.json new file mode 100644 index 0000000..2f37dd3 --- /dev/null +++ b/test/Iris.Tests/appsettings.json @@ -0,0 +1,556 @@ +{ + "Luminance": { + "FormulaYval1": 413.435, //CD luminance formula values + "FormulaYval2": 0.002745, + "FormulaYval3": 0.0189623, + "FormulaYvalpow": 2.2, + "RelativeLuminanceFlashThreshold": 0.1, //flash transition + "RelativeDarkLuminanceThreshold": 0.8, + "CdLuminanceFlashThreshold": 20, //flash transition + "CdDarkLuminanceThreshold": 160, + //possible CD luminanve values for look up table + "CdLuminanceValues": [ + 0.07, + 0.09, + 0.12, + 0.15, + 0.18, + 0.22, + 0.27, + 0.31, + 0.37, + 0.42, + 0.48, + 0.55, + 0.62, + 0.69, + 0.77, + 0.85, + 0.94, + 1.03, + 1.13, + 1.23, + 1.34, + 1.45, + 1.57, + 1.69, + 1.82, + 1.95, + 2.09, + 2.23, + 2.37, + 2.53, + 2.68, + 2.85, + 3.01, + 3.19, + 3.37, + 3.55, + 3.74, + 3.93, + 4.13, + 4.34, + 4.55, + 4.77, + 4.99, + 5.21, + 5.45, + 5.68, + 5.93, + 6.18, + 6.43, + 6.69, + 6.96, + 7.23, + 7.51, + 7.79, + 8.08, + 8.38, + 8.68, + 8.98, + 9.3, + 9.61, + 9.94, + 10.27, + 10.6, + 10.94, + 11.29, + 11.64, + 12, + 12.37, + 12.74, + 13.12, + 13.5, + 13.89, + 14.28, + 14.69, + 15.09, + 15.51, + 15.93, + 16.35, + 16.78, + 17.22, + 17.67, + 18.12, + 18.57, + 19.04, + 19.5, + 19.98, + 20.46, + 20.95, + 21.44, + 21.94, + 22.45, + 22.96, + 23.48, + 24.01, + 24.54, + 25.08, + 25.62, + 26.17, + 26.73, + 27.29, + 27.86, + 28.44, + 29.02, + 29.61, + 30.21, + 30.81, + 31.42, + 32.03, + 32.66, + 33.29, + 33.92, + 34.56, + 35.21, + 35.86, + 36.53, + 37.19, + 37.87, + 38.55, + 39.24, + 39.93, + 40.63, + 41.34, + 42.05, + 42.78, + 43.5, + 44.24, + 44.98, + 45.73, + 46.48, + 47.24, + 48.01, + 48.79, + 49.57, + 50.36, + 51.15, + 51.95, + 52.76, + 53.58, + 54.4, + 55.23, + 56.07, + 56.91, + 57.76, + 58.62, + 59.48, + 60.35, + 61.23, + 62.11, + 63, + 63.9, + 64.81, + 65.72, + 66.64, + 67.56, + 68.5, + 69.44, + 70.38, + 71.34, + 72.3, + 73.27, + 74.24, + 75.22, + 76.21, + 77.21, + 78.21, + 79.22, + 80.24, + 81.26, + 82.3, + 83.34, + 84.38, + 85.43, + 86.49, + 87.56, + 88.64, + 89.72, + 90.81, + 91.9, + 93, + 94.11, + 95.23, + 96.36, + 97.49, + 98.63, + 99.77, + 100.93, + 102.09, + 103.25, + 104.43, + 105.61, + 106.8, + 108, + 109.2, + 110.41, + 111.63, + 112.86, + 114.09, + 115.33, + 116.58, + 117.84, + 119.1, + 120.37, + 121.65, + 122.93, + 124.22, + 125.52, + 126.83, + 128.14, + 129.47, + 130.8, + 132.13, + 133.48, + 134.83, + 136.19, + 137.55, + 138.93, + 140.31, + 141.69, + 143.09, + 144.49, + 145.9, + 147.32, + 148.75, + 150.18, + 151.62, + 153.07, + 154.53, + 155.99, + 157.46, + 158.94, + 160.43, + 161.92, + 163.42, + 164.93, + 166.45, + 167.97, + 169.5, + 171.04, + 172.59, + 174.14, + 175.7, + 177.27, + 178.85, + 180.43, + 182.03, + 183.63, + 185.23, + 186.85, + 188.47, + 190.1, + 191.74, + 193.38, + 195.04, + 196.7, + 198.37, + 200 + ] + }, + "RedSaturation": { + "FlashThreshold": 20, //threshold for red saturation transitions + "RedDarkThreshold": 321 + }, + "FlashDetection": { + "AreaProportion": 0.25, //screen display flashing area + //sRGB possible values for look up table + "sRGBValues": [ + 0, + 0.000303527, + 0.000607054, + 0.000910581, + 0.001214108, + 0.001517635, + 0.001821162, + 0.0021246888, + 0.002428216, + 0.002731743, + 0.00303527, + 0.0033465363, + 0.0036765079, + 0.004024718, + 0.004391443, + 0.004776954, + 0.005181518, + 0.0056053926, + 0.006048834, + 0.0065120924, + 0.0069954116, + 0.007499033, + 0.008023194, + 0.008568126, + 0.009134059, + 0.00972122, + 0.010329825, + 0.010960096, + 0.011612247, + 0.012286489, + 0.0129830325, + 0.013702083, + 0.014443846, + 0.015208517, + 0.015996296, + 0.016807377, + 0.017641956, + 0.01850022, + 0.019382365, + 0.020288566, + 0.021219013, + 0.022173887, + 0.023153368, + 0.024157634, + 0.025186861, + 0.026241226, + 0.027320895, + 0.028426042, + 0.029556837, + 0.030713446, + 0.031896036, + 0.033104766, + 0.03433981, + 0.035601318, + 0.03688945, + 0.03820437, + 0.039546244, + 0.040915202, + 0.042311415, + 0.043735035, + 0.04518621, + 0.04666509, + 0.048171826, + 0.049706567, + 0.05126947, + 0.05286066, + 0.05448029, + 0.056128502, + 0.05780544, + 0.059511248, + 0.06124608, + 0.06301004, + 0.06480329, + 0.06662596, + 0.06847819, + 0.07036012, + 0.07227187, + 0.07421359, + 0.0761854, + 0.078187436, + 0.080219835, + 0.08228272, + 0.08437622, + 0.08650047, + 0.08865561, + 0.09084174, + 0.09305899, + 0.09530749, + 0.09758737, + 0.09989875, + 0.102241755, + 0.10461651, + 0.10702312, + 0.10946173, + 0.11193245, + 0.11443539, + 0.11697068, + 0.11953844, + 0.122138806, + 0.12477185, + 0.12743771, + 0.1301365, + 0.13286835, + 0.13563335, + 0.13843164, + 0.14126332, + 0.14412849, + 0.14702728, + 0.1499598, + 0.15292618, + 0.15592648, + 0.15896088, + 0.16202942, + 0.16513222, + 0.16826941, + 0.17144111, + 0.1746474, + 0.17788842, + 0.18116425, + 0.18447499, + 0.18782078, + 0.19120169, + 0.19461782, + 0.19806932, + 0.20155625, + 0.20507872, + 0.20863685, + 0.21223074, + 0.21586055, + 0.21952623, + 0.223228, + 0.2269659, + 0.23074009, + 0.23455067, + 0.23839766, + 0.24228121, + 0.24620141, + 0.25015837, + 0.25415218, + 0.25818294, + 0.26225075, + 0.2663557, + 0.27049786, + 0.2746774, + 0.27889434, + 0.28314883, + 0.2874409, + 0.29177073, + 0.29613835, + 0.30054384, + 0.30498737, + 0.30946898, + 0.31398878, + 0.31854683, + 0.32314327, + 0.32777816, + 0.33245158, + 0.33716366, + 0.34191447, + 0.3467041, + 0.35153273, + 0.35640025, + 0.3613069, + 0.36625272, + 0.37123778, + 0.37626222, + 0.3813261, + 0.38642955, + 0.39157256, + 0.39675534, + 0.40197787, + 0.4072403, + 0.4125427, + 0.41788515, + 0.42326775, + 0.42869058, + 0.4341537, + 0.43965724, + 0.44520128, + 0.45078585, + 0.4564111, + 0.46207705, + 0.46778387, + 0.47353154, + 0.47932023, + 0.48515, + 0.4910209, + 0.49693304, + 0.5028866, + 0.50888145, + 0.5149178, + 0.5209957, + 0.5271153, + 0.53327656, + 0.5394796, + 0.5457246, + 0.55201155, + 0.5583405, + 0.56471163, + 0.5711249, + 0.5775806, + 0.58407855, + 0.59061897, + 0.5972019, + 0.6038274, + 0.6104957, + 0.61720663, + 0.6239605, + 0.6307572, + 0.63759696, + 0.64447975, + 0.6514057, + 0.6583749, + 0.66538733, + 0.6724432, + 0.67954254, + 0.6866855, + 0.6938719, + 0.7011021, + 0.70837593, + 0.71569365, + 0.7230553, + 0.7304609, + 0.73791057, + 0.74540436, + 0.7529423, + 0.76052463, + 0.7681513, + 0.77582234, + 0.7835379, + 0.79129803, + 0.79910284, + 0.80695236, + 0.8148467, + 0.82278585, + 0.83076996, + 0.8387991, + 0.84687334, + 0.8549927, + 0.8631573, + 0.8713672, + 0.87962234, + 0.8879232, + 0.89626944, + 0.90466136, + 0.9130987, + 0.92158204, + 0.9301109, + 0.9386859, + 0.9473066, + 0.9559735, + 0.9646863, + 0.9734455, + 0.9822506, + 0.9911022, + 1 + ] + }, + "TransitionEvaluator": { + "MaxTransitions": 6, + "MinTransitions": 4, + "ExtendedFailSeconds": 4, + "ExtendedFailWindow": 5 + }, + "PatternDetection": { + "MinStripes": 6, //min stripes for harmful patterns + "TimeThreshold": 0.5, //max seconds for harmful patterns until failure + "CDDarkLuminanceThreshold": 160, + "RelativeDarkLuminanceThreshold": 0.8, + "AreaProportion": 0.25 + }, + + "VideoAnalyser": { + "ScreenShotsPath": "ScreenShots", + "LuminanceType": "RELATIVE", //CD || RELATIVE + "PatternDetectionEnabled": false + } +} \ No newline at end of file diff --git a/test/Iris.Tests/data/ExpectedVideoLogFiles/2Hz_5s_RELATIVE.csv b/test/Iris.Tests/data/ExpectedVideoLogFiles/2Hz_5s_RELATIVE.csv new file mode 100644 index 0000000..c21cb70 --- /dev/null +++ b/test/Iris.Tests/data/ExpectedVideoLogFiles/2Hz_5s_RELATIVE.csv @@ -0,0 +1,26 @@ +Frame,TimeStamp,AverageLuminance,FlashAreaLuminance,AverageLuminanceDiff,AverageLuminanceDiffAcc,AverageRed,FlashAreaRed,AverageRedDiff,AverageRedDiffAcc,PatternRisk,LuminanceTransitions,RedTransitions,LuminanceExtendedFailCount,RedExtendedFailCount,FlashLuminanceFailedFrame,FlashRedFailedFrame,PatternFailedFrame +1,00:00.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +2,00:00.2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +3,00:00.4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +4,00:00.6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +5,00:00.8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +6,00:01.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +7,00:01.2,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0 +8,00:01.4,0,1,-1,-1,0,0,0,0,0,2,0,0,0,0,0,0 +9,00:01.6,1,1,1,1,0,0,0,0,0,3,0,0,0,0,0,0 +10,00:01.8,0,1,-1,-1,0,0,0,0,0,4,0,1,0,1,0,0 +11,00:02.0,1,1,1,1,0,0,0,0,0,5,0,2,0,1,0,0 +12,00:02.2,0,1,-1,-1,0,0,0,0,0,5,0,3,0,1,0,0 +13,00:02.4,1,1,1,1,0,0,0,0,0,5,0,4,0,1,0,0 +14,00:02.6,0,1,-1,-1,0,0,0,0,0,5,0,5,0,1,0,0 +15,00:02.8,0,0,0,-1,0,0,0,0,0,4,0,6,0,1,0,0 +16,00:03.0,1,1,1,1,0,0,0,0,0,4,0,7,0,1,0,0 +17,00:03.2,0,1,-1,-1,0,0,0,0,0,4,0,8,0,1,0,0 +18,00:03.4,1,1,1,1,0,0,0,0,0,4,0,9,0,1,0,0 +19,00:03.6,0,1,-1,-1,0,0,0,0,0,4,0,10,0,1,0,0 +20,00:03.8,0,0,0,-1,0,0,0,0,0,4,0,11,0,1,0,0 +21,00:04.0,1,1,1,1,0,0,0,0,0,4,0,12,0,1,0,0 +22,00:04.2,0,1,-1,-1,0,0,0,0,0,4,0,13,0,1,0,0 +23,00:04.4,1,1,1,1,0,0,0,0,0,4,0,14,0,1,0,0 +24,00:04.6,0,1,-1,-1,0,0,0,0,0,4,0,15,0,1,0,0 +25,00:04.8,0,0,0,-1,0,0,0,0,0,4,0,16,0,1,0,0 diff --git a/test/Iris.Tests/data/ExpectedVideoLogFiles/2Hz_6s_RELATIVE.csv b/test/Iris.Tests/data/ExpectedVideoLogFiles/2Hz_6s_RELATIVE.csv new file mode 100644 index 0000000..31f09e3 --- /dev/null +++ b/test/Iris.Tests/data/ExpectedVideoLogFiles/2Hz_6s_RELATIVE.csv @@ -0,0 +1,150 @@ +Frame,TimeStamp,AverageLuminance,FlashAreaLuminance,AverageLuminanceDiff,AverageLuminanceDiffAcc,AverageRed,FlashAreaRed,AverageRedDiff,AverageRedDiffAcc,PatternRisk,LuminanceTransitions,RedTransitions,LuminanceExtendedFailCount,RedExtendedFailCount,FlashLuminanceFailedFrame,FlashRedFailedFrame,PatternFailedFrame +1,00:00.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +2,00:00.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +3,00:00.1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +4,00:00.1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +5,00:00.2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +6,00:00.2,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0 +7,00:00.2,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0 +8,00:00.3,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0 +9,00:00.3,0,1,-1,-1,0,0,0,0,0,2,0,0,0,0,0,0 +10,00:00.4,0,0,0,-1,0,0,0,0,0,2,0,0,0,0,0,0 +11,00:00.4,0,0,0,-1,0,0,0,0,0,2,0,0,0,0,0,0 +12,00:00.4,1,1,1,1,0,0,0,0,0,3,0,0,0,0,0,0 +13,00:00.5,1,0,0,1,0,0,0,0,0,3,0,0,0,0,0,0 +14,00:00.5,1,0,0,1,0,0,0,0,0,3,0,0,0,0,0,0 +15,00:00.6,0,1,-1,-1,0,0,0,0,0,4,0,1,0,1,0,0 +16,00:00.6,0,0,0,-1,0,0,0,0,0,4,0,2,0,1,0,0 +17,00:00.6,0,0,0,-1,0,0,0,0,0,4,0,3,0,1,0,0 +18,00:00.7,0,0,0,-1,0,0,0,0,0,4,0,4,0,1,0,0 +19,00:00.7,0,0,0,-1,0,0,0,0,0,4,0,5,0,1,0,0 +20,00:00.8,0,0,0,-1,0,0,0,0,0,4,0,6,0,1,0,0 +21,00:00.8,0,0,0,-1,0,0,0,0,0,4,0,7,0,1,0,0 +22,00:00.8,0,0,0,-1,0,0,0,0,0,4,0,8,0,1,0,0 +23,00:00.9,0,0,0,-1,0,0,0,0,0,4,0,9,0,1,0,0 +24,00:00.9,0,0,0,-1,0,0,0,0,0,4,0,10,0,1,0,0 +25,00:01.0,0,0,0,-1,0,0,0,0,0,4,0,11,0,1,0,0 +26,00:01.0,0,0,0,-1,0,0,0,0,0,4,0,12,0,1,0,0 +27,00:01.0,0,0,0,-1,0,0,0,0,0,4,0,13,0,1,0,0 +28,00:01.1,0,0,0,-1,0,0,0,0,0,4,0,14,0,1,0,0 +29,00:01.1,0,0,0,-1,0,0,0,0,0,4,0,15,0,1,0,0 +30,00:01.2,1,1,1,1,0,0,0,0,0,5,0,16,0,1,0,0 +31,00:01.2,1,0,0,1,0,0,0,0,0,4,0,17,0,1,0,0 +32,00:01.2,1,0,0,1,0,0,0,0,0,4,0,18,0,1,0,0 +33,00:01.3,0,1,-1,-1,0,0,0,0,0,5,0,19,0,1,0,0 +34,00:01.3,0,0,0,-1,0,0,0,0,0,4,0,20,0,1,0,0 +35,00:01.4,0,0,0,-1,0,0,0,0,0,4,0,21,0,1,0,0 +36,00:01.4,1,1,1,1,0,0,0,0,0,5,0,22,0,1,0,0 +37,00:01.4,1,0,0,1,0,0,0,0,0,4,0,23,0,1,0,0 +38,00:01.5,1,0,0,1,0,0,0,0,0,4,0,24,0,1,0,0 +39,00:01.5,0,1,-1,-1,0,0,0,0,0,5,0,25,0,1,0,0 +40,00:01.6,0,0,0,-1,0,0,0,0,0,4,0,26,0,1,0,0 +41,00:01.6,0,0,0,-1,0,0,0,0,0,4,0,27,0,1,0,0 +42,00:01.6,0,0,0,-1,0,0,0,0,0,4,0,28,0,1,0,0 +43,00:01.7,0,0,0,-1,0,0,0,0,0,4,0,29,0,1,0,0 +44,00:01.7,0,0,0,-1,0,0,0,0,0,4,0,30,0,1,0,0 +45,00:01.8,0,0,0,-1,0,0,0,0,0,4,0,31,0,1,0,0 +46,00:01.8,0,0,0,-1,0,0,0,0,0,4,0,32,0,1,0,0 +47,00:01.8,0,0,0,-1,0,0,0,0,0,4,0,33,0,1,0,0 +48,00:01.9,0,0,0,-1,0,0,0,0,0,4,0,34,0,1,0,0 +49,00:01.9,0,0,0,-1,0,0,0,0,0,4,0,35,0,1,0,0 +50,00:02.0,0,0,0,-1,0,0,0,0,0,4,0,36,0,1,0,0 +51,00:02.0,0,0,0,-1,0,0,0,0,0,4,0,37,0,1,0,0 +52,00:02.0,0,0,0,-1,0,0,0,0,0,4,0,38,0,1,0,0 +53,00:02.1,0,0,0,-1,0,0,0,0,0,4,0,39,0,1,0,0 +54,00:02.1,0,0,0,-1,0,0,0,0,0,4,0,40,0,1,0,0 +55,00:02.2,1,1,1,1,0,0,0,0,0,4,0,41,0,1,0,0 +56,00:02.2,1,0,0,1,0,0,0,0,0,4,0,42,0,1,0,0 +57,00:02.2,1,0,0,1,0,0,0,0,0,4,0,43,0,1,0,0 +58,00:02.3,0,1,-1,-1,0,0,0,0,0,4,0,44,0,1,0,0 +59,00:02.3,0,0,0,-1,0,0,0,0,0,4,0,45,0,1,0,0 +60,00:02.4,0,0,0,-1,0,0,0,0,0,4,0,46,0,1,0,0 +61,00:02.4,1,1,1,1,0,0,0,0,0,4,0,47,0,1,0,0 +62,00:02.4,1,0,0,1,0,0,0,0,0,4,0,48,0,1,0,0 +63,00:02.5,1,0,0,1,0,0,0,0,0,4,0,49,0,1,0,0 +64,00:02.5,0,1,-1,-1,0,0,0,0,0,4,0,50,0,1,0,0 +65,00:02.6,0,0,0,-1,0,0,0,0,0,4,0,51,0,1,0,0 +66,00:02.6,0,0,0,-1,0,0,0,0,0,4,0,52,0,1,0,0 +67,00:02.6,0,0,0,-1,0,0,0,0,0,4,0,53,0,1,0,0 +68,00:02.7,0,0,0,-1,0,0,0,0,0,4,0,54,0,1,0,0 +69,00:02.7,0,0,0,-1,0,0,0,0,0,4,0,55,0,1,0,0 +70,00:02.8,0,0,0,-1,0,0,0,0,0,4,0,56,0,1,0,0 +71,00:02.8,0,0,0,-1,0,0,0,0,0,4,0,57,0,1,0,0 +72,00:02.8,0,0,0,-1,0,0,0,0,0,4,0,58,0,1,0,0 +73,00:02.9,0,0,0,-1,0,0,0,0,0,4,0,59,0,1,0,0 +74,00:02.9,0,0,0,-1,0,0,0,0,0,4,0,60,0,1,0,0 +75,00:03.0,0,0,0,-1,0,0,0,0,0,4,0,61,0,1,0,0 +76,00:03.0,0,0,0,-1,0,0,0,0,0,4,0,62,0,1,0,0 +77,00:03.0,0,0,0,-1,0,0,0,0,0,4,0,63,0,1,0,0 +78,00:03.1,0,0,0,-1,0,0,0,0,0,4,0,64,0,1,0,0 +79,00:03.1,0,0,0,-1,0,0,0,0,0,4,0,65,0,1,0,0 +80,00:03.2,1,1,1,1,0,0,0,0,0,4,0,66,0,1,0,0 +81,00:03.2,1,0,0,1,0,0,0,0,0,4,0,67,0,1,0,0 +82,00:03.2,1,0,0,1,0,0,0,0,0,4,0,68,0,1,0,0 +83,00:03.3,0,1,-1,-1,0,0,0,0,0,4,0,69,0,1,0,0 +84,00:03.3,0,0,0,-1,0,0,0,0,0,4,0,70,0,1,0,0 +85,00:03.4,0,0,0,-1,0,0,0,0,0,4,0,71,0,1,0,0 +86,00:03.4,1,1,1,1,0,0,0,0,0,4,0,72,0,1,0,0 +87,00:03.4,1,0,0,1,0,0,0,0,0,4,0,73,0,1,0,0 +88,00:03.5,1,0,0,1,0,0,0,0,0,4,0,74,0,1,0,0 +89,00:03.5,0,1,-1,-1,0,0,0,0,0,4,0,75,0,1,0,0 +90,00:03.6,0,0,0,-1,0,0,0,0,0,4,0,76,0,1,0,0 +91,00:03.6,0,0,0,-1,0,0,0,0,0,4,0,77,0,1,0,0 +92,00:03.6,0,0,0,-1,0,0,0,0,0,4,0,78,0,1,0,0 +93,00:03.7,0,0,0,-1,0,0,0,0,0,4,0,79,0,1,0,0 +94,00:03.7,0,0,0,-1,0,0,0,0,0,4,0,80,0,1,0,0 +95,00:03.8,0,0,0,-1,0,0,0,0,0,4,0,81,0,1,0,0 +96,00:03.8,0,0,0,-1,0,0,0,0,0,4,0,82,0,1,0,0 +97,00:03.8,0,0,0,-1,0,0,0,0,0,4,0,83,0,1,0,0 +98,00:03.9,0,0,0,-1,0,0,0,0,0,4,0,84,0,1,0,0 +99,00:03.9,0,0,0,-1,0,0,0,0,0,4,0,85,0,1,0,0 +100,00:04.0,0,0,0,-1,0,0,0,0,0,4,0,86,0,1,0,0 +101,00:04.0,0,0,0,-1,0,0,0,0,0,4,0,87,0,1,0,0 +102,00:04.0,0,0,0,-1,0,0,0,0,0,4,0,88,0,1,0,0 +103,00:04.1,0,0,0,-1,0,0,0,0,0,4,0,89,0,1,0,0 +104,00:04.1,0,0,0,-1,0,0,0,0,0,4,0,90,0,1,0,0 +105,00:04.2,1,1,1,1,0,0,0,0,0,4,0,91,0,1,0,0 +106,00:04.2,1,0,0,1,0,0,0,0,0,4,0,92,0,1,0,0 +107,00:04.2,1,0,0,1,0,0,0,0,0,4,0,93,0,1,0,0 +108,00:04.3,0,1,-1,-1,0,0,0,0,0,4,0,94,0,1,0,0 +109,00:04.3,0,0,0,-1,0,0,0,0,0,4,0,95,0,1,0,0 +110,00:04.4,0,0,0,-1,0,0,0,0,0,4,0,96,0,1,0,0 +111,00:04.4,1,1,1,1,0,0,0,0,0,4,0,97,0,1,0,0 +112,00:04.4,1,0,0,1,0,0,0,0,0,4,0,98,0,1,0,0 +113,00:04.5,1,0,0,1,0,0,0,0,0,4,0,99,0,1,0,0 +114,00:04.5,0,1,-1,-1,0,0,0,0,0,4,0,100,0,2,0,0 +115,00:04.6,0,0,0,-1,0,0,0,0,0,4,0,101,0,2,0,0 +116,00:04.6,0,0,0,-1,0,0,0,0,0,4,0,102,0,2,0,0 +117,00:04.6,0,0,0,-1,0,0,0,0,0,4,0,103,0,2,0,0 +118,00:04.7,0,0,0,-1,0,0,0,0,0,4,0,104,0,2,0,0 +119,00:04.7,0,0,0,-1,0,0,0,0,0,4,0,105,0,2,0,0 +120,00:04.8,0,0,0,-1,0,0,0,0,0,4,0,106,0,2,0,0 +121,00:04.8,0,0,0,-1,0,0,0,0,0,4,0,107,0,2,0,0 +122,00:04.8,0,0,0,-1,0,0,0,0,0,4,0,108,0,2,0,0 +123,00:04.9,0,0,0,-1,0,0,0,0,0,4,0,109,0,2,0,0 +124,00:04.9,0,0,0,-1,0,0,0,0,0,4,0,110,0,2,0,0 +125,00:05.0,0,0,0,-1,0,0,0,0,0,4,0,111,0,2,0,0 +126,00:05.0,0,0,0,-1,0,0,0,0,0,4,0,112,0,2,0,0 +127,00:05.0,0,0,0,-1,0,0,0,0,0,4,0,113,0,2,0,0 +128,00:05.1,0,0,0,-1,0,0,0,0,0,4,0,114,0,2,0,0 +129,00:05.1,0,0,0,-1,0,0,0,0,0,4,0,115,0,2,0,0 +130,00:05.2,1,1,1,1,0,0,0,0,0,4,0,116,0,2,0,0 +131,00:05.2,1,0,0,1,0,0,0,0,0,4,0,117,0,2,0,0 +132,00:05.2,1,0,0,1,0,0,0,0,0,4,0,118,0,2,0,0 +133,00:05.3,0,1,-1,-1,0,0,0,0,0,4,0,119,0,2,0,0 +134,00:05.3,0,0,0,-1,0,0,0,0,0,4,0,120,0,2,0,0 +135,00:05.4,0,0,0,-1,0,0,0,0,0,4,0,121,0,2,0,0 +136,00:05.4,1,1,1,1,0,0,0,0,0,4,0,122,0,2,0,0 +137,00:05.4,1,0,0,1,0,0,0,0,0,4,0,123,0,2,0,0 +138,00:05.5,1,0,0,1,0,0,0,0,0,4,0,124,0,2,0,0 +139,00:05.5,0,1,-1,-1,0,0,0,0,0,4,0,125,0,2,0,0 +140,00:05.6,0,0,0,-1,0,0,0,0,0,4,0,125,0,2,0,0 +141,00:05.6,0,0,0,-1,0,0,0,0,0,4,0,125,0,2,0,0 +142,00:05.6,0,0,0,-1,0,0,0,0,0,4,0,125,0,2,0,0 +143,00:05.7,0,0,0,-1,0,0,0,0,0,4,0,125,0,2,0,0 +144,00:05.7,0,0,0,-1,0,0,0,0,0,4,0,125,0,2,0,0 +145,00:05.8,0,0,0,-1,0,0,0,0,0,4,0,125,0,2,0,0 +146,00:05.8,0,0,0,-1,0,0,0,0,0,4,0,125,0,2,0,0 +147,00:05.8,0,0,0,-1,0,0,0,0,0,4,0,125,0,2,0,0 +148,00:05.9,0,0,0,-1,0,0,0,0,0,4,0,125,0,2,0,0 +149,00:05.9,0,0,0,-1,0,0,0,0,0,4,0,125,0,2,0,0 diff --git a/test/Iris.Tests/data/ExpectedVideoLogFiles/3Hz_6s_RELATIVE.csv b/test/Iris.Tests/data/ExpectedVideoLogFiles/3Hz_6s_RELATIVE.csv new file mode 100644 index 0000000..482daf1 --- /dev/null +++ b/test/Iris.Tests/data/ExpectedVideoLogFiles/3Hz_6s_RELATIVE.csv @@ -0,0 +1,150 @@ +Frame,TimeStamp,AverageLuminance,FlashAreaLuminance,AverageLuminanceDiff,AverageLuminanceDiffAcc,AverageRed,FlashAreaRed,AverageRedDiff,AverageRedDiffAcc,PatternRisk,LuminanceTransitions,RedTransitions,LuminanceExtendedFailCount,RedExtendedFailCount,FlashLuminanceFailedFrame,FlashRedFailedFrame,PatternFailedFrame +1,00:00.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +2,00:00.0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +3,00:00.1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +4,00:00.1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +5,00:00.2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +6,00:00.2,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0 +7,00:00.2,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0 +8,00:00.3,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0 +9,00:00.3,0,1,-1,-1,0,0,0,0,0,2,0,0,0,0,0,0 +10,00:00.4,0,0,0,-1,0,0,0,0,0,2,0,0,0,0,0,0 +11,00:00.4,0,0,0,-1,0,0,0,0,0,2,0,0,0,0,0,0 +12,00:00.4,1,1,1,1,0,0,0,0,0,3,0,0,0,0,0,0 +13,00:00.5,1,0,0,1,0,0,0,0,0,3,0,0,0,0,0,0 +14,00:00.5,1,0,0,1,0,0,0,0,0,3,0,0,0,0,0,0 +15,00:00.6,0,1,-1,-1,0,0,0,0,0,4,0,1,0,1,0,0 +16,00:00.6,0,0,0,-1,0,0,0,0,0,4,0,2,0,1,0,0 +17,00:00.6,1,1,1,1,0,0,0,0,0,5,0,3,0,1,0,0 +18,00:00.7,1,0,0,1,0,0,0,0,0,5,0,4,0,1,0,0 +19,00:00.7,1,0,0,1,0,0,0,0,0,5,0,5,0,1,0,0 +20,00:00.8,0,1,-1,-1,0,0,0,0,0,6,0,6,0,1,0,0 +21,00:00.8,0,0,0,-1,0,0,0,0,0,6,0,7,0,1,0,0 +22,00:00.8,0,0,0,-1,0,0,0,0,0,6,0,8,0,1,0,0 +23,00:00.9,0,0,0,-1,0,0,0,0,0,6,0,9,0,1,0,0 +24,00:00.9,0,0,0,-1,0,0,0,0,0,6,0,10,0,1,0,0 +25,00:01.0,0,0,0,-1,0,0,0,0,0,6,0,11,0,1,0,0 +26,00:01.0,0,0,0,-1,0,0,0,0,0,6,0,12,0,1,0,0 +27,00:01.0,0,0,0,-1,0,0,0,0,0,6,0,13,0,1,0,0 +28,00:01.1,0,0,0,-1,0,0,0,0,0,6,0,14,0,1,0,0 +29,00:01.1,0,0,0,-1,0,0,0,0,0,6,0,15,0,1,0,0 +30,00:01.2,1,1,1,1,0,0,0,0,0,7,0,15,0,3,0,0 +31,00:01.2,1,0,0,1,0,0,0,0,0,6,0,16,0,1,0,0 +32,00:01.2,1,0,0,1,0,0,0,0,0,6,0,17,0,1,0,0 +33,00:01.3,0,1,-1,-1,0,0,0,0,0,7,0,17,0,3,0,0 +34,00:01.3,0,0,0,-1,0,0,0,0,0,6,0,18,0,1,0,0 +35,00:01.4,0,0,0,-1,0,0,0,0,0,6,0,19,0,1,0,0 +36,00:01.4,1,1,1,1,0,0,0,0,0,7,0,19,0,3,0,0 +37,00:01.4,1,0,0,1,0,0,0,0,0,6,0,20,0,1,0,0 +38,00:01.5,1,0,0,1,0,0,0,0,0,6,0,21,0,1,0,0 +39,00:01.5,0,1,-1,-1,0,0,0,0,0,7,0,21,0,3,0,0 +40,00:01.6,0,0,0,-1,0,0,0,0,0,6,0,22,0,1,0,0 +41,00:01.6,1,1,1,1,0,0,0,0,0,7,0,22,0,3,0,0 +42,00:01.6,1,0,0,1,0,0,0,0,0,6,0,23,0,1,0,0 +43,00:01.7,1,0,0,1,0,0,0,0,0,6,0,24,0,1,0,0 +44,00:01.7,0,1,-1,-1,0,0,0,0,0,7,0,24,0,3,0,0 +45,00:01.8,0,0,0,-1,0,0,0,0,0,6,0,25,0,1,0,0 +46,00:01.8,0,0,0,-1,0,0,0,0,0,6,0,26,0,1,0,0 +47,00:01.8,0,0,0,-1,0,0,0,0,0,6,0,27,0,1,0,0 +48,00:01.9,0,0,0,-1,0,0,0,0,0,6,0,28,0,1,0,0 +49,00:01.9,0,0,0,-1,0,0,0,0,0,6,0,29,0,1,0,0 +50,00:02.0,0,0,0,-1,0,0,0,0,0,6,0,30,0,1,0,0 +51,00:02.0,0,0,0,-1,0,0,0,0,0,6,0,31,0,1,0,0 +52,00:02.0,0,0,0,-1,0,0,0,0,0,6,0,32,0,1,0,0 +53,00:02.1,0,0,0,-1,0,0,0,0,0,6,0,33,0,1,0,0 +54,00:02.1,0,0,0,-1,0,0,0,0,0,6,0,34,0,1,0,0 +55,00:02.2,1,1,1,1,0,0,0,0,0,6,0,35,0,1,0,0 +56,00:02.2,1,0,0,1,0,0,0,0,0,6,0,36,0,1,0,0 +57,00:02.2,1,0,0,1,0,0,0,0,0,6,0,37,0,1,0,0 +58,00:02.3,0,1,-1,-1,0,0,0,0,0,6,0,38,0,1,0,0 +59,00:02.3,0,0,0,-1,0,0,0,0,0,6,0,39,0,1,0,0 +60,00:02.4,0,0,0,-1,0,0,0,0,0,6,0,40,0,1,0,0 +61,00:02.4,1,1,1,1,0,0,0,0,0,6,0,41,0,1,0,0 +62,00:02.4,1,0,0,1,0,0,0,0,0,6,0,42,0,1,0,0 +63,00:02.5,1,0,0,1,0,0,0,0,0,6,0,43,0,1,0,0 +64,00:02.5,0,1,-1,-1,0,0,0,0,0,6,0,44,0,1,0,0 +65,00:02.6,0,0,0,-1,0,0,0,0,0,6,0,45,0,1,0,0 +66,00:02.6,1,1,1,1,0,0,0,0,0,6,0,46,0,1,0,0 +67,00:02.6,1,0,0,1,0,0,0,0,0,6,0,47,0,1,0,0 +68,00:02.7,1,0,0,1,0,0,0,0,0,6,0,48,0,1,0,0 +69,00:02.7,0,1,-1,-1,0,0,0,0,0,6,0,49,0,1,0,0 +70,00:02.8,0,0,0,-1,0,0,0,0,0,6,0,50,0,1,0,0 +71,00:02.8,0,0,0,-1,0,0,0,0,0,6,0,51,0,1,0,0 +72,00:02.8,0,0,0,-1,0,0,0,0,0,6,0,52,0,1,0,0 +73,00:02.9,0,0,0,-1,0,0,0,0,0,6,0,53,0,1,0,0 +74,00:02.9,0,0,0,-1,0,0,0,0,0,6,0,54,0,1,0,0 +75,00:03.0,0,0,0,-1,0,0,0,0,0,6,0,55,0,1,0,0 +76,00:03.0,0,0,0,-1,0,0,0,0,0,6,0,56,0,1,0,0 +77,00:03.0,0,0,0,-1,0,0,0,0,0,6,0,57,0,1,0,0 +78,00:03.1,0,0,0,-1,0,0,0,0,0,6,0,58,0,1,0,0 +79,00:03.1,0,0,0,-1,0,0,0,0,0,6,0,59,0,1,0,0 +80,00:03.2,1,1,1,1,0,0,0,0,0,6,0,60,0,1,0,0 +81,00:03.2,1,0,0,1,0,0,0,0,0,6,0,61,0,1,0,0 +82,00:03.2,1,0,0,1,0,0,0,0,0,6,0,62,0,1,0,0 +83,00:03.3,0,1,-1,-1,0,0,0,0,0,6,0,63,0,1,0,0 +84,00:03.3,0,0,0,-1,0,0,0,0,0,6,0,64,0,1,0,0 +85,00:03.4,0,0,0,-1,0,0,0,0,0,6,0,65,0,1,0,0 +86,00:03.4,1,1,1,1,0,0,0,0,0,6,0,66,0,1,0,0 +87,00:03.4,1,0,0,1,0,0,0,0,0,6,0,67,0,1,0,0 +88,00:03.5,1,0,0,1,0,0,0,0,0,6,0,68,0,1,0,0 +89,00:03.5,0,1,-1,-1,0,0,0,0,0,6,0,69,0,1,0,0 +90,00:03.6,0,0,0,-1,0,0,0,0,0,6,0,70,0,1,0,0 +91,00:03.6,1,1,1,1,0,0,0,0,0,6,0,71,0,1,0,0 +92,00:03.6,1,0,0,1,0,0,0,0,0,6,0,72,0,1,0,0 +93,00:03.7,1,0,0,1,0,0,0,0,0,6,0,73,0,1,0,0 +94,00:03.7,0,1,-1,-1,0,0,0,0,0,6,0,74,0,1,0,0 +95,00:03.8,0,0,0,-1,0,0,0,0,0,6,0,75,0,1,0,0 +96,00:03.8,0,0,0,-1,0,0,0,0,0,6,0,76,0,1,0,0 +97,00:03.8,0,0,0,-1,0,0,0,0,0,6,0,77,0,1,0,0 +98,00:03.9,0,0,0,-1,0,0,0,0,0,6,0,78,0,1,0,0 +99,00:03.9,0,0,0,-1,0,0,0,0,0,6,0,79,0,1,0,0 +100,00:04.0,0,0,0,-1,0,0,0,0,0,6,0,80,0,1,0,0 +101,00:04.0,0,0,0,-1,0,0,0,0,0,6,0,81,0,1,0,0 +102,00:04.0,0,0,0,-1,0,0,0,0,0,6,0,82,0,1,0,0 +103,00:04.1,0,0,0,-1,0,0,0,0,0,6,0,83,0,1,0,0 +104,00:04.1,0,0,0,-1,0,0,0,0,0,6,0,84,0,1,0,0 +105,00:04.2,1,1,1,1,0,0,0,0,0,6,0,85,0,1,0,0 +106,00:04.2,1,0,0,1,0,0,0,0,0,6,0,86,0,1,0,0 +107,00:04.2,1,0,0,1,0,0,0,0,0,6,0,87,0,1,0,0 +108,00:04.3,0,1,-1,-1,0,0,0,0,0,6,0,88,0,1,0,0 +109,00:04.3,0,0,0,-1,0,0,0,0,0,6,0,89,0,1,0,0 +110,00:04.4,0,0,0,-1,0,0,0,0,0,6,0,90,0,1,0,0 +111,00:04.4,1,1,1,1,0,0,0,0,0,6,0,91,0,1,0,0 +112,00:04.4,1,0,0,1,0,0,0,0,0,6,0,92,0,1,0,0 +113,00:04.5,1,0,0,1,0,0,0,0,0,6,0,93,0,1,0,0 +114,00:04.5,0,1,-1,-1,0,0,0,0,0,6,0,94,0,1,0,0 +115,00:04.6,0,0,0,-1,0,0,0,0,0,6,0,95,0,1,0,0 +116,00:04.6,1,1,1,1,0,0,0,0,0,6,0,96,0,1,0,0 +117,00:04.6,1,0,0,1,0,0,0,0,0,6,0,97,0,1,0,0 +118,00:04.7,1,0,0,1,0,0,0,0,0,6,0,98,0,1,0,0 +119,00:04.7,0,1,-1,-1,0,0,0,0,0,6,0,99,0,1,0,0 +120,00:04.8,0,0,0,-1,0,0,0,0,0,6,0,100,0,2,0,0 +121,00:04.8,0,0,0,-1,0,0,0,0,0,6,0,101,0,2,0,0 +122,00:04.8,0,0,0,-1,0,0,0,0,0,6,0,102,0,2,0,0 +123,00:04.9,0,0,0,-1,0,0,0,0,0,6,0,103,0,2,0,0 +124,00:04.9,0,0,0,-1,0,0,0,0,0,6,0,104,0,2,0,0 +125,00:05.0,0,0,0,-1,0,0,0,0,0,6,0,105,0,2,0,0 +126,00:05.0,0,0,0,-1,0,0,0,0,0,6,0,106,0,2,0,0 +127,00:05.0,0,0,0,-1,0,0,0,0,0,6,0,107,0,2,0,0 +128,00:05.1,0,0,0,-1,0,0,0,0,0,6,0,108,0,2,0,0 +129,00:05.1,0,0,0,-1,0,0,0,0,0,6,0,109,0,2,0,0 +130,00:05.2,1,1,1,1,0,0,0,0,0,6,0,110,0,2,0,0 +131,00:05.2,1,0,0,1,0,0,0,0,0,6,0,111,0,2,0,0 +132,00:05.2,1,0,0,1,0,0,0,0,0,6,0,112,0,2,0,0 +133,00:05.3,0,1,-1,-1,0,0,0,0,0,6,0,113,0,2,0,0 +134,00:05.3,0,0,0,-1,0,0,0,0,0,6,0,114,0,2,0,0 +135,00:05.4,0,0,0,-1,0,0,0,0,0,6,0,115,0,2,0,0 +136,00:05.4,1,1,1,1,0,0,0,0,0,6,0,116,0,2,0,0 +137,00:05.4,1,0,0,1,0,0,0,0,0,6,0,117,0,2,0,0 +138,00:05.5,1,0,0,1,0,0,0,0,0,6,0,118,0,2,0,0 +139,00:05.5,0,1,-1,-1,0,0,0,0,0,6,0,119,0,2,0,0 +140,00:05.6,0,0,0,-1,0,0,0,0,0,6,0,119,0,2,0,0 +141,00:05.6,1,1,1,1,0,0,0,0,0,6,0,119,0,2,0,0 +142,00:05.6,1,0,0,1,0,0,0,0,0,6,0,119,0,2,0,0 +143,00:05.7,1,0,0,1,0,0,0,0,0,6,0,119,0,2,0,0 +144,00:05.7,0,1,-1,-1,0,0,0,0,0,6,0,119,0,2,0,0 +145,00:05.8,0,0,0,-1,0,0,0,0,0,6,0,119,0,2,0,0 +146,00:05.8,0,0,0,-1,0,0,0,0,0,6,0,119,0,2,0,0 +147,00:05.8,0,0,0,-1,0,0,0,0,0,6,0,119,0,2,0,0 +148,00:05.9,0,0,0,-1,0,0,0,0,0,6,0,119,0,2,0,0 +149,00:05.9,0,0,0,-1,0,0,0,0,0,6,0,119,0,2,0,0 diff --git a/test/Iris.Tests/data/ExpectedVideoLogFiles/GradualRedIncrease_RELATIVE.csv b/test/Iris.Tests/data/ExpectedVideoLogFiles/GradualRedIncrease_RELATIVE.csv new file mode 100644 index 0000000..9d959dc Binary files /dev/null and b/test/Iris.Tests/data/ExpectedVideoLogFiles/GradualRedIncrease_RELATIVE.csv differ diff --git a/test/Iris.Tests/data/ExpectedVideoLogFiles/Hypno Spiral Small_RELATIVE.csv b/test/Iris.Tests/data/ExpectedVideoLogFiles/Hypno Spiral Small_RELATIVE.csv new file mode 100644 index 0000000..74bb625 --- /dev/null +++ b/test/Iris.Tests/data/ExpectedVideoLogFiles/Hypno Spiral Small_RELATIVE.csv @@ -0,0 +1,919 @@ +Frame,TimeStamp,AverageLuminanceDiff,AverageLuminanceDiffAcc,AverageRedDiff,AverageRedDiffAcc,PatternRisk,LuminanceTransitions,RedTransitions,TotalTransitions,FlashFailedFrames,PatternFailedFrames +1,00:00:00,0,0,0,0,0,0,0,0,0,0 +2,00:00:00.1160000,0,0,0,0,0,0,0,0,0,0 +3,00:00:00.1500000,0,0,0,0,0,0,0,0,0,0 +4,00:00:00.1830000,0,0,0,0,0,0,0,0,0,0 +5,00:00:00.2160000,0,0,0,0,0,0,0,0,0,0 +6,00:00:00.2500000,0,0,0,0,0,0,0,0,0,0 +7,00:00:00.2830000,0,0,0,0,0,0,0,0,0,0 +8,00:00:00.3160000,0,0,0,0,0,0,0,0,0,0 +9,00:00:00.3500000,0,0,0,0,0,0,0,0,0,0 +10,00:00:00.3830000,0,0,0,0,0,0,0,0,0,0 +11,00:00:00.4160000,0,0,0,0,0,0,0,0,0,0 +12,00:00:00.4500000,0,0,0,0,0,0,0,0,0,0 +13,00:00:00.4830000,0,0,0,0,0,0,0,0,0,0 +14,00:00:00.5160000,0,0,0,0,0,0,0,0,0,0 +15,00:00:00.5500000,0,0,0,0,0,0,0,0,0,0 +16,00:00:00.5830000,0,0,0,0,0,0,0,0,0,0 +17,00:00:00.6160000,0,0,0,0,0,0,0,0,0,0 +18,00:00:00.6500000,0,0,0,0,0,0,0,0,0,0 +19,00:00:00.6830000,0,0,0,0,0,0,0,0,0,0 +20,00:00:00.7160000,0,0,0,0,0,0,0,0,0,0 +21,00:00:00.7500000,0.00043203266,0.00043203266,0,0,0.552,0,0,0,0,0 +22,00:00:00.7830000,0.00039369252,0.00082572515,0,0,0.366,0,0,0,0,0 +23,00:00:00.8160000,0.00039369252,0.00082572515,0,0,0.337,0,0,0,0,0 +24,00:00:00.8500000,0.00067318464,0.0014989098,0,0,0.979,0,0,0,0,0 +25,00:00:00.8830000,0.0006512221,0.0021501319,0,0,0.964,0,0,0,0,0 +26,00:00:00.9160000,0.0008744098,0.0030245418,0,0,0.964,0,0,0,0,0 +27,00:00:00.9500000,0.00093086524,0.003955407,0,0,0.881,0,0,0,0,0 +28,00:00:00.9830000,0.00093086524,0.003955407,0,0,0.948,0,0,0,0,0 +29,00:00:01.0160000,0.0014783692,0.0054337764,0,0,0,0,0,0,0,0 +30,00:00:01.0500000,0.00155326,0.006987036,0,0,1,0,0,0,0,0 +31,00:00:01.0830000,0.0019004487,0.008887485,0,0,0,0,0,0,0,0 +32,00:00:01.1160000,0.0019620382,0.010849522,0,0,1,0,0,0,0,0 +33,00:00:01.1500000,0.0019620382,0.010849522,0,0,1,0,0,0,0,0 +34,00:00:01.1830000,0.0021972691,0.013046792,0,0,0.994,0,0,0,0,0 +35,00:00:01.2160000,0.0023103366,0.015357128,0,0,0.979,0,0,0,0,0 +36,00:00:01.2500000,0.002726383,0.01808351,0,0,1,0,0,0,0,0 +37,00:00:01.2830000,0.0029429456,0.021026457,0,0,0.979,0,0,0,0,0 +38,00:00:01.3160000,0.0029429456,0.021026457,0,0,0.948,0,0,0,0,0 +39,00:00:01.3500000,0.003259409,0.024285866,0,0,0.99,0,0,0,0,0 +40,00:00:01.3830000,0.003444576,0.027730443,0,0,0,0,0,0,0,0 +41,00:00:01.4160000,0.004304283,0.032034725,0,0,0,0,0,0,0,0 +42,00:00:01.4500000,0.004351966,0.03638669,0,0,0,0,0,0,0,0 +43,00:00:01.4830000,0.004351966,0.03638669,0,0,0,0,0,0,0,0 +44,00:00:01.5160000,0.004703555,0.041090246,0,0,0.911,0,0,0,0,0 +45,00:00:01.5500000,0.0048530847,0.04594333,0,0,0,0,0,0,0,0 +46,00:00:01.5830000,0.0052909604,0.05123429,0,0,0,0,0,0,0,0 +47,00:00:01.6160000,0.005585506,0.056819797,0,0,0,0,0,0,0,0 +48,00:00:01.6500000,0.005585506,0.056819797,0,0,0,0,0,0,0,0 +49,00:00:01.6830000,0.006709865,0.06352966,0,0,0,0,0,0,0,0 +50,00:00:01.7160000,0.0072400253,0.07076969,0,0,0,0,0,0,0,0 +51,00:00:01.7500000,0.0076867105,0.07802437,0,0,0.737,0,0,0,0,0 +52,00:00:01.7830000,0.008007088,0.08603146,0,0,0.734,0,0,0,0,0 +53,00:00:01.8160000,0.008007088,0.08603146,0,0,0.734,0,0,0,0,0 +54,00:00:01.8500000,0.008339519,0.094370976,0,0,0,0,0,0,0,0 +55,00:00:01.8830000,0.008752213,0.10312319,0,0,0.694,1,0,1,0,0 +56,00:00:01.9160000,0.00920903,0.11233222,0,0,0.656,1,0,1,0,0 +57,00:00:01.9500000,0.009895454,0.12222767,0,0,0.658,1,0,1,0,0 +58,00:00:01.9830000,0.009895454,0.12222767,0,0,0.657,1,0,1,0,0 +59,00:00:02.0160000,0.010663742,0.13289142,0,0,0.611,1,0,1,0,0 +60,00:00:02.0500000,0.011402311,0.14429373,0,0,0.605,1,0,1,0,0 +61,00:00:02.0830000,-0.012056688,-0.012056688,0,0,0.564,1,0,1,0,0 +62,00:00:02.1160000,0.013295153,0.013295153,0,0,0.563,1,0,1,0,0 +63,00:00:02.1500000,0.013295153,0.013295153,0,0,0.563,1,0,1,0,0 +64,00:00:02.1830000,-0.013570258,-0.013570258,0,0,0.552,1,0,1,0,0 +65,00:00:02.2160000,-0.013912324,-0.027482582,0,0,0.54,1,0,1,0,0 +66,00:00:02.2500000,-0.01521141,-0.04269399,0,0,0.51,1,0,1,0,0 +67,00:00:02.2830000,0.015290396,0.015290396,0,0,0.5,1,0,1,0,0 +68,00:00:02.3160000,0.015290396,0.015290396,0,0,0.5,1,0,1,0,0 +69,00:00:02.3500000,-0.017013805,-0.017013805,0,0,0.456,1,0,1,0,1 +70,00:00:02.3830000,-0.018246572,-0.03526038,0,0,0.44,1,0,1,0,1 +71,00:00:02.4160000,-0.01924116,-0.05450154,0,0,0.43,1,0,1,0,1 +72,00:00:02.4500000,0.019256493,0.019256493,0,0,0.431,1,0,1,0,1 +73,00:00:02.4830000,0.019256493,0.019256493,0,0,0.431,1,0,1,0,1 +74,00:00:02.5160000,-0.019898629,-0.019898629,0,0,0.414,1,0,1,0,1 +75,00:00:02.5500000,0.020485442,0.020485442,0,0,0.401,1,0,1,0,1 +76,00:00:02.5830000,-0.022085339,-0.022085339,0,0,0.376,1,0,1,0,0 +77,00:00:02.6160000,0.023747578,0.023747578,0,0,0.371,1,0,1,0,0 +78,00:00:02.6500000,0.023747578,0.023747578,0,0,0.371,1,0,1,0,0 +79,00:00:02.6830000,-0.024257425,-0.024257425,0,0,0.341,1,0,1,0,0 +80,00:00:02.7160000,0.025170706,0.025170706,0,0,0.338,1,0,1,0,0 +81,00:00:02.7500000,-0.026433775,-0.026433775,0,0,0.318,1,0,1,0,0 +82,00:00:02.7830000,0.02791689,0.02791689,0,0,0.314,1,0,1,0,0 +83,00:00:02.8160000,0.02791689,0.02791689,0,0,0.314,1,0,1,0,0 +84,00:00:02.8500000,-0.028403226,-0.028403226,0,0,0.295,1,0,1,0,0 +85,00:00:02.8830000,0.029986674,0.029986674,0,0,0.286,0,0,0,0,0 +86,00:00:02.9160000,0.030086877,0.06007355,0,0,0.268,0,0,0,0,0 +87,00:00:02.9500000,0.032004673,0.092078224,0,0,0.255,0,0,0,0,0 +88,00:00:02.9830000,0.032004673,0.092078224,0,0,0.255,0,0,0,0,0 +89,00:00:03.0160000,-0.033328068,-0.033328068,0,0,0.246,0,0,0,0,0 +90,00:00:03.0500000,-0.033992916,-0.06732099,0,0,0.239,0,0,0,0,0 +91,00:00:03.0830000,-0.035023354,-0.10234434,0,0,0.219,1,0,1,0,0 +92,00:00:03.1160000,0.037019216,0.037019216,0,0,0.215,1,0,1,0,0 +93,00:00:03.1500000,0.037019216,0.037019216,0,0,0.215,1,0,1,0,0 +94,00:00:03.1830000,-0.037910268,-0.037910268,0,0,0.198,1,0,1,0,0 +95,00:00:03.2160000,0.038844764,0.038844764,0,0,0.189,1,0,1,0,0 +96,00:00:03.2500000,-0.040877733,-0.040877733,0,0,0.169,1,0,1,0,0 +97,00:00:03.2830000,0.04278828,0.04278828,0,0,0.162,1,0,1,0,0 +98,00:00:03.3160000,0.04278828,0.04278828,0,0,0.162,1,0,1,0,0 +99,00:00:03.3500000,0.043047823,0.0858361,0,0,0.15,1,0,1,0,0 +100,00:00:03.3830000,-0.04533572,-0.04533572,0,0,0.146,1,0,1,0,0 +101,00:00:03.4160000,-0.04968609,-0.095021814,0,0,0.128,1,0,1,0,0 +102,00:00:03.4500000,-0.048046272,-0.14306809,0,0,0.12,2,0,2,0,0 +103,00:00:03.4830000,-0.048046272,-0.14306809,0,0,0.12,2,0,2,0,0 +104,00:00:03.5160000,0.050569966,0.050569966,0,0,0.109,2,0,2,0,0 +105,00:00:03.5500000,-0.051384132,-0.051384132,0,0,0.1,2,0,2,0,0 +106,00:00:03.5830000,-0.055303928,-0.10668806,0,0,0.086,3,0,3,0,0 +107,00:00:03.6160000,0.055321086,0.055321086,0,0,0.079,3,0,3,0,0 +108,00:00:03.6500000,0.055321086,0.055321086,0,0,0.079,3,0,3,0,0 +109,00:00:03.6830000,-0.05895876,-0.05895876,0,0,0.067,3,0,3,0,0 +110,00:00:03.7160000,-0.060547143,-0.119505905,0,0,0.054,4,0,4,0,0 +111,00:00:03.7500000,0.060165025,0.060165025,0,0,0.045,4,0,4,0,0 +112,00:00:03.7830000,0.06125961,0.12142463,0,0,0.038,5,0,5,0,0 +113,00:00:03.8160000,0.06125961,0.12142463,0,0,0.038,5,0,5,0,0 +114,00:00:03.8500000,-0.064103656,-0.064103656,0,0,0.033,5,0,5,0,0 +115,00:00:03.8830000,0.063869305,0.063869305,0,0,0.025,5,0,5,0,0 +116,00:00:03.9160000,-0.067910925,-0.067910925,0,0,0.014,5,0,5,0,0 +117,00:00:03.9500000,-0.06816202,-0.13607293,0,0,0.005,6,0,6,0,0 +118,00:00:03.9830000,-0.06816202,-0.13607293,0,0,0.006,6,0,6,0,0 +119,00:00:04.0160000,-0.07164098,-0.20771392,0,0,0.008,6,0,6,0,0 +120,00:00:04.0500000,-0.07333727,-0.2810512,0,0,0.015,6,0,6,0,0 +121,00:00:04.0830000,-0.07647312,-0.3575243,0,0,0.03,5,0,5,0,0 +122,00:00:04.1160000,0.07758867,0.07758867,0,0,0.033,5,0,5,0,0 +123,00:00:04.1500000,0.07758867,0.07758867,0,0,0.033,5,0,5,0,0 +124,00:00:04.1830000,-0.07825738,-0.07825738,0,0,0.041,5,0,5,0,0 +125,00:00:04.2160000,0.0791826,0.0791826,0,0,0.048,5,0,5,0,0 +126,00:00:04.2500000,-0.085642114,-0.085642114,0,0,0.063,5,0,5,0,0 +127,00:00:04.2830000,0.086146675,0.086146675,0,0,0.069,5,0,5,0,0 +128,00:00:04.3160000,0.086146675,0.086146675,0,0,0.069,5,0,5,0,0 +129,00:00:04.3500000,0.08781744,0.17396411,0,0,0.078,6,0,6,0,0 +130,00:00:04.3830000,0.08902175,0.26298586,0,0,0.09,6,0,6,0,0 +131,00:00:04.4160000,-0.0932806,-0.0932806,0,0,0.097,6,0,6,0,0 +132,00:00:04.4500000,0.0944236,0.0944236,0,0,0.099,5,0,5,0,0 +133,00:00:04.4830000,0.0944236,0.0944236,0,0,0.099,5,0,5,0,0 +134,00:00:04.5160000,-0.096227236,-0.096227236,0,0,0.108,5,0,5,0,0 +135,00:00:04.5500000,-0.100512184,-0.19673942,0,0,0.116,6,0,6,0,0 +136,00:00:04.5830000,-0.10113725,-0.29787666,0,0,0.129,5,0,5,0,0 +137,00:00:04.6160000,0.10181885,0.10181885,0,0,0.135,6,0,6,0,0 +138,00:00:04.6500000,0.10181885,0.10181885,0,0,0.135,6,0,6,0,0 +139,00:00:04.6830000,-0.10679788,-0.10679788,0,0,0.143,7,0,7,2,0 +140,00:00:04.7160000,0.107230775,0.107230775,0,0,0.151,7,0,7,2,0 +141,00:00:04.7500000,-0.110708155,-0.110708155,0,0,0.163,8,0,8,2,0 +142,00:00:04.7830000,0.11143877,0.11143877,0,0,0.166,8,0,8,2,0 +143,00:00:04.8160000,0.11143877,0.11143877,0,0,0.166,8,0,8,2,0 +144,00:00:04.8500000,-0.11849721,-0.11849721,0,0,0.17,9,0,9,2,0 +145,00:00:04.8830000,-0.1191424,-0.2376396,0,0,0.178,9,0,9,2,0 +146,00:00:04.9160000,0.11879207,0.11879207,0,0,0.185,10,0,10,2,0 +147,00:00:04.9500000,-0.123100445,-0.123100445,0,0,0.192,10,0,10,2,0 +148,00:00:04.9830000,-0.123100445,-0.123100445,0,0,0.192,10,0,10,2,0 +149,00:00:05.0160000,-0.12510985,-0.2482103,0,0,0.197,10,0,10,2,0 +150,00:00:05.0500000,-0.12980106,-0.37801135,0,0,0.207,10,0,10,2,0 +151,00:00:05.0830000,-0.131518,-0.50952935,0,0,0.219,10,0,10,2,0 +152,00:00:05.1160000,0.1368559,0.1368559,0,0,0.224,11,0,11,2,0 +153,00:00:05.1500000,0.1368559,0.1368559,0,0,0.224,11,0,11,2,0 +154,00:00:05.1830000,0.13581261,0.2726685,0,0,0.228,11,0,11,2,0 +155,00:00:05.2160000,0.14048333,0.41315186,0,0,0.234,11,0,11,2,0 +156,00:00:05.2500000,0.14151585,0.5546677,0,0,0.242,11,0,11,2,0 +157,00:00:05.2830000,-0.14730881,-0.14730881,0,0,0.247,12,0,12,2,0 +158,00:00:05.3160000,-0.14730881,-0.14730881,0,0,0.246,12,0,12,2,0 +159,00:00:05.3500000,-0.14939511,-0.29670393,0,0,0.252,11,0,11,2,0 +160,00:00:05.3830000,-0.15600853,-0.45271248,0,0,0.258,11,0,11,2,0 +161,00:00:05.4160000,-0.14924797,-0.6019604,0,0,0.266,11,0,11,2,0 +162,00:00:05.4500000,-0.15747245,-0.75943285,0,0,0.275,11,0,11,2,0 +163,00:00:05.4830000,-0.15747245,-0.75943285,0,0,0.275,11,0,11,2,0 +164,00:00:05.5160000,-0.16167426,-0.9211071,0,0,0.284,11,0,11,2,0 +165,00:00:05.5500000,0.16063714,0.16063714,0,0,0.285,11,0,11,2,0 +166,00:00:05.5830000,0.16604549,0.32668263,0,0,0.29,11,0,11,2,0 +167,00:00:05.6160000,-0.1703326,-0.1703326,0,0,0.3,11,0,11,2,0 +168,00:00:05.6500000,-0.1703326,-0.1703326,0,0,0.3,11,0,11,2,0 +169,00:00:05.6830000,0.17447351,0.17447351,0,0,0.309,11,0,11,2,0 +170,00:00:05.7160000,-0.17628184,-0.17628184,0,0,0.313,11,0,11,2,0 +171,00:00:05.7500000,-0.17901978,-0.35530162,0,0,0.322,10,0,10,2,0 +172,00:00:05.7830000,0.1875136,0.1875136,0,0,0.325,10,0,10,2,0 +173,00:00:05.8160000,0.1875136,0.1875136,0,0,0.325,10,0,10,2,0 +174,00:00:05.8500000,-0.18474323,-0.18474323,0,0,0.327,10,0,10,2,0 +175,00:00:05.8830000,0.18683307,0.18683307,0,0,0.332,11,0,11,2,0 +176,00:00:05.9160000,-0.18787955,-0.18787955,0,0,0.338,11,0,11,2,0 +177,00:00:05.9500000,0.19539914,0.19539914,0,0,0.346,11,0,11,2,0 +178,00:00:05.9830000,0.19539914,0.19539914,0,0,0.346,11,0,11,2,0 +179,00:00:06.0160000,0.19598433,0.39138347,0,0,0.352,11,0,11,2,0 +180,00:00:06.0500000,0.2042398,0.59562325,0,0,0.359,11,0,11,2,0 +181,00:00:06.0830000,0.20216061,0.79778385,0,0,0.366,11,0,11,2,0 +182,00:00:06.1160000,0.21246147,1.0102453,0,0,0.371,10,0,10,2,0 +183,00:00:06.1500000,0.21246147,1.0102453,0,0,0.371,10,0,10,2,0 +184,00:00:06.1830000,-0.20811827,-0.20811827,0,0,0.375,11,0,11,2,0 +185,00:00:06.2160000,-0.21865812,-0.4267764,0,0,0.382,11,0,11,2,0 +186,00:00:06.2500000,-0.22729151,-0.65406793,0,0,0.398,11,0,11,2,0 +187,00:00:06.2830000,0.22685453,0.22685453,0,0,0.402,11,0,11,2,0 +188,00:00:06.3160000,0.22685453,0.22685453,0,0,0.402,11,0,11,2,0 +189,00:00:06.3500000,0.22741298,0.4542675,0,0,0.404,11,0,11,2,0 +190,00:00:06.3830000,0.23320948,0.687477,0,0,0.406,11,0,11,2,0 +191,00:00:06.4160000,0.23188092,0.9193579,0,0,0.408,11,0,11,2,0 +192,00:00:06.4500000,0.23826744,1.1576253,0,0,0.411,11,0,11,2,0 +193,00:00:06.4830000,0.23826744,1.1576253,0,0,0.411,11,0,11,2,0 +194,00:00:06.5160000,0.23716906,1.3947943,0,0,0.415,11,0,11,2,0 +195,00:00:06.5500000,-0.24736466,-0.24736466,0,0,0.425,11,0,11,2,0 +196,00:00:06.5830000,-0.2414188,-0.48878345,0,0,0.432,11,0,11,2,0 +197,00:00:06.6160000,-0.24929413,-0.7380776,0,0,0.438,10,0,10,2,0 +198,00:00:06.6500000,-0.24929413,-0.7380776,0,0,0.438,10,0,10,2,0 +199,00:00:06.6830000,0.25485495,0.25485495,0,0,0.441,10,0,10,2,0 +200,00:00:06.7160000,0.25499,0.50984496,0,0,0.446,9,0,9,2,0 +201,00:00:06.7500000,-0.27449858,-0.27449858,0,0,0.464,10,0,10,2,1 +202,00:00:06.7830000,0.27968174,0.27968174,0,0,0.467,10,0,10,2,1 +203,00:00:06.8160000,0.27968174,0.27968174,0,0,0.467,10,0,10,2,1 +204,00:00:06.8500000,0.27554828,0.55523,0,0,0.468,9,0,9,2,1 +205,00:00:06.8830000,-0.2826872,-0.2826872,0,0,0.471,9,0,9,2,1 +206,00:00:06.9160000,-0.27991185,-0.56259906,0,0,0.474,8,0,8,2,1 +207,00:00:06.9500000,0.28248492,0.28248492,0,0,0.477,8,0,8,2,1 +208,00:00:06.9830000,0.28248492,0.28248492,0,0,0.477,8,0,8,2,1 +209,00:00:07.0160000,0.29176834,0.57425326,0,0,0.477,8,0,8,2,1 +210,00:00:07.0500000,-0.2836823,-0.2836823,0,0,0.48,9,0,9,2,1 +211,00:00:07.0830000,-0.28910872,-0.572791,0,0,0.488,9,0,9,2,1 +212,00:00:07.1160000,-0.29313532,-0.86592627,0,0,0.497,9,0,9,2,1 +213,00:00:07.1500000,-0.29313532,-0.86592627,0,0,0.497,9,0,9,2,1 +214,00:00:07.1830000,0.30233645,0.30233645,0,0,0.501,9,0,9,2,1 +215,00:00:07.2160000,0.30293357,0.60527,0,0,0.506,9,0,9,2,1 +216,00:00:07.2500000,-0.31965622,-0.31965622,0,0,0.525,10,0,10,2,1 +217,00:00:07.2830000,-0.33019543,-0.6498517,0,0,0.528,9,0,9,2,1 +218,00:00:07.3160000,-0.33019543,-0.6498517,0,0,0.528,9,0,9,2,1 +219,00:00:07.3500000,-0.32354695,-0.9733986,0,0,0.529,9,0,9,2,1 +220,00:00:07.3830000,-0.32419628,-1.2975949,0,0,0.534,9,0,9,2,1 +221,00:00:07.4160000,0.32561025,0.32561025,0,0,0.535,10,0,10,2,1 +222,00:00:07.4500000,0.3296956,0.65530586,0,0,0.537,10,0,10,2,1 +223,00:00:07.4830000,0.3296956,0.65530586,0,0,0.537,10,0,10,2,1 +224,00:00:07.5160000,0.32474282,0.98004866,0,0,0.538,10,0,10,2,1 +225,00:00:07.5500000,0.33176854,1.3118172,0,0,0.539,9,0,9,2,1 +226,00:00:07.5830000,-0.3267269,-0.3267269,0,0,0.545,10,0,10,2,1 +227,00:00:07.6160000,0.3463743,0.3463743,0,0,0.553,11,0,11,2,1 +228,00:00:07.6500000,0.3463743,0.3463743,0,0,0.553,11,0,11,2,1 +229,00:00:07.6830000,-0.34006488,-0.34006488,0,0,0.555,11,0,11,2,1 +230,00:00:07.7160000,-0.35644138,-0.69650626,0,0,0.56,11,0,11,2,1 +231,00:00:07.7500000,-0.36995462,-1.0664608,0,0,0.574,10,0,10,2,1 +232,00:00:07.7830000,0.37464753,0.37464753,0,0,0.578,10,0,10,2,1 +233,00:00:07.8160000,0.37464753,0.37464753,0,0,0.578,10,0,10,2,1 +234,00:00:07.8500000,-0.37079793,-0.37079793,0,0,0.58,11,0,11,2,1 +235,00:00:07.8830000,-0.38560718,-0.7564051,0,0,0.583,10,0,10,2,1 +236,00:00:07.9160000,0.37139356,0.37139356,0,0,0.586,11,0,11,2,1 +237,00:00:07.9500000,-0.38785675,-0.38785675,0,0,0.589,11,0,11,2,1 +238,00:00:07.9830000,-0.38785675,-0.38785675,0,0,0.589,11,0,11,2,1 +239,00:00:08.0160000,-0.39128312,-0.7791399,0,0,0.59,11,0,11,2,1 +240,00:00:08.0500000,0.38120246,0.38120246,0,0,0.594,11,0,11,2,1 +241,00:00:08.0830000,-0.40002197,-0.40002197,0,0,0.597,12,0,12,2,1 +242,00:00:08.1160000,-0.40555155,-0.8055735,0,0,0.607,12,0,12,2,1 +243,00:00:08.1500000,-0.40555155,-0.8055735,0,0,0.607,12,0,12,2,1 +244,00:00:08.1830000,-0.4138964,-1.2194699,0,0,0.613,11,0,11,2,1 +245,00:00:08.2160000,-0.4158539,-1.6353238,0,0,0.622,11,0,11,2,1 +246,00:00:08.2500000,0.41866365,0.41866365,0,0,0.631,11,0,11,2,1 +247,00:00:08.2830000,-0.4324274,-0.4324274,0,0,0.634,12,0,12,2,1 +248,00:00:08.3160000,-0.4324274,-0.4324274,0,0,0.634,12,0,12,2,1 +249,00:00:08.3500000,0.43374497,0.43374497,0,0,0.636,13,0,13,2,1 +250,00:00:08.3830000,0.44287986,0.8766248,0,0,0.639,13,0,13,2,1 +251,00:00:08.4160000,-0.44006613,-0.44006613,0,0,0.642,13,0,13,2,1 +252,00:00:08.4500000,-0.44713736,-0.88720345,0,0,0.645,13,0,13,2,1 +253,00:00:08.4830000,-0.44713736,-0.88720345,0,0,0.645,13,0,13,2,1 +254,00:00:08.5160000,0.46223736,0.46223736,0,0,0.647,14,0,14,2,1 +255,00:00:08.5500000,0.4591056,0.92134297,0,0,0.649,14,0,14,2,1 +256,00:00:08.5830000,0.44714352,1.3684865,0,0,0.652,13,0,13,2,1 +257,00:00:08.6160000,0.44714352,1.3684865,0,0,0.653,12,0,12,2,1 +258,00:00:08.6500000,0.46666387,1.8351504,0,0,0.659,12,0,12,2,1 +259,00:00:08.6830000,0.470141,2.3052914,0,0,0.665,11,0,11,2,1 +260,00:00:08.7160000,0.47952706,2.7848184,0,0,0.669,11,0,11,2,1 +261,00:00:08.7500000,-0.4887698,-0.4887698,0,0,0.675,12,0,12,2,1 +262,00:00:08.7830000,0.50747085,0.50747085,0,0,0.678,12,0,12,2,1 +263,00:00:08.8160000,0.50747085,0.50747085,0,0,0.678,12,0,12,2,1 +264,00:00:08.8500000,-0.4938848,-0.4938848,0,0,0.679,12,0,12,2,1 +265,00:00:08.8830000,0.49695665,0.49695665,0,0,0.682,13,0,13,2,1 +266,00:00:08.9160000,0.50688034,1.003837,0,0,0.682,12,0,12,2,1 +267,00:00:08.9500000,-0.5106892,-0.5106892,0,0,0.684,12,0,12,2,1 +268,00:00:08.9830000,-0.5106892,-0.5106892,0,0,0.684,12,0,12,2,1 +269,00:00:09.0160000,0.5050218,0.5050218,0,0,0.684,13,0,13,2,1 +270,00:00:09.0500000,0.5116518,1.0166736,0,0,0.685,12,0,12,2,1 +271,00:00:09.0830000,0.4988919,1.5155654,0,0,0.685,11,0,11,2,1 +272,00:00:09.1160000,0.4988919,1.5155654,0,0,0.685,11,0,11,2,1 +273,00:00:09.1500000,-0.50738347,-0.50738347,0,0,0.687,12,0,12,2,1 +274,00:00:09.1830000,0.5145942,0.5145942,0,0,0.686,13,0,13,2,1 +275,00:00:09.2160000,0.50838023,1.0229745,0,0,0.687,13,0,13,2,1 +276,00:00:09.2500000,0.50116533,1.5241399,0,0,0.688,12,0,12,2,1 +277,00:00:09.2830000,0.50116533,1.5241399,0,0,0.688,11,0,11,2,1 +278,00:00:09.3160000,-0.50773305,-0.50773305,0,0,0.689,12,0,12,2,1 +279,00:00:09.3500000,0.52794915,0.52794915,0,0,0.688,12,0,12,2,1 +280,00:00:09.3830000,0.5126015,1.0405507,0,0,0.689,12,0,12,2,1 +281,00:00:09.4160000,0.51727986,1.5578306,0,0,0.688,11,0,11,2,1 +282,00:00:09.4500000,0.51727986,1.5578306,0,0,0.688,11,0,11,2,1 +283,00:00:09.4830000,-0.5007863,-0.5007863,0,0,0.689,12,0,12,2,1 +284,00:00:09.5160000,0.5049145,0.5049145,0,0,0.689,12,0,12,2,1 +285,00:00:09.5500000,-0.51463646,-0.51463646,0,0,0.689,13,0,13,2,1 +286,00:00:09.5830000,0.5154929,0.5154929,0,0,0.689,14,0,14,2,1 +287,00:00:09.6160000,0.5154929,0.5154929,0,0,0.689,14,0,14,2,1 +288,00:00:09.6500000,-0.50859135,-0.50859135,0,0,0.69,15,0,15,2,1 +289,00:00:09.6830000,0.5041684,0.5041684,0,0,0.69,16,0,16,2,1 +290,00:00:09.7160000,-0.5153153,-0.5153153,0,0,0.691,17,0,17,2,1 +291,00:00:09.7500000,0.5124692,0.5124692,0,0,0.691,17,0,17,2,1 +292,00:00:09.7830000,0.5124692,0.5124692,0,0,0.691,16,0,16,2,1 +293,00:00:09.8160000,-0.52118164,-0.52118164,0,0,0.692,17,0,17,2,1 +294,00:00:09.8500000,0.511621,0.511621,0,0,0.69,17,0,17,2,1 +295,00:00:09.8830000,-0.5135282,-0.5135282,0,0,0.691,17,0,17,2,1 +296,00:00:09.9160000,0.520887,0.520887,0,0,0.691,18,0,18,2,1 +297,00:00:09.9500000,0.520887,0.520887,0,0,0.691,17,0,17,2,1 +298,00:00:09.9830000,0.5134496,1.0343366,0,0,0.691,17,0,17,2,1 +299,00:00:10.0160000,0.50968146,1.544018,0,0,0.69,16,0,16,2,1 +300,00:00:10.0500000,0.5124749,2.0564928,0,0,0.691,16,0,16,2,1 +301,00:00:10.0830000,0.5471025,2.6035953,0,0,0.686,16,0,16,2,1 +302,00:00:10.1160000,0.5471025,2.6035953,0,0,0.686,16,0,16,2,1 +303,00:00:10.1500000,-0.51728034,-0.51728034,0,0,0.687,16,0,16,2,1 +304,00:00:10.1830000,0.5215072,0.5215072,0,0,0.687,16,0,16,2,1 +305,00:00:10.2160000,0.52245224,1.0439594,0,0,0.688,16,0,16,2,1 +306,00:00:10.2500000,0.5228962,1.5668555,0,0,0.688,16,0,16,2,1 +307,00:00:10.2830000,0.5228962,1.5668555,0,0,0.688,16,0,16,2,1 +308,00:00:10.3160000,0.52734727,2.0942028,0,0,0.69,15,0,15,2,1 +309,00:00:10.3500000,0.52220446,2.6164072,0,0,0.689,14,0,14,2,1 +310,00:00:10.3830000,0.5168584,3.1332655,0,0,0.689,14,0,14,2,1 +311,00:00:10.4160000,0.52309126,3.6563568,0,0,0.689,14,0,14,2,1 +312,00:00:10.4500000,0.52309126,3.6563568,0,0,0.689,14,0,14,2,1 +313,00:00:10.4830000,0.52263266,4.1789894,0,0,0.69,13,0,13,2,1 +314,00:00:10.5160000,0.52406347,4.703053,0,0,0.689,12,0,12,2,1 +315,00:00:10.5500000,0.51739794,5.220451,0,0,0.69,11,0,11,2,1 +316,00:00:10.5830000,0.520734,5.7411847,0,0,0.69,10,0,10,2,1 +317,00:00:10.6160000,0.520734,5.7411847,0,0,0.69,10,0,10,2,1 +318,00:00:10.6500000,0.52204734,6.263232,0,0,0.69,9,0,9,2,1 +319,00:00:10.6830000,0.5205617,6.783794,0,0,0.69,8,0,8,2,1 +320,00:00:10.7160000,0.51986784,7.303662,0,0,0.69,7,0,7,2,1 +321,00:00:10.7500000,0.51484716,7.818509,0,0,0.69,6,0,6,0,1 +322,00:00:10.7830000,0.51484716,7.818509,0,0,0.69,6,0,6,0,1 +323,00:00:10.8160000,0.5131316,8.33164,0,0,0.691,5,0,5,0,1 +324,00:00:10.8500000,0.51892483,8.850565,0,0,0.689,4,0,4,0,1 +325,00:00:10.8830000,0.5121106,9.362676,0,0,0.691,3,0,3,0,1 +326,00:00:10.9160000,0.5140903,9.876766,0,0,0.689,2,0,2,0,1 +327,00:00:10.9500000,0.5140903,9.876766,0,0,0.689,2,0,2,0,1 +328,00:00:10.9830000,0.50994766,10.386714,0,0,0.689,2,0,2,0,1 +329,00:00:11.0160000,0.5172348,10.903949,0,0,0.689,2,0,2,0,1 +330,00:00:11.0500000,0.5204498,11.424398,0,0,0.69,2,0,2,0,1 +331,00:00:11.0830000,0.518128,11.942527,0,0,0.689,2,0,2,0,1 +332,00:00:11.1160000,0.518128,11.942527,0,0,0.689,2,0,2,0,1 +333,00:00:11.1500000,0.5180752,12.460602,0,0,0.69,1,0,1,0,1 +334,00:00:11.1830000,0.51587385,12.976476,0,0,0.691,0,0,0,0,1 +335,00:00:11.2160000,0.5187284,13.495204,0,0,0.69,0,0,0,0,1 +336,00:00:11.2500000,0.51950145,14.014706,0,0,0.69,0,0,0,0,1 +337,00:00:11.2830000,0.51950145,14.014706,0,0,0.69,0,0,0,0,1 +338,00:00:11.3160000,0.5166947,14.531401,0,0,0.69,0,0,0,0,1 +339,00:00:11.3500000,0.52754927,15.05895,0,0,0.69,0,0,0,0,1 +340,00:00:11.3830000,0.5249287,15.583879,0,0,0.69,0,0,0,0,1 +341,00:00:11.4160000,0.51375306,16.097633,0,0,0.69,0,0,0,0,1 +342,00:00:11.4500000,0.51375306,16.097633,0,0,0.69,0,0,0,0,1 +343,00:00:11.4830000,0.5189057,16.616539,0,0,0.69,0,0,0,0,1 +344,00:00:11.5160000,0.5241408,17.14068,0,0,0.69,0,0,0,0,1 +345,00:00:11.5500000,0.5134961,17.654177,0,0,0.691,0,0,0,0,1 +346,00:00:11.5830000,0.5234604,18.177637,0,0,0.69,0,0,0,0,1 +347,00:00:11.6160000,0.5234604,18.177637,0,0,0.69,0,0,0,0,1 +348,00:00:11.6500000,0.5092883,18.686926,0,0,0.69,0,0,0,0,1 +349,00:00:11.6830000,0.51607305,19.203,0,0,0.69,0,0,0,0,1 +350,00:00:11.7160000,0.52443594,19.727434,0,0,0.69,0,0,0,0,1 +351,00:00:11.7500000,0.52107847,20.248512,0,0,0.69,0,0,0,0,1 +352,00:00:11.7830000,0.52107847,20.248512,0,0,0.69,0,0,0,0,1 +353,00:00:11.8160000,-0.5144032,-0.5144032,0,0,0.69,1,0,1,0,1 +354,00:00:11.8500000,0.5165335,0.5165335,0,0,0.691,2,0,2,0,1 +355,00:00:11.8830000,0.5206742,1.0372077,0,0,0.69,2,0,2,0,1 +356,00:00:11.9160000,0.51642287,1.5536306,0,0,0.69,2,0,2,0,1 +357,00:00:11.9500000,0.51642287,1.5536306,0,0,0.69,2,0,2,0,1 +358,00:00:11.9830000,0.5164086,2.0700393,0,0,0.69,2,0,2,0,1 +359,00:00:12.0160000,0.5106775,2.5807168,0,0,0.691,2,0,2,0,1 +360,00:00:12.0500000,-0.51214415,-0.51214415,0,0,0.691,3,0,3,0,1 +361,00:00:12.0830000,0.5119385,0.5119385,0,0,0.69,4,0,4,0,1 +362,00:00:12.1160000,0.5119385,0.5119385,0,0,0.69,4,0,4,0,1 +363,00:00:12.1500000,-0.5050455,-0.5050455,0,0,0.69,5,0,5,0,1 +364,00:00:12.1830000,0.51732737,0.51732737,0,0,0.691,6,0,6,0,1 +365,00:00:12.2160000,0.50650114,1.0238285,0,0,0.691,6,0,6,0,1 +366,00:00:12.2500000,0.51528573,1.5391142,0,0,0.69,6,0,6,0,1 +367,00:00:12.2830000,0.51528573,1.5391142,0,0,0.69,6,0,6,0,1 +368,00:00:12.3160000,0.5091945,2.0483088,0,0,0.691,6,0,6,0,1 +369,00:00:12.3500000,0.5119176,2.5602264,0,0,0.691,6,0,6,0,1 +370,00:00:12.3830000,-0.5114901,-0.5114901,0,0,0.69,7,0,7,2,1 +371,00:00:12.4160000,0.50712657,0.50712657,0,0,0.691,8,0,8,2,1 +372,00:00:12.4500000,0.50712657,0.50712657,0,0,0.691,8,0,8,2,1 +373,00:00:12.4830000,0.5075559,1.0146825,0,0,0.692,8,0,8,2,1 +374,00:00:12.5160000,0.5109373,1.5256197,0,0,0.691,8,0,8,2,1 +375,00:00:12.5500000,-0.5028197,-0.5028197,0,0,0.691,9,0,9,2,1 +376,00:00:12.5830000,0.519282,0.519282,0,0,0.691,10,0,10,2,1 +377,00:00:12.6160000,0.519282,0.519282,0,0,0.691,10,0,10,2,1 +378,00:00:12.6500000,-0.50890666,-0.50890666,0,0,0.691,11,0,11,2,1 +379,00:00:12.6830000,-0.5111722,-1.0200789,0,0,0.691,11,0,11,2,1 +380,00:00:12.7160000,0.5217161,0.5217161,0,0,0.69,12,0,12,2,1 +381,00:00:12.7500000,-0.5154006,-0.5154006,0,0,0.691,13,0,13,2,1 +382,00:00:12.7830000,-0.5154006,-0.5154006,0,0,0.691,13,0,13,2,1 +383,00:00:12.8160000,-0.5099407,-1.0253413,0,0,0.691,12,0,12,2,1 +384,00:00:12.8500000,0.5116224,0.5116224,0,0,0.692,12,0,12,2,1 +385,00:00:12.8830000,-0.5032808,-0.5032808,0,0,0.691,13,0,13,2,1 +386,00:00:12.9160000,0.5082434,0.5082434,0,0,0.69,14,0,14,2,1 +387,00:00:12.9500000,0.5082434,0.5082434,0,0,0.69,14,0,14,2,1 +388,00:00:12.9830000,0.50462276,1.0128661,0,0,0.691,14,0,14,2,1 +389,00:00:13.0160000,0.51408374,1.5269499,0,0,0.691,14,0,14,2,1 +390,00:00:13.0500000,-0.5188351,-0.5188351,0,0,0.691,14,0,14,2,1 +391,00:00:13.0830000,0.5125652,0.5125652,0,0,0.691,14,0,14,2,1 +392,00:00:13.1160000,0.5125652,0.5125652,0,0,0.691,14,0,14,2,1 +393,00:00:13.1500000,0.5128487,1.0254139,0,0,0.691,13,0,13,2,1 +394,00:00:13.1830000,0.51410764,1.5395215,0,0,0.691,12,0,12,2,1 +395,00:00:13.2160000,0.5152703,2.0547917,0,0,0.691,12,0,12,2,1 +396,00:00:13.2500000,0.5172471,2.5720387,0,0,0.691,12,0,12,2,1 +397,00:00:13.2830000,0.5172471,2.5720387,0,0,0.691,12,0,12,2,1 +398,00:00:13.3160000,0.50842345,3.080462,0,0,0.691,12,0,12,2,1 +399,00:00:13.3500000,0.52083623,3.6012983,0,0,0.691,12,0,12,2,1 +400,00:00:13.3830000,0.5082726,4.109571,0,0,0.691,11,0,11,2,1 +401,00:00:13.4160000,0.5209009,4.6304717,0,0,0.691,10,0,10,2,1 +402,00:00:13.4500000,0.5209009,4.6304717,0,0,0.691,10,0,10,2,1 +403,00:00:13.4830000,0.514227,5.1446986,0,0,0.691,10,0,10,2,1 +404,00:00:13.5160000,0.51069766,5.6553965,0,0,0.691,10,0,10,2,1 +405,00:00:13.5500000,0.52293533,6.178332,0,0,0.69,9,0,9,2,1 +406,00:00:13.5830000,0.5085674,6.686899,0,0,0.691,8,0,8,2,1 +407,00:00:13.6160000,0.5085674,6.686899,0,0,0.691,8,0,8,2,1 +408,00:00:13.6500000,0.5149997,7.201899,0,0,0.691,7,0,7,2,1 +409,00:00:13.6830000,0.5159983,7.7178974,0,0,0.691,7,0,7,2,1 +410,00:00:13.7160000,0.50953895,8.227436,0,0,0.691,6,0,6,0,1 +411,00:00:13.7500000,0.5137763,8.741213,0,0,0.691,5,0,5,0,1 +412,00:00:13.7830000,0.5137763,8.741213,0,0,0.691,5,0,5,0,1 +413,00:00:13.8160000,0.5141588,9.255372,0,0,0.691,5,0,5,0,1 +414,00:00:13.8500000,0.51834106,9.773713,0,0,0.691,4,0,4,0,1 +415,00:00:13.8830000,0.526149,10.299862,0,0,0.69,3,0,3,0,1 +416,00:00:13.9160000,0.51813257,10.817994,0,0,0.691,2,0,2,0,1 +417,00:00:13.9500000,0.51813257,10.817994,0,0,0.691,2,0,2,0,1 +418,00:00:13.9830000,0.5157707,11.333765,0,0,0.691,2,0,2,0,1 +419,00:00:14.0160000,0.51443475,11.8482,0,0,0.692,2,0,2,0,1 +420,00:00:14.0500000,0.5163526,12.3645525,0,0,0.69,1,0,1,0,1 +421,00:00:14.0830000,0.5258546,12.890408,0,0,0.69,0,0,0,0,1 +422,00:00:14.1160000,0.5258546,12.890408,0,0,0.69,0,0,0,0,1 +423,00:00:14.1500000,0.5105041,13.400911,0,0,0.691,0,0,0,0,1 +424,00:00:14.1830000,0.5251086,13.92602,0,0,0.691,0,0,0,0,1 +425,00:00:14.2160000,0.5103357,14.436356,0,0,0.69,0,0,0,0,1 +426,00:00:14.2500000,0.5253174,14.961673,0,0,0.69,0,0,0,0,1 +427,00:00:14.2830000,0.5253174,14.961673,0,0,0.69,0,0,0,0,1 +428,00:00:14.3160000,0.51308817,15.474761,0,0,0.691,0,0,0,0,1 +429,00:00:14.3500000,0.51803184,15.992793,0,0,0.692,0,0,0,0,1 +430,00:00:14.3830000,0.51692927,16.509722,0,0,0.69,0,0,0,0,1 +431,00:00:14.4160000,0.5189104,17.028633,0,0,0.692,0,0,0,0,1 +432,00:00:14.4500000,0.5189104,17.028633,0,0,0.692,0,0,0,0,1 +433,00:00:14.4830000,0.5055475,17.53418,0,0,0.692,0,0,0,0,1 +434,00:00:14.5160000,0.51809597,18.052277,0,0,0.692,0,0,0,0,1 +435,00:00:14.5500000,0.5149442,18.56722,0,0,0.691,0,0,0,0,1 +436,00:00:14.5830000,0.5132325,19.080454,0,0,0.692,0,0,0,0,1 +437,00:00:14.6160000,0.5132325,19.080454,0,0,0.692,0,0,0,0,1 +438,00:00:14.6500000,0.52048206,19.600937,0,0,0.693,0,0,0,0,1 +439,00:00:14.6830000,0.52118516,20.122122,0,0,0.693,0,0,0,0,1 +440,00:00:14.7160000,0.53252155,20.654644,0,0,0.692,0,0,0,0,1 +441,00:00:14.7500000,0.5215314,21.176176,0,0,0.692,0,0,0,0,1 +442,00:00:14.7830000,0.5215314,21.176176,0,0,0.692,0,0,0,0,1 +443,00:00:14.8160000,0.515671,21.691847,0,0,0.691,0,0,0,0,1 +444,00:00:14.8500000,0.52418655,22.216034,0,0,0.692,0,0,0,0,1 +445,00:00:14.8830000,0.519223,22.735256,0,0,0.691,0,0,0,0,1 +446,00:00:14.9160000,0.5281284,23.263384,0,0,0.692,0,0,0,0,1 +447,00:00:14.9500000,0.5281284,23.263384,0,0,0.692,0,0,0,0,1 +448,00:00:14.9830000,0.51339555,23.77678,0,0,0.692,0,0,0,0,1 +449,00:00:15.0160000,0.5198091,24.296589,0,0,0.692,0,0,0,0,1 +450,00:00:15.0500000,0.51741296,24.814001,0,0,0.692,0,0,0,0,1 +451,00:00:15.0830000,0.52485645,24.826292,0,0,0.692,0,0,0,0,1 +452,00:00:15.1160000,0.52485645,24.826292,0,0,0.692,0,0,0,0,1 +453,00:00:15.1500000,0.51763433,25.343925,0,0,0.693,0,0,0,0,1 +454,00:00:15.1830000,0.5286432,25.872568,0,0,0.693,0,0,0,0,1 +455,00:00:15.2160000,0.5235563,26.396124,0,0,0.692,0,0,0,0,1 +456,00:00:15.2500000,0.52551013,26.921635,0,0,0.693,0,0,0,0,1 +457,00:00:15.2830000,0.52551013,26.921635,0,0,0.693,0,0,0,0,1 +458,00:00:15.3160000,0.5172158,27.43885,0,0,0.693,0,0,0,0,1 +459,00:00:15.3500000,0.5249064,27.963757,0,0,0.693,0,0,0,0,1 +460,00:00:15.3830000,0.52232873,28.486086,0,0,0.692,0,0,0,0,1 +461,00:00:15.4160000,0.5268086,29.012894,0,0,0.692,0,0,0,0,1 +462,00:00:15.4500000,0.5268086,29.012894,0,0,0.692,0,0,0,0,1 +463,00:00:15.4830000,0.5149262,29.52782,0,0,0.692,0,0,0,0,1 +464,00:00:15.5160000,0.51259613,30.040417,0,0,0.693,0,0,0,0,1 +465,00:00:15.5500000,0.51918745,30.559605,0,0,0.692,0,0,0,0,1 +466,00:00:15.5830000,0.5181972,31.077803,0,0,0.693,0,0,0,0,1 +467,00:00:15.6160000,0.5181972,31.077803,0,0,0.693,0,0,0,0,1 +468,00:00:15.6500000,0.51732826,31.59513,0,0,0.693,0,0,0,0,1 +469,00:00:15.6830000,-0.519989,-0.519989,0,0,0.693,1,0,1,0,1 +470,00:00:15.7160000,0.5196648,0.5196648,0,0,0.692,2,0,2,0,1 +471,00:00:15.7500000,0.51892966,1.0385945,0,0,0.693,2,0,2,0,1 +472,00:00:15.7830000,0.51892966,1.0385945,0,0,0.693,2,0,2,0,1 +473,00:00:15.8160000,0.51825863,1.556853,0,0,0.692,2,0,2,0,1 +474,00:00:15.8500000,0.5199121,2.076765,0,0,0.693,2,0,2,0,1 +475,00:00:15.8830000,0.5301945,2.6069596,0,0,0.692,2,0,2,0,1 +476,00:00:15.9160000,0.5244753,3.131435,0,0,0.693,2,0,2,0,1 +477,00:00:15.9500000,0.5244753,3.131435,0,0,0.694,2,0,2,0,1 +478,00:00:15.9830000,0.52341944,3.6548543,0,0,0.693,2,0,2,0,1 +479,00:00:16.0160000,-0.5237637,-0.5237637,0,0,0.693,3,0,3,0,1 +480,00:00:16.0500000,0.53235346,0.53235346,0,0,0.692,4,0,4,0,1 +481,00:00:16.0830000,0.527908,1.0602615,0,0,0.693,4,0,4,0,1 +482,00:00:16.1160000,0.527908,1.0602615,0,0,0.693,4,0,4,0,1 +483,00:00:16.1500000,0.5248332,1.5850947,0,0,0.692,4,0,4,0,1 +484,00:00:16.1830000,0.52249485,2.1075895,0,0,0.693,4,0,4,0,1 +485,00:00:16.2160000,0.5253266,2.632916,0,0,0.692,4,0,4,0,1 +486,00:00:16.2500000,0.53244376,3.1653597,0,0,0.692,4,0,4,0,1 +487,00:00:16.2830000,0.53244376,3.1653597,0,0,0.692,4,0,4,0,1 +488,00:00:16.3160000,0.527775,3.6931348,0,0,0.692,4,0,4,0,1 +489,00:00:16.3500000,0.53215384,4.2252884,0,0,0.692,4,0,4,0,1 +490,00:00:16.3830000,0.5286385,4.7539268,0,0,0.692,4,0,4,0,1 +491,00:00:16.4160000,0.5229561,5.2768826,0,0,0.693,4,0,4,0,1 +492,00:00:16.4500000,0.5229561,5.2768826,0,0,0.693,4,0,4,0,1 +493,00:00:16.4830000,0.5204545,5.797337,0,0,0.693,4,0,4,0,1 +494,00:00:16.5160000,0.5286368,6.325974,0,0,0.693,4,0,4,0,1 +495,00:00:16.5500000,0.5217291,6.847703,0,0,0.692,4,0,4,0,1 +496,00:00:16.5830000,0.5210862,7.368789,0,0,0.692,4,0,4,0,1 +497,00:00:16.6160000,0.5210862,7.368789,0,0,0.692,4,0,4,0,1 +498,00:00:16.6500000,0.51772535,7.8865147,0,0,0.692,4,0,4,0,1 +499,00:00:16.6830000,-0.5167276,-0.5167276,0,0,0.693,4,0,4,0,1 +500,00:00:16.7160000,0.51525825,0.51525825,0,0,0.693,4,0,4,0,1 +501,00:00:16.7500000,0.5321907,1.0474489,0,0,0.693,4,0,4,0,1 +502,00:00:16.7830000,0.5321907,1.0474489,0,0,0.693,4,0,4,0,1 +503,00:00:16.8160000,0.51853377,1.5659826,0,0,0.692,4,0,4,0,1 +504,00:00:16.8500000,0.5240952,2.0900779,0,0,0.693,4,0,4,0,1 +505,00:00:16.8830000,0.5229332,2.6130111,0,0,0.693,4,0,4,0,1 +506,00:00:16.9160000,0.5229332,2.6130111,0,0,0.693,4,0,4,0,1 +507,00:00:16.9500000,0.534086,3.147097,0,0,0.694,4,0,4,0,1 +508,00:00:16.9830000,0.53293437,3.6800315,0,0,0.693,4,0,4,0,1 +509,00:00:17.0160000,0.53106683,4.211098,0,0,0.694,3,0,3,0,1 +510,00:00:17.0500000,0.5258798,4.736978,0,0,0.693,2,0,2,0,1 +511,00:00:17.0830000,0.5318489,5.268827,0,0,0.692,2,0,2,0,1 +512,00:00:17.1160000,0.5318489,5.268827,0,0,0.692,2,0,2,0,1 +513,00:00:17.1500000,0.52496636,5.793793,0,0,0.692,2,0,2,0,1 +514,00:00:17.1830000,0.52095246,6.314746,0,0,0.692,2,0,2,0,1 +515,00:00:17.2160000,0.5246072,6.839353,0,0,0.693,2,0,2,0,1 +516,00:00:17.2500000,0.5244412,7.3637943,0,0,0.693,2,0,2,0,1 +517,00:00:17.2830000,0.5244412,7.3637943,0,0,0.693,2,0,2,0,1 +518,00:00:17.3160000,0.527788,7.8915825,0,0,0.693,2,0,2,0,1 +519,00:00:17.3500000,0.52880305,8.420385,0,0,0.693,2,0,2,0,1 +520,00:00:17.3830000,0.5270982,8.947483,0,0,0.693,2,0,2,0,1 +521,00:00:17.4160000,0.5270982,8.947483,0,0,0.693,2,0,2,0,1 +522,00:00:17.4500000,-0.52014685,-0.52014685,0,0,0.694,3,0,3,0,1 +523,00:00:17.4830000,0.52832127,0.52832127,0,0,0.693,4,0,4,0,1 +524,00:00:17.5160000,0.52399874,1.05232,0,0,0.694,4,0,4,0,1 +525,00:00:17.5500000,0.53711504,1.5894351,0,0,0.693,4,0,4,0,1 +526,00:00:17.5830000,0.53711504,1.5894351,0,0,0.693,4,0,4,0,1 +527,00:00:17.6160000,0.5261403,2.1155753,0,0,0.692,4,0,4,0,1 +528,00:00:17.6500000,0.5225828,2.638158,0,0,0.692,4,0,4,0,1 +529,00:00:17.6830000,-0.52443016,-0.52443016,0,0,0.693,4,0,4,0,1 +530,00:00:17.7160000,0.5265886,0.5265886,0,0,0.693,4,0,4,0,1 +531,00:00:17.7500000,0.5265886,0.5265886,0,0,0.693,4,0,4,0,1 +532,00:00:17.7830000,0.52460104,1.0511897,0,0,0.694,4,0,4,0,1 +533,00:00:17.8160000,0.526438,1.5776277,0,0,0.694,4,0,4,0,1 +534,00:00:17.8500000,0.52694017,2.1045678,0,0,0.694,4,0,4,0,1 +535,00:00:17.8830000,0.5241874,2.628755,0,0,0.694,4,0,4,0,1 +536,00:00:17.9160000,0.5241874,2.628755,0,0,0.694,4,0,4,0,1 +537,00:00:17.9500000,0.52501017,3.1537652,0,0,0.694,4,0,4,0,1 +538,00:00:17.9830000,0.52696,3.680725,0,0,0.694,4,0,4,0,1 +539,00:00:18.0160000,0.52589625,4.206621,0,0,0.694,4,0,4,0,1 +540,00:00:18.0500000,0.5343762,4.7409973,0,0,0.694,4,0,4,0,1 +541,00:00:18.0830000,0.5343762,4.7409973,0,0,0.694,4,0,4,0,1 +542,00:00:18.1160000,0.51854515,5.2595425,0,0,0.694,4,0,4,0,1 +543,00:00:18.1500000,0.52243394,5.781976,0,0,0.694,4,0,4,0,1 +544,00:00:18.1830000,0.5252205,6.3071966,0,0,0.694,4,0,4,0,1 +545,00:00:18.2160000,-0.52441025,-0.52441025,0,0,0.694,5,0,5,0,1 +546,00:00:18.2500000,-0.52441025,-0.52441025,0,0,0.694,5,0,5,0,1 +547,00:00:18.2830000,0.5241195,0.5241195,0,0,0.693,6,0,6,0,1 +548,00:00:18.3160000,0.52555555,1.049675,0,0,0.693,6,0,6,0,1 +549,00:00:18.3500000,0.5226261,1.5723011,0,0,0.693,6,0,6,0,1 +550,00:00:18.3830000,0.5272629,2.099564,0,0,0.693,6,0,6,0,1 +551,00:00:18.4160000,0.5272629,2.099564,0,0,0.693,6,0,6,0,1 +552,00:00:18.4500000,0.5368449,2.636409,0,0,0.694,5,0,5,0,1 +553,00:00:18.4830000,0.53658223,3.1729913,0,0,0.693,4,0,4,0,1 +554,00:00:18.5160000,0.517539,3.6905303,0,0,0.693,4,0,4,0,1 +555,00:00:18.5500000,0.5306897,4.22122,0,0,0.693,4,0,4,0,1 +556,00:00:18.5830000,0.5306897,4.22122,0,0,0.693,4,0,4,0,1 +557,00:00:18.6160000,-0.53304034,-0.53304034,0,0,0.694,5,0,5,0,1 +558,00:00:18.6500000,0.52938044,0.52938044,0,0,0.694,6,0,6,0,1 +559,00:00:18.6830000,0.5328798,1.0622603,0,0,0.694,5,0,5,0,1 +560,00:00:18.7160000,0.5312526,1.5935129,0,0,0.694,4,0,4,0,1 +561,00:00:18.7500000,0.5312526,1.5935129,0,0,0.694,4,0,4,0,1 +562,00:00:18.7830000,0.530908,2.124421,0,0,0.694,4,0,4,0,1 +563,00:00:18.8160000,0.5326938,2.6571147,0,0,0.694,4,0,4,0,1 +564,00:00:18.8500000,0.52437645,3.1814911,0,0,0.694,4,0,4,0,1 +565,00:00:18.8830000,0.5289705,3.7104616,0,0,0.694,4,0,4,0,1 +566,00:00:18.9160000,0.5289705,3.7104616,0,0,0.694,4,0,4,0,1 +567,00:00:18.9500000,0.5261961,4.2366576,0,0,0.694,4,0,4,1,1 +568,00:00:18.9830000,0.52810854,4.764766,0,0,0.694,4,0,4,1,1 +569,00:00:19.0160000,0.5304704,5.2952366,0,0,0.694,4,0,4,1,1 +570,00:00:19.0500000,0.53516096,5.8303976,0,0,0.694,4,0,4,1,1 +571,00:00:19.0830000,0.53516096,5.8303976,0,0,0.694,4,0,4,1,1 +572,00:00:19.1160000,0.5364267,6.366824,0,0,0.694,4,0,4,1,1 +573,00:00:19.1500000,0.5296635,6.8964877,0,0,0.694,4,0,4,1,1 +574,00:00:19.1830000,0.5309023,7.42739,0,0,0.693,4,0,4,1,1 +575,00:00:19.2160000,0.5396465,7.9670367,0,0,0.693,3,0,3,1,1 +576,00:00:19.2500000,0.5396465,7.9670367,0,0,0.693,3,0,3,1,1 +577,00:00:19.2830000,0.5349989,8.502035,0,0,0.693,2,0,2,1,1 +578,00:00:19.3160000,0.5360957,9.038131,0,0,0.693,2,0,2,1,1 +579,00:00:19.3500000,0.5243345,9.562466,0,0,0.694,2,0,2,1,1 +580,00:00:19.3830000,0.53795713,10.100423,0,0,0.693,2,0,2,1,1 +581,00:00:19.4160000,0.53795713,10.100423,0,0,0.693,2,0,2,1,1 +582,00:00:19.4500000,0.5283212,10.628744,0,0,0.693,2,0,2,1,1 +583,00:00:19.4830000,0.53486925,11.163613,0,0,0.694,2,0,2,1,1 +584,00:00:19.5160000,0.5269357,11.690549,0,0,0.694,2,0,2,1,1 +585,00:00:19.5500000,0.53196716,12.222516,0,0,0.694,2,0,2,1,1 +586,00:00:19.5830000,0.53196716,12.222516,0,0,0.694,2,0,2,1,1 +587,00:00:19.6160000,0.525764,12.74828,0,0,0.694,1,0,1,1,1 +588,00:00:19.6500000,0.531358,13.279637,0,0,0.693,0,0,0,1,1 +589,00:00:19.6830000,0.5330545,13.812692,0,0,0.693,0,0,0,1,1 +590,00:00:19.7160000,0.5339615,14.346653,0,0,0.693,0,0,0,1,1 +591,00:00:19.7500000,0.5339615,14.346653,0,0,0.693,0,0,0,1,1 +592,00:00:19.7830000,0.52943236,14.876085,0,0,0.694,0,0,0,1,1 +593,00:00:19.8160000,0.526485,15.402571,0,0,0.694,0,0,0,1,1 +594,00:00:19.8500000,0.5182843,15.920855,0,0,0.694,0,0,0,1,1 +595,00:00:19.8830000,0.5295525,16.450407,0,0,0.694,0,0,0,1,1 +596,00:00:19.9160000,0.5295525,16.450407,0,0,0.694,0,0,0,1,1 +597,00:00:19.9500000,0.5302412,16.980648,0,0,0.693,0,0,0,1,1 +598,00:00:19.9830000,0.5215253,17.502172,0,0,0.694,0,0,0,1,1 +599,00:00:20.0160000,-0.54467535,-0.54467535,0,0,0.694,1,0,1,1,1 +600,00:00:20.0500000,0.52327025,0.52327025,0,0,0.694,2,0,2,1,1 +601,00:00:20.0830000,-0.023799114,-0.023799114,0,0,0.693,2,0,2,1,1 +602,00:00:20.1160000,-0.52352244,-0.54732156,0,0,0.692,3,0,3,1,1 +603,00:00:20.1500000,0.52455074,0.52455074,0,0,0.693,4,0,4,1,1 +604,00:00:20.1830000,-0.532604,-0.532604,0,0,0.693,5,0,5,1,1 +605,00:00:20.2160000,0.534751,0.534751,0,0,0.694,6,0,6,1,1 +606,00:00:20.2500000,0.534751,0.534751,0,0,0.694,6,0,6,1,1 +607,00:00:20.2830000,-0.5258177,-0.5258177,0,0,0.693,7,0,7,2,1 +608,00:00:20.3160000,0.5345538,0.5345538,0,0,0.694,8,0,8,2,1 +609,00:00:20.3500000,0.52968895,1.0642428,0,0,0.694,8,0,8,2,1 +610,00:00:20.3830000,0.53828853,1.6025314,0,0,0.694,8,0,8,2,1 +611,00:00:20.4160000,0.53828853,1.6025314,0,0,0.694,8,0,8,2,1 +612,00:00:20.4500000,-0.52759045,-0.52759045,0,0,0.694,9,0,9,2,1 +613,00:00:20.4830000,0.53190887,0.53190887,0,0,0.695,10,0,10,2,1 +614,00:00:20.5160000,0.5297117,1.0616206,0,0,0.694,10,0,10,2,1 +615,00:00:20.5500000,0.528888,1.5905086,0,0,0.694,10,0,10,2,1 +616,00:00:20.5830000,0.528888,1.5905086,0,0,0.694,10,0,10,2,1 +617,00:00:20.6160000,0.5216895,2.112198,0,0,0.694,10,0,10,2,1 +618,00:00:20.6500000,0.52529097,2.637489,0,0,0.693,10,0,10,2,1 +619,00:00:20.6830000,0.5176582,3.1551473,0,0,0.694,10,0,10,2,1 +620,00:00:20.7160000,0.53474706,3.6898944,0,0,0.693,10,0,10,2,1 +621,00:00:20.7500000,0.53474706,3.6898944,0,0,0.693,10,0,10,2,1 +622,00:00:20.7830000,0.53162354,4.221518,0,0,0.693,10,0,10,2,1 +623,00:00:20.8160000,0.5242377,4.7457557,0,0,0.694,10,0,10,2,1 +624,00:00:20.8500000,0.52124935,5.267005,0,0,0.694,10,0,10,2,1 +625,00:00:20.8830000,0.5327109,5.799716,0,0,0.693,10,0,10,2,1 +626,00:00:20.9160000,0.5327109,5.799716,0,0,0.693,10,0,10,2,1 +627,00:00:20.9500000,0.52327895,6.322995,0,0,0.693,10,0,10,2,1 +628,00:00:20.9830000,0.5277515,6.8507466,0,0,0.694,10,0,10,2,1 +629,00:00:21.0160000,0.53099,7.3817368,0,0,0.694,9,0,9,2,1 +630,00:00:21.0500000,-0.5262865,-0.5262865,0,0,0.694,9,0,9,2,1 +631,00:00:21.0830000,-0.5262865,-0.5262865,0,0,0.694,9,0,9,2,1 +632,00:00:21.1160000,0.5261131,0.5261131,0,0,0.693,9,0,9,2,1 +633,00:00:21.1500000,-0.5278999,-0.5278999,0,0,0.693,9,0,9,2,1 +634,00:00:21.1830000,0.52194595,0.52194595,0,0,0.694,9,0,9,2,1 +635,00:00:21.2160000,0.5333221,1.055268,0,0,0.693,8,0,8,2,1 +636,00:00:21.2500000,0.5333221,1.055268,0,0,0.693,8,0,8,2,1 +637,00:00:21.2830000,0.5226034,1.5778714,0,0,0.694,7,0,7,2,1 +638,00:00:21.3160000,-0.525948,-0.525948,0,0,0.693,7,0,7,2,1 +639,00:00:21.3500000,0.5161867,0.5161867,0,0,0.693,8,0,8,2,1 +640,00:00:21.3830000,0.5111425,1.0273292,0,0,0.693,8,0,8,2,1 +641,00:00:21.4160000,0.5111425,1.0273292,0,0,0.693,8,0,8,2,1 +642,00:00:21.4500000,0.5139977,1.5413269,0,0,0.692,7,0,7,2,1 +643,00:00:21.4830000,-0.5228652,-0.5228652,0,0,0.693,7,0,7,2,1 +644,00:00:21.5160000,0.52714986,0.52714986,0,0,0.693,8,0,8,2,1 +645,00:00:21.5500000,0.527955,1.0551049,0,0,0.693,8,0,8,2,1 +646,00:00:21.5830000,0.527955,1.0551049,0,0,0.693,8,0,8,2,1 +647,00:00:21.6160000,0.52423245,1.5793374,0,0,0.692,8,0,8,2,1 +648,00:00:21.6500000,0.5233963,2.1027336,0,0,0.693,8,0,8,2,1 +649,00:00:21.6830000,0.5221746,2.6249082,0,0,0.693,8,0,8,2,1 +650,00:00:21.7160000,0.52910924,3.1540174,0,0,0.693,8,0,8,2,1 +651,00:00:21.7500000,0.52910924,3.1540174,0,0,0.693,8,0,8,2,1 +652,00:00:21.7830000,0.5257531,3.6797705,0,0,0.693,8,0,8,2,1 +653,00:00:21.8160000,0.5273288,4.2070994,0,0,0.694,8,0,8,2,1 +654,00:00:21.8500000,-0.52306974,-0.52306974,0,0,0.694,9,0,9,2,1 +655,00:00:21.8830000,0.5319676,0.5319676,0,0,0.693,10,0,10,2,1 +656,00:00:21.9160000,0.5319676,0.5319676,0,0,0.693,10,0,10,2,1 +657,00:00:21.9500000,0.5210241,1.0529916,0,0,0.693,10,0,10,2,1 +658,00:00:21.9830000,0.53028184,1.5832734,0,0,0.694,10,0,10,2,1 +659,00:00:22.0160000,0.51806414,2.1013374,0,0,0.694,10,0,10,2,1 +660,00:00:22.0500000,0.53260756,2.633945,0,0,0.694,9,0,9,2,1 +661,00:00:22.0830000,0.53260756,2.633945,0,0,0.694,9,0,9,2,1 +662,00:00:22.1160000,0.53143543,3.1653805,0,0,0.694,8,0,8,2,1 +663,00:00:22.1500000,0.52333707,3.6887176,0,0,0.693,7,0,7,2,1 +664,00:00:22.1830000,0.52499634,4.213714,0,0,0.693,6,0,6,1,1 +665,00:00:22.2160000,0.53176767,4.745482,0,0,0.694,6,0,6,1,1 +666,00:00:22.2500000,0.53176767,4.745482,0,0,0.694,6,0,6,1,1 +667,00:00:22.2830000,0.52253324,5.2680154,0,0,0.694,6,0,6,1,1 +668,00:00:22.3160000,0.5254913,5.7935066,0,0,0.694,5,0,5,1,1 +669,00:00:22.3500000,-0.52331996,-0.52331996,0,0,0.694,5,0,5,1,1 +670,00:00:22.3830000,0.5284843,0.5284843,0,0,0.694,6,0,6,1,1 +671,00:00:22.4160000,0.5284843,0.5284843,0,0,0.694,6,0,6,1,1 +672,00:00:22.4500000,-0.5213319,-0.5213319,0,0,0.693,7,0,7,2,1 +673,00:00:22.4830000,-0.52968967,-1.0510216,0,0,0.695,6,0,6,1,1 +674,00:00:22.5160000,0.5266052,0.5266052,0,0,0.694,6,0,6,1,1 +675,00:00:22.5500000,0.5299927,1.056598,0,0,0.694,6,0,6,1,1 +676,00:00:22.5830000,0.5299927,1.056598,0,0,0.694,6,0,6,1,1 +677,00:00:22.6160000,0.52593523,1.5825331,0,0,0.694,6,0,6,1,1 +678,00:00:22.6500000,0.52509415,2.1076274,0,0,0.695,6,0,6,1,1 +679,00:00:22.6830000,0.52029,2.6279173,0,0,0.695,6,0,6,1,1 +680,00:00:22.7160000,0.539762,3.1676793,0,0,0.695,6,0,6,1,1 +681,00:00:22.7500000,0.539762,3.1676793,0,0,0.695,6,0,6,1,1 +682,00:00:22.7830000,0.5216955,3.689375,0,0,0.695,6,0,6,1,1 +683,00:00:22.8160000,-0.52082616,-0.52082616,0,0,0.694,7,0,7,2,1 +684,00:00:22.8500000,0.52299887,0.52299887,0,0,0.693,7,0,7,2,1 +685,00:00:22.8830000,-0.5208356,-0.5208356,0,0,0.694,7,0,7,2,1 +686,00:00:22.9160000,-0.5208356,-0.5208356,0,0,0.694,7,0,7,2,1 +687,00:00:22.9500000,0.5231648,0.5231648,0,0,0.694,8,0,8,2,1 +688,00:00:22.9830000,0.539507,1.0626718,0,0,0.693,8,0,8,2,1 +689,00:00:23.0160000,0.5154656,1.5781374,0,0,0.694,8,0,8,2,1 +690,00:00:23.0500000,0.52228755,2.100425,0,0,0.694,8,0,8,2,1 +691,00:00:23.0830000,0.52228755,2.100425,0,0,0.694,8,0,8,2,1 +692,00:00:23.1160000,0.5346509,2.635076,0,0,0.693,8,0,8,2,1 +693,00:00:23.1500000,-0.526601,-0.526601,0,0,0.693,9,0,9,2,1 +694,00:00:23.1830000,0.52230823,0.52230823,0,0,0.694,10,0,10,2,1 +695,00:00:23.2160000,0.5302448,1.052553,0,0,0.693,10,0,10,2,1 +696,00:00:23.2500000,0.5302448,1.052553,0,0,0.693,10,0,10,2,1 +697,00:00:23.2830000,0.5251182,1.5776713,0,0,0.693,10,0,10,2,1 +698,00:00:23.3160000,0.52422005,2.1018913,0,0,0.693,10,0,10,2,1 +699,00:00:23.3500000,0.5187251,2.6206164,0,0,0.693,9,0,9,2,1 +700,00:00:23.3830000,0.53050286,3.1511192,0,0,0.694,8,0,8,2,1 +701,00:00:23.4160000,0.53050286,3.1511192,0,0,0.694,8,0,8,2,1 +702,00:00:23.4500000,0.5271487,3.678268,0,0,0.693,7,0,7,2,1 +703,00:00:23.4830000,0.52418655,4.2024546,0,0,0.694,7,0,7,2,1 +704,00:00:23.5160000,0.52857095,4.7310257,0,0,0.694,6,0,6,1,1 +705,00:00:23.5500000,0.533529,5.2645545,0,0,0.694,6,0,6,1,1 +706,00:00:23.5830000,0.533529,5.2645545,0,0,0.694,6,0,6,1,1 +707,00:00:23.6160000,0.5242514,5.788806,0,0,0.692,6,0,6,1,1 +708,00:00:23.6500000,0.53130245,6.3201084,0,0,0.694,6,0,6,1,1 +709,00:00:23.6830000,0.5245283,6.844637,0,0,0.694,6,0,6,1,1 +710,00:00:23.7160000,0.5333147,7.3779516,0,0,0.694,6,0,6,1,1 +711,00:00:23.7500000,0.5333147,7.3779516,0,0,0.694,6,0,6,1,1 +712,00:00:23.7830000,0.5301077,7.908059,0,0,0.694,6,0,6,1,1 +713,00:00:23.8160000,0.5313759,8.439435,0,0,0.693,5,0,5,1,1 +714,00:00:23.8500000,0.54019403,8.9796295,0,0,0.692,4,0,4,1,1 +715,00:00:23.8830000,0.5338859,9.513515,0,0,0.693,3,0,3,1,1 +716,00:00:23.9160000,0.5338859,9.513515,0,0,0.693,3,0,3,1,1 +717,00:00:23.9500000,0.536056,10.049571,0,0,0.693,2,0,2,1,1 +718,00:00:23.9830000,0.52551824,10.575089,0,0,0.694,2,0,2,1,1 +719,00:00:24.0160000,0.5281187,11.103209,0,0,0.693,2,0,2,1,1 +720,00:00:24.0500000,0.53067213,11.633881,0,0,0.694,2,0,2,1,1 +721,00:00:24.0830000,0.53067213,11.633881,0,0,0.694,2,0,2,1,1 +722,00:00:24.1160000,0.5272499,12.161131,0,0,0.693,2,0,2,1,1 +723,00:00:24.1500000,0.5289703,12.690102,0,0,0.693,1,0,1,1,1 +724,00:00:24.1830000,0.5275875,13.2176895,0,0,0.693,0,0,0,1,1 +725,00:00:24.2160000,0.5303976,13.748087,0,0,0.693,0,0,0,1,1 +726,00:00:24.2500000,0.5303976,13.748087,0,0,0.693,0,0,0,1,1 +727,00:00:24.2830000,0.5347056,14.282792,0,0,0.693,0,0,0,1,1 +728,00:00:24.3160000,0.52359366,14.806386,0,0,0.694,0,0,0,1,1 +729,00:00:24.3500000,0.5413892,15.347775,0,0,0.693,0,0,0,1,1 +730,00:00:24.3830000,0.534254,15.88203,0,0,0.693,0,0,0,1,1 +731,00:00:24.4160000,0.534254,15.88203,0,0,0.693,0,0,0,1,1 +732,00:00:24.4500000,0.5335448,16.415575,0,0,0.693,0,0,0,1,1 +733,00:00:24.4830000,0.5327397,16.948315,0,0,0.694,0,0,0,1,1 +734,00:00:24.5160000,0.5361263,17.484442,0,0,0.694,0,0,0,1,1 +735,00:00:24.5500000,0.5259041,18.010345,0,0,0.694,0,0,0,1,1 +736,00:00:24.5830000,0.5259041,18.010345,0,0,0.694,0,0,0,1,1 +737,00:00:24.6160000,-0.52792805,-0.52792805,0,0,0.694,1,0,1,1,1 +738,00:00:24.6500000,-0.52904224,-1.0569704,0,0,0.693,1,0,1,1,1 +739,00:00:24.6830000,0.5266105,0.5266105,0,0,0.693,2,0,2,1,1 +740,00:00:24.7160000,0.52938277,1.0559933,0,0,0.693,2,0,2,1,1 +741,00:00:24.7500000,0.52938277,1.0559933,0,0,0.693,2,0,2,1,1 +742,00:00:24.7830000,0.5205044,1.5764978,0,0,0.693,2,0,2,1,1 +743,00:00:24.8160000,0.5249455,2.1014433,0,0,0.692,2,0,2,1,1 +744,00:00:24.8500000,0.52693385,2.6283772,0,0,0.692,2,0,2,1,1 +745,00:00:24.8830000,0.5243875,3.1527648,0,0,0.693,2,0,2,1,1 +746,00:00:24.9160000,0.5243875,3.1527648,0,0,0.693,2,0,2,1,1 +747,00:00:24.9500000,0.52302533,3.67579,0,0,0.692,2,0,2,1,1 +748,00:00:24.9830000,0.523791,4.199581,0,0,0.693,2,0,2,1,1 +749,00:00:25.0160000,0.525749,4.7253304,0,0,0.692,2,0,2,1,1 +750,00:00:25.0500000,0.5269517,5.252282,0,0,0.693,2,0,2,1,1 +751,00:00:25.0830000,0.5269517,5.252282,0,0,0.693,2,0,2,1,1 +752,00:00:25.1160000,0.5284277,5.7807097,0,0,0.692,2,0,2,1,1 +753,00:00:25.1500000,0.5266438,6.3073535,0,0,0.692,2,0,2,1,1 +754,00:00:25.1830000,0.52638817,6.8337417,0,0,0.692,2,0,2,1,1 +755,00:00:25.2160000,0.5186682,7.35241,0,0,0.692,2,0,2,1,1 +756,00:00:25.2500000,0.5186682,7.35241,0,0,0.692,2,0,2,1,1 +757,00:00:25.2830000,0.5381339,7.890544,0,0,0.692,2,0,2,1,1 +758,00:00:25.3160000,0.53534245,8.425886,0,0,0.692,2,0,2,1,1 +759,00:00:25.3500000,0.5304532,8.956339,0,0,0.692,2,0,2,1,1 +760,00:00:25.3830000,0.53116846,9.487507,0,0,0.692,2,0,2,1,1 +761,00:00:25.4160000,0.53116846,9.487507,0,0,0.692,2,0,2,1,1 +762,00:00:25.4500000,0.531232,10.018739,0,0,0.692,2,0,2,1,1 +763,00:00:25.4830000,-0.53124434,-0.53124434,0,0,0.693,3,0,3,1,1 +764,00:00:25.5160000,0.5281724,0.5281724,0,0,0.693,4,0,4,1,1 +765,00:00:25.5500000,0.5286286,1.056801,0,0,0.694,4,0,4,1,1 +766,00:00:25.5830000,0.5286286,1.056801,0,0,0.694,4,0,4,1,1 +767,00:00:25.6160000,0.52665025,1.5834513,0,0,0.693,3,0,3,1,1 +768,00:00:25.6500000,0.5199793,2.1034305,0,0,0.694,3,0,3,1,1 +769,00:00:25.6830000,0.52125126,2.6246817,0,0,0.693,2,0,2,1,1 +770,00:00:25.7160000,0.52125126,2.6246817,0,0,0.693,2,0,2,1,1 +771,00:00:25.7500000,0.53395605,3.1586378,0,0,0.693,2,0,2,1,1 +772,00:00:25.7830000,0.54086196,3.6994996,0,0,0.693,2,0,2,1,1 +773,00:00:25.8160000,0.5271416,4.226641,0,0,0.694,2,0,2,1,1 +774,00:00:25.8500000,0.5304782,4.757119,0,0,0.693,2,0,2,1,1 +775,00:00:25.8830000,0.5304782,4.757119,0,0,0.693,2,0,2,1,1 +776,00:00:25.9160000,0.52778053,5.2848997,0,0,0.694,2,0,2,1,1 +777,00:00:25.9500000,0.53924006,5.8241396,0,0,0.694,2,0,2,1,1 +778,00:00:25.9830000,0.53603166,6.3601713,0,0,0.694,2,0,2,1,1 +779,00:00:26.0160000,0.5321781,6.8923492,0,0,0.693,2,0,2,1,1 +780,00:00:26.0500000,0.5321781,6.8923492,0,0,0.693,2,0,2,1,1 +781,00:00:26.0830000,0.530514,7.422863,0,0,0.693,2,0,2,1,1 +782,00:00:26.1160000,0.5344595,7.9573226,0,0,0.693,2,0,2,1,1 +783,00:00:26.1500000,0.53855866,8.495881,0,0,0.694,2,0,2,1,1 +784,00:00:26.1830000,0.53724784,9.033129,0,0,0.693,2,0,2,1,1 +785,00:00:26.2160000,0.53724784,9.033129,0,0,0.693,2,0,2,1,1 +786,00:00:26.2500000,0.5371279,9.570256,0,0,0.693,2,0,2,1,1 +787,00:00:26.2830000,0.53376305,10.104019,0,0,0.694,2,0,2,1,1 +788,00:00:26.3160000,0.53086036,10.634879,0,0,0.694,2,0,2,1,1 +789,00:00:26.3500000,0.53899264,11.173872,0,0,0.693,2,0,2,1,1 +790,00:00:26.3830000,0.53899264,11.173872,0,0,0.693,2,0,2,1,1 +791,00:00:26.4160000,0.53057265,11.704445,0,0,0.694,2,0,2,1,1 +792,00:00:26.4500000,0.53761995,12.242064,0,0,0.694,2,0,2,1,1 +793,00:00:26.4830000,0.5333219,12.775387,0,0,0.694,1,0,1,1,1 +794,00:00:26.5160000,0.5335379,13.308925,0,0,0.693,0,0,0,1,1 +795,00:00:26.5500000,0.5335379,13.308925,0,0,0.693,0,0,0,1,1 +796,00:00:26.5830000,0.53297406,13.841899,0,0,0.693,0,0,0,1,1 +797,00:00:26.6160000,0.5360921,14.377991,0,0,0.694,0,0,0,1,1 +798,00:00:26.6500000,0.52839464,14.906385,0,0,0.694,0,0,0,1,1 +799,00:00:26.6830000,0.5362065,15.442592,0,0,0.694,0,0,0,1,1 +800,00:00:26.7160000,0.5362065,15.442592,0,0,0.694,0,0,0,1,1 +801,00:00:26.7500000,0.5277689,15.970361,0,0,0.694,0,0,0,1,1 +802,00:00:26.7830000,0.52736163,16.497723,0,0,0.694,0,0,0,1,1 +803,00:00:26.8160000,0.54145086,17.039173,0,0,0.693,0,0,0,1,1 +804,00:00:26.8500000,0.53337723,17.57255,0,0,0.693,0,0,0,1,1 +805,00:00:26.8830000,0.53337723,17.57255,0,0,0.693,0,0,0,1,1 +806,00:00:26.9160000,0.519741,18.09229,0,0,0.693,0,0,0,1,1 +807,00:00:26.9500000,0.52822155,18.620512,0,0,0.693,0,0,0,1,1 +808,00:00:26.9830000,0.52555144,19.146063,0,0,0.693,0,0,0,1,1 +809,00:00:27.0160000,0.5384685,19.684532,0,0,0.693,0,0,0,1,1 +810,00:00:27.0500000,0.5384685,19.684532,0,0,0.693,0,0,0,1,1 +811,00:00:27.0830000,-0.52606547,-0.52606547,0,0,0.692,1,0,1,1,1 +812,00:00:27.1160000,0.5323288,0.5323288,0,0,0.693,2,0,2,1,1 +813,00:00:27.1500000,0.5284539,1.0607827,0,0,0.694,2,0,2,1,1 +814,00:00:27.1830000,0.5303309,1.5911136,0,0,0.693,2,0,2,1,1 +815,00:00:27.2160000,0.5303309,1.5911136,0,0,0.693,2,0,2,1,1 +816,00:00:27.2500000,0.5281718,2.1192853,0,0,0.694,2,0,2,1,1 +817,00:00:27.2830000,0.5213852,2.6406705,0,0,0.694,2,0,2,1,1 +818,00:00:27.3160000,0.5340764,3.174747,0,0,0.694,2,0,2,1,1 +819,00:00:27.3500000,0.5325789,3.707326,0,0,0.693,2,0,2,1,1 +820,00:00:27.3830000,0.5325789,3.707326,0,0,0.693,2,0,2,1,1 +821,00:00:27.4160000,0.5257892,4.233115,0,0,0.694,2,0,2,1,1 +822,00:00:27.4500000,0.52856326,4.7616787,0,0,0.694,2,0,2,1,1 +823,00:00:27.4830000,0.5334848,5.2951636,0,0,0.693,2,0,2,1,1 +824,00:00:27.5160000,0.5344309,5.8295946,0,0,0.693,2,0,2,1,1 +825,00:00:27.5500000,0.5344309,5.8295946,0,0,0.693,2,0,2,1,1 +826,00:00:27.5830000,0.5322046,6.3617992,0,0,0.694,2,0,2,1,1 +827,00:00:27.6160000,0.52911574,6.890915,0,0,0.694,2,0,2,1,1 +828,00:00:27.6500000,0.5319811,7.422896,0,0,0.693,2,0,2,1,1 +829,00:00:27.6830000,0.53307295,7.955969,0,0,0.693,2,0,2,1,1 +830,00:00:27.7160000,0.53307295,7.955969,0,0,0.693,2,0,2,1,1 +831,00:00:27.7500000,0.52129835,8.477267,0,0,0.694,2,0,2,1,1 +832,00:00:27.7830000,0.53503865,9.012306,0,0,0.694,2,0,2,1,1 +833,00:00:27.8160000,-0.52122545,-0.52122545,0,0,0.694,3,0,3,1,1 +834,00:00:27.8500000,0.5359854,0.5359854,0,0,0.693,4,0,4,1,1 +835,00:00:27.8830000,0.5359854,0.5359854,0,0,0.693,4,0,4,1,1 +836,00:00:27.9160000,0.52676326,1.0627487,0,0,0.693,4,0,4,1,1 +837,00:00:27.9500000,0.5323826,1.5951313,0,0,0.693,4,0,4,1,1 +838,00:00:27.9830000,0.5239387,2.11907,0,0,0.693,4,0,4,1,1 +839,00:00:28.0160000,0.5337669,2.652837,0,0,0.693,4,0,4,1,1 +840,00:00:28.0500000,0.5337669,2.652837,0,0,0.693,4,0,4,1,1 +841,00:00:28.0830000,0.521697,3.174534,0,0,0.693,3,0,3,1,1 +842,00:00:28.1160000,0.52793354,3.7024677,0,0,0.694,2,0,2,1,1 +843,00:00:28.1500000,0.51783746,4.220305,0,0,0.694,2,0,2,1,1 +844,00:00:28.1830000,0.5320279,4.7523327,0,0,0.693,2,0,2,1,1 +845,00:00:28.2160000,0.5320279,4.7523327,0,0,0.693,2,0,2,1,1 +846,00:00:28.2500000,0.52026635,5.272599,0,0,0.693,2,0,2,1,1 +847,00:00:28.2830000,0.53463715,5.807236,0,0,0.693,2,0,2,1,1 +848,00:00:28.3160000,0.5305197,6.337756,0,0,0.693,2,0,2,1,1 +849,00:00:28.3500000,0.53399557,6.871752,0,0,0.694,2,0,2,1,1 +850,00:00:28.3830000,0.53399557,6.871752,0,0,0.694,2,0,2,1,1 +851,00:00:28.4160000,0.52049136,7.3922434,0,0,0.694,2,0,2,1,1 +852,00:00:28.4500000,0.55095327,7.943197,0,0,0.693,2,0,2,1,1 +853,00:00:28.4830000,0.52459705,8.467793,0,0,0.694,2,0,2,1,1 +854,00:00:28.5160000,0.5363643,9.004158,0,0,0.694,2,0,2,1,1 +855,00:00:28.5500000,0.5363643,9.004158,0,0,0.694,2,0,2,1,1 +856,00:00:28.5830000,0.5284509,9.532609,0,0,0.694,2,0,2,1,1 +857,00:00:28.6160000,0.53877664,10.071385,0,0,0.694,2,0,2,1,1 +858,00:00:28.6500000,0.54211885,10.613504,0,0,0.694,2,0,2,1,1 +859,00:00:28.6830000,0.5247696,11.138274,0,0,0.694,2,0,2,1,1 +860,00:00:28.7160000,0.5247696,11.138274,0,0,0.694,2,0,2,1,1 +861,00:00:28.7500000,0.54683685,11.685111,0,0,0.694,2,0,2,1,1 +862,00:00:28.7830000,0.53438663,12.219498,0,0,0.694,2,0,2,1,1 +863,00:00:28.8160000,0.54283285,12.762331,0,0,0.694,1,0,1,1,1 +864,00:00:28.8500000,0.533174,13.295505,0,0,0.694,0,0,0,1,1 +865,00:00:28.8830000,0.533174,13.295505,0,0,0.694,0,0,0,1,1 +866,00:00:28.9160000,0.52783775,13.823342,0,0,0.694,0,0,0,1,1 +867,00:00:28.9500000,0.532071,14.355413,0,0,0.695,0,0,0,1,1 +868,00:00:28.9830000,0.53065854,14.886072,0,0,0.694,0,0,0,1,1 +869,00:00:29.0160000,0.533154,15.419226,0,0,0.694,0,0,0,1,1 +870,00:00:29.0500000,0.533154,15.419226,0,0,0.694,0,0,0,1,1 +871,00:00:29.0830000,0.522116,15.941341,0,0,0.693,0,0,0,1,1 +872,00:00:29.1160000,0.5312935,16.472635,0,0,0.693,0,0,0,1,1 +873,00:00:29.1500000,0.53915733,17.011793,0,0,0.693,0,0,0,1,1 +874,00:00:29.1830000,0.52995694,17.54175,0,0,0.694,0,0,0,1,1 +875,00:00:29.2160000,0.52995694,17.54175,0,0,0.694,0,0,0,1,1 +876,00:00:29.2500000,0.5262076,18.067957,0,0,0.694,0,0,0,1,1 +877,00:00:29.2830000,0.53269535,18.600653,0,0,0.694,0,0,0,1,1 +878,00:00:29.3160000,0.5276614,19.128315,0,0,0.693,0,0,0,1,1 +879,00:00:29.3500000,0.5323309,19.660646,0,0,0.695,0,0,0,1,1 +880,00:00:29.3830000,0.5323309,19.660646,0,0,0.695,0,0,0,1,1 +881,00:00:29.4160000,0.53562176,20.196268,0,0,0.694,0,0,0,1,1 +882,00:00:29.4500000,0.5285373,20.724806,0,0,0.695,0,0,0,1,1 +883,00:00:29.4830000,0.52954644,21.254353,0,0,0.693,0,0,0,1,1 +884,00:00:29.5160000,0.52768147,21.782034,0,0,0.694,0,0,0,1,1 +885,00:00:29.5500000,0.52768147,21.782034,0,0,0.694,0,0,0,1,1 +886,00:00:29.5830000,0.52182794,22.303862,0,0,0.694,0,0,0,1,1 +887,00:00:29.6160000,0.5373455,22.841208,0,0,0.694,0,0,0,1,1 +888,00:00:29.6500000,0.5288411,23.370049,0,0,0.694,0,0,0,1,1 +889,00:00:29.6830000,0.53263897,23.902687,0,0,0.694,0,0,0,1,1 +890,00:00:29.7160000,0.53263897,23.902687,0,0,0.694,0,0,0,1,1 +891,00:00:29.7500000,0.5239146,24.426601,0,0,0.694,0,0,0,1,1 +892,00:00:29.7830000,0.5278285,24.95443,0,0,0.694,0,0,0,1,1 +893,00:00:29.8160000,0.52889943,25.48333,0,0,0.694,0,0,0,1,1 +894,00:00:29.8500000,0.5358506,26.01918,0,0,0.694,0,0,0,1,1 +895,00:00:29.8830000,0.5358506,26.01918,0,0,0.694,0,0,0,1,1 +896,00:00:29.9160000,0.5248945,26.544075,0,0,0.693,0,0,0,1,1 +897,00:00:29.9500000,0.5278683,27.071943,0,0,0.694,0,0,0,1,1 +898,00:00:29.9830000,-0.53474957,-0.53474957,0,0,0.693,1,0,1,1,1 +899,00:00:30.0160000,-0.53625333,-1.071003,0,0,0.693,1,0,1,1,1 +900,00:00:30.0500000,-0.53625333,-1.071003,0,0,0.693,1,0,1,1,1 +901,00:00:30.0830000,0.53092474,0.53092474,0,0,0.692,2,0,2,1,1 +902,00:00:30.1160000,0.52703327,1.057958,0,0,0.693,2,0,2,1,1 +903,00:00:30.1500000,-0.5374447,-0.5374447,0,0,0.692,3,0,3,1,1 +904,00:00:30.1830000,-0.52779657,-1.0652413,0,0,0.694,3,0,3,1,1 +905,00:00:30.2160000,-0.52779657,-1.0652413,0,0,0.694,3,0,3,1,1 +906,00:00:30.2500000,0.5310039,0.5310039,0,0,0.694,4,0,4,1,1 +907,00:00:30.2830000,-0.53062725,-0.53062725,0,0,0.694,5,0,5,1,1 +908,00:00:30.3160000,0.53214127,0.53214127,0,0,0.693,6,0,6,1,1 +909,00:00:30.3500000,0.53184485,1.0639861,0,0,0.695,6,0,6,1,1 +910,00:00:30.3830000,0.53184485,1.0639861,0,0,0.695,6,0,6,1,1 +911,00:00:30.4160000,0.5333835,1.5973696,0,0,0.695,6,0,6,1,1 +912,00:00:30.4500000,0.53199166,2.1293612,0,0,0.694,6,0,6,1,1 +913,00:00:30.4830000,0.5244562,2.6538174,0,0,0.694,6,0,6,1,1 +914,00:00:30.5160000,0.53226507,3.1860824,0,0,0.693,6,0,6,1,1 +915,00:00:30.5500000,0.53226507,3.1860824,0,0,0.693,6,0,6,1,1 +916,00:00:30.5830000,0.5316009,3.7176833,0,0,0.693,6,0,6,1,1 +917,00:00:30.6160000,-0.53774184,-0.53774184,0,0,0.693,7,0,7,2,1 +918,00:00:30.6500000,0.5368083,0.5368083,0,0,0.693,8,0,8,2,1 diff --git a/test/Iris.Tests/data/ExpectedVideoLogFiles/Pattern - Circular 4_RELATIVE.csv b/test/Iris.Tests/data/ExpectedVideoLogFiles/Pattern - Circular 4_RELATIVE.csv new file mode 100644 index 0000000..5fee4c6 Binary files /dev/null and b/test/Iris.Tests/data/ExpectedVideoLogFiles/Pattern - Circular 4_RELATIVE.csv differ diff --git a/test/Iris.Tests/data/ExpectedVideoLogFiles/Pattern - Circular 5_RELATIVE.csv b/test/Iris.Tests/data/ExpectedVideoLogFiles/Pattern - Circular 5_RELATIVE.csv new file mode 100644 index 0000000..9d4ebc2 Binary files /dev/null and b/test/Iris.Tests/data/ExpectedVideoLogFiles/Pattern - Circular 5_RELATIVE.csv differ diff --git a/test/Iris.Tests/data/ExpectedVideoLogFiles/Pattern - Circular 8_RELATIVE.csv b/test/Iris.Tests/data/ExpectedVideoLogFiles/Pattern - Circular 8_RELATIVE.csv new file mode 100644 index 0000000..24db90e --- /dev/null +++ b/test/Iris.Tests/data/ExpectedVideoLogFiles/Pattern - Circular 8_RELATIVE.csv @@ -0,0 +1,301 @@ +Frame,TimeStamp,AverageLuminanceDiff,AverageLuminanceDiffAcc,AverageRedDiff,AverageRedDiffAcc,PatternRisk,LuminanceTransitions,RedTransitions,TotalTransitions,FlashFailedFrames,PatternFailedFrames +1,00:00:00,0,0,0,0,0.702,0,0,0,0,0 +2,00:00:00.0330000,0,0,0,0,0.702,0,0,0,0,0 +3,00:00:00.0660000,0,0,0,0,0.702,0,0,0,0,0 +4,00:00:00.0990000,0,0,0,0,0.702,0,0,0,0,0 +5,00:00:00.1330000,0,0,0,0,0.702,0,0,0,0,0 +6,00:00:00.1660000,0,0,0,0,0.702,0,0,0,0,0 +7,00:00:00.1990000,0,0,0,0,0.702,0,0,0,0,0 +8,00:00:00.2330000,0,0,0,0,0.702,0,0,0,0,0 +9,00:00:00.2660000,0,0,0,0,0.702,0,0,0,0,0 +10,00:00:00.2990000,0,0,0,0,0.702,0,0,0,0,0 +11,00:00:00.3330000,0,0,0,0,0.702,0,0,0,0,0 +12,00:00:00.3660000,0,0,0,0,0.702,0,0,0,0,0 +13,00:00:00.3990000,0,0,0,0,0.702,0,0,0,0,0 +14,00:00:00.4330000,0,0,0,0,0.702,0,0,0,0,0 +15,00:00:00.4660000,0,0,0,0,0.702,0,0,0,0,1 +16,00:00:00.4990000,0,0,0,0,0.702,0,0,0,0,1 +17,00:00:00.5330000,0,0,0,0,0.702,0,0,0,0,1 +18,00:00:00.5660000,0,0,0,0,0.702,0,0,0,0,1 +19,00:00:00.5990000,0,0,0,0,0.702,0,0,0,0,1 +20,00:00:00.6330000,0,0,0,0,0.702,0,0,0,0,1 +21,00:00:00.6660000,0,0,0,0,0.702,0,0,0,0,1 +22,00:00:00.6990000,0,0,0,0,0.702,0,0,0,0,1 +23,00:00:00.7330000,0,0,0,0,0.702,0,0,0,0,1 +24,00:00:00.7660000,0,0,0,0,0.702,0,0,0,0,1 +25,00:00:00.7990000,0,0,0,0,0.702,0,0,0,0,1 +26,00:00:00.8330000,0,0,0,0,0.702,0,0,0,0,1 +27,00:00:00.8660000,0,0,0,0,0.702,0,0,0,0,1 +28,00:00:00.8990000,0,0,0,0,0.702,0,0,0,0,1 +29,00:00:00.9330000,0,0,0,0,0.702,0,0,0,0,1 +30,00:00:00.9660000,0,0,0,0,0.702,0,0,0,0,1 +31,00:00:00.9990000,0,0,0,0,0.702,0,0,0,0,1 +32,00:00:01.0330000,0,0,0,0,0.702,0,0,0,0,1 +33,00:00:01.0660000,0,0,0,0,0.702,0,0,0,0,1 +34,00:00:01.0990000,0,0,0,0,0.702,0,0,0,0,1 +35,00:00:01.1330000,0,0,0,0,0.702,0,0,0,0,1 +36,00:00:01.1660000,0,0,0,0,0.702,0,0,0,0,1 +37,00:00:01.1990000,0,0,0,0,0.702,0,0,0,0,1 +38,00:00:01.2330000,0,0,0,0,0.702,0,0,0,0,1 +39,00:00:01.2660000,0,0,0,0,0.702,0,0,0,0,1 +40,00:00:01.2990000,0,0,0,0,0.702,0,0,0,0,1 +41,00:00:01.3330000,0,0,0,0,0.702,0,0,0,0,1 +42,00:00:01.3660000,0,0,0,0,0.702,0,0,0,0,1 +43,00:00:01.3990000,0,0,0,0,0.702,0,0,0,0,1 +44,00:00:01.4330000,0,0,0,0,0.702,0,0,0,0,1 +45,00:00:01.4660000,0,0,0,0,0.702,0,0,0,0,1 +46,00:00:01.4990000,0,0,0,0,0.702,0,0,0,0,1 +47,00:00:01.5330000,0,0,0,0,0.702,0,0,0,0,1 +48,00:00:01.5660000,0,0,0,0,0.702,0,0,0,0,1 +49,00:00:01.5990000,0,0,0,0,0.702,0,0,0,0,1 +50,00:00:01.6330000,0,0,0,0,0.702,0,0,0,0,1 +51,00:00:01.6660000,0,0,0,0,0.702,0,0,0,0,1 +52,00:00:01.6990000,0,0,0,0,0.702,0,0,0,0,1 +53,00:00:01.7330000,0,0,0,0,0.702,0,0,0,0,1 +54,00:00:01.7660000,0,0,0,0,0.702,0,0,0,0,1 +55,00:00:01.7990000,0,0,0,0,0.702,0,0,0,0,1 +56,00:00:01.8330000,0,0,0,0,0.702,0,0,0,0,1 +57,00:00:01.8660000,0,0,0,0,0.702,0,0,0,0,1 +58,00:00:01.8990000,0,0,0,0,0.702,0,0,0,0,1 +59,00:00:01.9330000,0,0,0,0,0.702,0,0,0,0,1 +60,00:00:01.9660000,0,0,0,0,0.702,0,0,0,0,1 +61,00:00:01.9990000,0,0,0,0,0.702,0,0,0,0,1 +62,00:00:02.0330000,0,0,0,0,0.702,0,0,0,0,1 +63,00:00:02.0660000,0,0,0,0,0.702,0,0,0,0,1 +64,00:00:02.0990000,0,0,0,0,0.702,0,0,0,0,1 +65,00:00:02.1330000,0,0,0,0,0.702,0,0,0,0,1 +66,00:00:02.1660000,0,0,0,0,0.702,0,0,0,0,1 +67,00:00:02.1990000,0,0,0,0,0.702,0,0,0,0,1 +68,00:00:02.2330000,0,0,0,0,0.702,0,0,0,0,1 +69,00:00:02.2660000,0,0,0,0,0.702,0,0,0,0,1 +70,00:00:02.2990000,0,0,0,0,0.702,0,0,0,0,1 +71,00:00:02.3330000,0,0,0,0,0.702,0,0,0,0,1 +72,00:00:02.3660000,0,0,0,0,0.702,0,0,0,0,1 +73,00:00:02.3990000,0,0,0,0,0.702,0,0,0,0,1 +74,00:00:02.4330000,0,0,0,0,0.702,0,0,0,0,1 +75,00:00:02.4660000,0,0,0,0,0.702,0,0,0,0,1 +76,00:00:02.4990000,0,0,0,0,0.702,0,0,0,0,1 +77,00:00:02.5330000,0,0,0,0,0.702,0,0,0,0,1 +78,00:00:02.5660000,0,0,0,0,0.702,0,0,0,0,1 +79,00:00:02.5990000,0,0,0,0,0.702,0,0,0,0,1 +80,00:00:02.6330000,0,0,0,0,0.702,0,0,0,0,1 +81,00:00:02.6660000,0,0,0,0,0.702,0,0,0,0,1 +82,00:00:02.6990000,0,0,0,0,0.702,0,0,0,0,1 +83,00:00:02.7330000,0,0,0,0,0.702,0,0,0,0,1 +84,00:00:02.7660000,0,0,0,0,0.702,0,0,0,0,1 +85,00:00:02.7990000,0,0,0,0,0.702,0,0,0,0,1 +86,00:00:02.8330000,0,0,0,0,0.702,0,0,0,0,1 +87,00:00:02.8660000,0,0,0,0,0.702,0,0,0,0,1 +88,00:00:02.8990000,0,0,0,0,0.702,0,0,0,0,1 +89,00:00:02.9330000,0,0,0,0,0.702,0,0,0,0,1 +90,00:00:02.9660000,0,0,0,0,0.702,0,0,0,0,1 +91,00:00:02.9990000,0,0,0,0,0.702,0,0,0,0,1 +92,00:00:03.0330000,0,0,0,0,0.702,0,0,0,0,1 +93,00:00:03.0660000,0,0,0,0,0.702,0,0,0,0,1 +94,00:00:03.0990000,0,0,0,0,0.702,0,0,0,0,1 +95,00:00:03.1330000,0,0,0,0,0.702,0,0,0,0,1 +96,00:00:03.1660000,0,0,0,0,0.702,0,0,0,0,1 +97,00:00:03.1990000,0,0,0,0,0.702,0,0,0,0,1 +98,00:00:03.2330000,0,0,0,0,0.702,0,0,0,0,1 +99,00:00:03.2660000,0,0,0,0,0.702,0,0,0,0,1 +100,00:00:03.2990000,0,0,0,0,0.702,0,0,0,0,1 +101,00:00:03.3330000,0,0,0,0,0.702,0,0,0,0,1 +102,00:00:03.3660000,0,0,0,0,0.702,0,0,0,0,1 +103,00:00:03.3990000,0,0,0,0,0.702,0,0,0,0,1 +104,00:00:03.4330000,0,0,0,0,0.702,0,0,0,0,1 +105,00:00:03.4660000,0,0,0,0,0.702,0,0,0,0,1 +106,00:00:03.4990000,0,0,0,0,0.702,0,0,0,0,1 +107,00:00:03.5330000,0,0,0,0,0.702,0,0,0,0,1 +108,00:00:03.5660000,0,0,0,0,0.702,0,0,0,0,1 +109,00:00:03.5990000,0,0,0,0,0.702,0,0,0,0,1 +110,00:00:03.6330000,0,0,0,0,0.702,0,0,0,0,1 +111,00:00:03.6660000,0,0,0,0,0.702,0,0,0,0,1 +112,00:00:03.6990000,0,0,0,0,0.702,0,0,0,0,1 +113,00:00:03.7330000,0,0,0,0,0.702,0,0,0,0,1 +114,00:00:03.7660000,0,0,0,0,0.702,0,0,0,0,1 +115,00:00:03.7990000,0,0,0,0,0.702,0,0,0,0,1 +116,00:00:03.8330000,0,0,0,0,0.702,0,0,0,0,1 +117,00:00:03.8660000,0,0,0,0,0.702,0,0,0,0,1 +118,00:00:03.8990000,0,0,0,0,0.702,0,0,0,0,1 +119,00:00:03.9330000,0,0,0,0,0.702,0,0,0,0,1 +120,00:00:03.9660000,0,0,0,0,0.702,0,0,0,0,1 +121,00:00:03.9990000,0,0,0,0,0.702,0,0,0,0,1 +122,00:00:04.0330000,0,0,0,0,0.702,0,0,0,0,1 +123,00:00:04.0660000,0,0,0,0,0.702,0,0,0,0,1 +124,00:00:04.0990000,0,0,0,0,0.702,0,0,0,0,1 +125,00:00:04.1330000,0,0,0,0,0.702,0,0,0,0,1 +126,00:00:04.1660000,0,0,0,0,0.702,0,0,0,0,1 +127,00:00:04.1990000,0,0,0,0,0.702,0,0,0,0,1 +128,00:00:04.2330000,0,0,0,0,0.702,0,0,0,0,1 +129,00:00:04.2660000,0,0,0,0,0.702,0,0,0,0,1 +130,00:00:04.2990000,0,0,0,0,0.702,0,0,0,0,1 +131,00:00:04.3330000,0,0,0,0,0.702,0,0,0,0,1 +132,00:00:04.3660000,0,0,0,0,0.702,0,0,0,0,1 +133,00:00:04.3990000,0,0,0,0,0.702,0,0,0,0,1 +134,00:00:04.4330000,0,0,0,0,0.702,0,0,0,0,1 +135,00:00:04.4660000,0,0,0,0,0.702,0,0,0,0,1 +136,00:00:04.4990000,0,0,0,0,0.702,0,0,0,0,1 +137,00:00:04.5330000,0,0,0,0,0.702,0,0,0,0,1 +138,00:00:04.5660000,0,0,0,0,0.702,0,0,0,0,1 +139,00:00:04.5990000,0,0,0,0,0.702,0,0,0,0,1 +140,00:00:04.6330000,0,0,0,0,0.702,0,0,0,0,1 +141,00:00:04.6660000,0,0,0,0,0.702,0,0,0,0,1 +142,00:00:04.6990000,0,0,0,0,0.702,0,0,0,0,1 +143,00:00:04.7330000,0,0,0,0,0.702,0,0,0,0,1 +144,00:00:04.7660000,0,0,0,0,0.702,0,0,0,0,1 +145,00:00:04.7990000,0,0,0,0,0.702,0,0,0,0,1 +146,00:00:04.8330000,0,0,0,0,0.702,0,0,0,0,1 +147,00:00:04.8660000,0,0,0,0,0.702,0,0,0,0,1 +148,00:00:04.8990000,0,0,0,0,0.702,0,0,0,0,1 +149,00:00:04.9330000,0,0,0,0,0.702,0,0,0,0,1 +150,00:00:04.9660000,0,0,0,0,0.702,0,0,0,0,1 +151,00:00:04.9990000,0,0,0,0,0.702,0,0,0,0,1 +152,00:00:05.0330000,0,0,0,0,0.702,0,0,0,0,1 +153,00:00:05.0660000,0,0,0,0,0.702,0,0,0,0,1 +154,00:00:05.0990000,0,0,0,0,0.702,0,0,0,0,1 +155,00:00:05.1330000,0,0,0,0,0.702,0,0,0,0,1 +156,00:00:05.1660000,0,0,0,0,0.702,0,0,0,0,1 +157,00:00:05.1990000,0,0,0,0,0.702,0,0,0,0,1 +158,00:00:05.2330000,0,0,0,0,0.702,0,0,0,0,1 +159,00:00:05.2660000,0,0,0,0,0.702,0,0,0,0,1 +160,00:00:05.2990000,0,0,0,0,0.702,0,0,0,0,1 +161,00:00:05.3330000,0,0,0,0,0.702,0,0,0,0,1 +162,00:00:05.3660000,0,0,0,0,0.702,0,0,0,0,1 +163,00:00:05.3990000,0,0,0,0,0.702,0,0,0,0,1 +164,00:00:05.4330000,0,0,0,0,0.702,0,0,0,0,1 +165,00:00:05.4660000,0,0,0,0,0.702,0,0,0,0,1 +166,00:00:05.4990000,0,0,0,0,0.702,0,0,0,0,1 +167,00:00:05.5330000,0,0,0,0,0.702,0,0,0,0,1 +168,00:00:05.5660000,0,0,0,0,0.702,0,0,0,0,1 +169,00:00:05.5990000,0,0,0,0,0.702,0,0,0,0,1 +170,00:00:05.6330000,0,0,0,0,0.702,0,0,0,0,1 +171,00:00:05.6660000,0,0,0,0,0.702,0,0,0,0,1 +172,00:00:05.6990000,0,0,0,0,0.702,0,0,0,0,1 +173,00:00:05.7330000,0,0,0,0,0.702,0,0,0,0,1 +174,00:00:05.7660000,0,0,0,0,0.702,0,0,0,0,1 +175,00:00:05.7990000,0,0,0,0,0.702,0,0,0,0,1 +176,00:00:05.8330000,0,0,0,0,0.702,0,0,0,0,1 +177,00:00:05.8660000,0,0,0,0,0.702,0,0,0,0,1 +178,00:00:05.8990000,0,0,0,0,0.702,0,0,0,0,1 +179,00:00:05.9330000,0,0,0,0,0.702,0,0,0,0,1 +180,00:00:05.9660000,0,0,0,0,0.702,0,0,0,0,1 +181,00:00:05.9990000,0,0,0,0,0.702,0,0,0,0,1 +182,00:00:06.0330000,0,0,0,0,0.702,0,0,0,0,1 +183,00:00:06.0660000,0,0,0,0,0.702,0,0,0,0,1 +184,00:00:06.0990000,0,0,0,0,0.702,0,0,0,0,1 +185,00:00:06.1330000,0,0,0,0,0.702,0,0,0,0,1 +186,00:00:06.1660000,0,0,0,0,0.702,0,0,0,0,1 +187,00:00:06.1990000,0,0,0,0,0.702,0,0,0,0,1 +188,00:00:06.2330000,0,0,0,0,0.702,0,0,0,0,1 +189,00:00:06.2660000,0,0,0,0,0.702,0,0,0,0,1 +190,00:00:06.2990000,0,0,0,0,0.702,0,0,0,0,1 +191,00:00:06.3330000,0,0,0,0,0.702,0,0,0,0,1 +192,00:00:06.3660000,0,0,0,0,0.702,0,0,0,0,1 +193,00:00:06.3990000,0,0,0,0,0.702,0,0,0,0,1 +194,00:00:06.4330000,0,0,0,0,0.702,0,0,0,0,1 +195,00:00:06.4660000,0,0,0,0,0.702,0,0,0,0,1 +196,00:00:06.4990000,0,0,0,0,0.702,0,0,0,0,1 +197,00:00:06.5330000,0,0,0,0,0.702,0,0,0,0,1 +198,00:00:06.5660000,0,0,0,0,0.702,0,0,0,0,1 +199,00:00:06.5990000,0,0,0,0,0.702,0,0,0,0,1 +200,00:00:06.6330000,0,0,0,0,0.702,0,0,0,0,1 +201,00:00:06.6660000,0,0,0,0,0.702,0,0,0,0,1 +202,00:00:06.6990000,0,0,0,0,0.702,0,0,0,0,1 +203,00:00:06.7330000,0,0,0,0,0.702,0,0,0,0,1 +204,00:00:06.7660000,0,0,0,0,0.702,0,0,0,0,1 +205,00:00:06.7990000,0,0,0,0,0.702,0,0,0,0,1 +206,00:00:06.8330000,0,0,0,0,0.702,0,0,0,0,1 +207,00:00:06.8660000,0,0,0,0,0.702,0,0,0,0,1 +208,00:00:06.8990000,0,0,0,0,0.702,0,0,0,0,1 +209,00:00:06.9330000,0,0,0,0,0.702,0,0,0,0,1 +210,00:00:06.9660000,0,0,0,0,0.702,0,0,0,0,1 +211,00:00:06.9990000,0,0,0,0,0.702,0,0,0,0,1 +212,00:00:07.0330000,0,0,0,0,0.702,0,0,0,0,1 +213,00:00:07.0660000,0,0,0,0,0.702,0,0,0,0,1 +214,00:00:07.0990000,0,0,0,0,0.702,0,0,0,0,1 +215,00:00:07.1330000,0,0,0,0,0.702,0,0,0,0,1 +216,00:00:07.1660000,0,0,0,0,0.702,0,0,0,0,1 +217,00:00:07.1990000,0,0,0,0,0.702,0,0,0,0,1 +218,00:00:07.2330000,0,0,0,0,0.702,0,0,0,0,1 +219,00:00:07.2660000,0,0,0,0,0.702,0,0,0,0,1 +220,00:00:07.2990000,0,0,0,0,0.702,0,0,0,0,1 +221,00:00:07.3330000,0,0,0,0,0.702,0,0,0,0,1 +222,00:00:07.3660000,0,0,0,0,0.702,0,0,0,0,1 +223,00:00:07.3990000,0,0,0,0,0.702,0,0,0,0,1 +224,00:00:07.4330000,0,0,0,0,0.702,0,0,0,0,1 +225,00:00:07.4660000,0,0,0,0,0.702,0,0,0,0,1 +226,00:00:07.4990000,0,0,0,0,0.702,0,0,0,0,1 +227,00:00:07.5330000,0,0,0,0,0.702,0,0,0,0,1 +228,00:00:07.5660000,0,0,0,0,0.702,0,0,0,0,1 +229,00:00:07.5990000,0,0,0,0,0.702,0,0,0,0,1 +230,00:00:07.6330000,0,0,0,0,0.702,0,0,0,0,1 +231,00:00:07.6660000,0,0,0,0,0.702,0,0,0,0,1 +232,00:00:07.6990000,0,0,0,0,0.702,0,0,0,0,1 +233,00:00:07.7330000,0,0,0,0,0.702,0,0,0,0,1 +234,00:00:07.7660000,0,0,0,0,0.702,0,0,0,0,1 +235,00:00:07.7990000,0,0,0,0,0.702,0,0,0,0,1 +236,00:00:07.8330000,0,0,0,0,0.702,0,0,0,0,1 +237,00:00:07.8660000,0,0,0,0,0.702,0,0,0,0,1 +238,00:00:07.8990000,0,0,0,0,0.702,0,0,0,0,1 +239,00:00:07.9330000,0,0,0,0,0.702,0,0,0,0,1 +240,00:00:07.9660000,0,0,0,0,0.702,0,0,0,0,1 +241,00:00:07.9990000,0,0,0,0,0.702,0,0,0,0,1 +242,00:00:08.0330000,0,0,0,0,0.702,0,0,0,0,1 +243,00:00:08.0660000,0,0,0,0,0.702,0,0,0,0,1 +244,00:00:08.0990000,0,0,0,0,0.702,0,0,0,0,1 +245,00:00:08.1330000,0,0,0,0,0.702,0,0,0,0,1 +246,00:00:08.1660000,0,0,0,0,0.702,0,0,0,0,1 +247,00:00:08.1990000,0,0,0,0,0.702,0,0,0,0,1 +248,00:00:08.2330000,0,0,0,0,0.702,0,0,0,0,1 +249,00:00:08.2660000,0,0,0,0,0.702,0,0,0,0,1 +250,00:00:08.2990000,0,0,0,0,0.702,0,0,0,0,1 +251,00:00:08.3330000,0,0,0,0,0.702,0,0,0,0,1 +252,00:00:08.3660000,0,0,0,0,0.702,0,0,0,0,1 +253,00:00:08.3990000,0,0,0,0,0.702,0,0,0,0,1 +254,00:00:08.4330000,0,0,0,0,0.702,0,0,0,0,1 +255,00:00:08.4660000,0,0,0,0,0.702,0,0,0,0,1 +256,00:00:08.4990000,0,0,0,0,0.702,0,0,0,0,1 +257,00:00:08.5330000,0,0,0,0,0.702,0,0,0,0,1 +258,00:00:08.5660000,0,0,0,0,0.702,0,0,0,0,1 +259,00:00:08.5990000,0,0,0,0,0.702,0,0,0,0,1 +260,00:00:08.6330000,0,0,0,0,0.702,0,0,0,0,1 +261,00:00:08.6660000,0,0,0,0,0.702,0,0,0,0,1 +262,00:00:08.6990000,0,0,0,0,0.702,0,0,0,0,1 +263,00:00:08.7330000,0,0,0,0,0.702,0,0,0,0,1 +264,00:00:08.7660000,0,0,0,0,0.702,0,0,0,0,1 +265,00:00:08.7990000,0,0,0,0,0.702,0,0,0,0,1 +266,00:00:08.8330000,0,0,0,0,0.702,0,0,0,0,1 +267,00:00:08.8660000,0,0,0,0,0.702,0,0,0,0,1 +268,00:00:08.8990000,0,0,0,0,0.702,0,0,0,0,1 +269,00:00:08.9330000,0,0,0,0,0.702,0,0,0,0,1 +270,00:00:08.9660000,0,0,0,0,0.702,0,0,0,0,1 +271,00:00:08.9990000,0,0,0,0,0.702,0,0,0,0,1 +272,00:00:09.0330000,0,0,0,0,0.702,0,0,0,0,1 +273,00:00:09.0660000,0,0,0,0,0.702,0,0,0,0,1 +274,00:00:09.0990000,0,0,0,0,0.702,0,0,0,0,1 +275,00:00:09.1330000,0,0,0,0,0.702,0,0,0,0,1 +276,00:00:09.1660000,0,0,0,0,0.702,0,0,0,0,1 +277,00:00:09.1990000,0,0,0,0,0.702,0,0,0,0,1 +278,00:00:09.2330000,0,0,0,0,0.702,0,0,0,0,1 +279,00:00:09.2660000,0,0,0,0,0.702,0,0,0,0,1 +280,00:00:09.2990000,0,0,0,0,0.702,0,0,0,0,1 +281,00:00:09.3330000,0,0,0,0,0.702,0,0,0,0,1 +282,00:00:09.3660000,0,0,0,0,0.702,0,0,0,0,1 +283,00:00:09.3990000,0,0,0,0,0.702,0,0,0,0,1 +284,00:00:09.4330000,0,0,0,0,0.702,0,0,0,0,1 +285,00:00:09.4660000,0,0,0,0,0.702,0,0,0,0,1 +286,00:00:09.4990000,0,0,0,0,0.702,0,0,0,0,1 +287,00:00:09.5330000,0,0,0,0,0.702,0,0,0,0,1 +288,00:00:09.5660000,0,0,0,0,0.702,0,0,0,0,1 +289,00:00:09.5990000,0,0,0,0,0.702,0,0,0,0,1 +290,00:00:09.6330000,0,0,0,0,0.702,0,0,0,0,1 +291,00:00:09.6660000,0,0,0,0,0.702,0,0,0,0,1 +292,00:00:09.6990000,0,0,0,0,0.702,0,0,0,0,1 +293,00:00:09.7330000,0,0,0,0,0.702,0,0,0,0,1 +294,00:00:09.7660000,0,0,0,0,0.702,0,0,0,0,1 +295,00:00:09.7990000,0,0,0,0,0.702,0,0,0,0,1 +296,00:00:09.8330000,0,0,0,0,0.702,0,0,0,0,1 +297,00:00:09.8660000,0,0,0,0,0.702,0,0,0,0,1 +298,00:00:09.8990000,0,0,0,0,0.702,0,0,0,0,1 +299,00:00:09.9330000,0,0,0,0,0.702,0,0,0,0,1 +300,00:00:09.9660000,0,0,0,0,0.702,0,0,0,0,1 diff --git a/test/Iris.Tests/data/ExpectedVideoLogFiles/extendedFLONG_RELATIVE.csv b/test/Iris.Tests/data/ExpectedVideoLogFiles/extendedFLONG_RELATIVE.csv new file mode 100644 index 0000000..23c7e5e Binary files /dev/null and b/test/Iris.Tests/data/ExpectedVideoLogFiles/extendedFLONG_RELATIVE.csv differ diff --git a/test/Iris.Tests/data/ExpectedVideoLogFiles/flashStripes_RELATIVE.csv b/test/Iris.Tests/data/ExpectedVideoLogFiles/flashStripes_RELATIVE.csv new file mode 100644 index 0000000..f1f6963 --- /dev/null +++ b/test/Iris.Tests/data/ExpectedVideoLogFiles/flashStripes_RELATIVE.csv @@ -0,0 +1,37 @@ +Frame,TimeStamp,AverageLuminanceDiff,AverageLuminanceDiffAcc,AverageRedDiff,AverageRedDiffAcc,PatternRisk,LuminanceTransitions,RedTransitions,TotalTransitions,FlashFailedFrames,PatternFailedFrames +1,00:00:00,0,0,0,0,0.702,0,0,0,0,0 +2,00:00:00.0400000,-0.9427821,-0.9427821,0,0,0,1,0,1,0,0 +3,00:00:00.0800000,0.94370276,0.94370276,0,0,0.702,2,0,2,0,0 +4,00:00:00.1200000,-0.94370276,-0.94370276,0,0,0,3,0,3,0,0 +5,00:00:00.1600000,0.94370276,0.94370276,0,0,0.702,4,0,4,0,0 +6,00:00:00.2000000,-0.94370276,-0.94370276,0,0,0,5,0,5,0,0 +7,00:00:00.2400000,0.94370276,0.94370276,0,0,0.702,6,0,6,0,0 +8,00:00:00.2800000,-0.94370276,-0.94370276,0,0,0,7,0,7,2,0 +9,00:00:00.3200000,0.94370276,0.94370276,0,0,0.702,8,0,8,2,0 +10,00:00:00.3600000,-0.94370276,-0.94370276,0,0,0,9,0,9,2,0 +11,00:00:00.4000000,0.94370276,0.94370276,0,0,0.702,10,0,10,2,0 +12,00:00:00.4400000,-0.94370276,-0.94370276,0,0,0,11,0,11,2,0 +13,00:00:00.4800000,0.94370276,0.94370276,0,0,0.702,12,0,12,2,0 +14,00:00:00.5200000,-0.94370276,-0.94370276,0,0,0,13,0,13,2,0 +15,00:00:00.5600000,0.94370276,0.94370276,0,0,0.702,14,0,14,2,0 +16,00:00:00.6000000,-0.94370276,-0.94370276,0,0,0,15,0,15,2,0 +17,00:00:00.6400000,0.9301942,0.9301942,0,0,0.701,16,0,16,2,0 +18,00:00:00.6800000,-0.9301942,-0.9301942,0,0,0,17,0,17,2,0 +19,00:00:00.7200000,0.9301942,0.9301942,0,0,0.701,18,0,18,2,0 +20,00:00:00.7600000,-0.9301942,-0.9301942,0,0,0,19,0,19,2,0 +21,00:00:00.8000000,0.9301942,0.9301942,0,0,0.701,20,0,20,2,0 +22,00:00:00.8400000,-0.9301942,-0.9301942,0,0,0,21,0,21,2,0 +23,00:00:00.8800000,0.9301942,0.9301942,0,0,0.701,22,0,22,2,0 +24,00:00:00.9200000,-0.9301942,-0.9301942,0,0,0,23,0,23,2,0 +25,00:00:00.9600000,0.9301942,0.9301942,0,0,0.701,24,0,24,2,0 +26,00:00:01,-0.9301942,-0.9301942,0,0,0,25,0,25,2,0 +27,00:00:01.0400000,0.9301942,0.9301942,0,0,0.701,25,0,25,2,0 +28,00:00:01.0800000,-0.9301942,-0.9301942,0,0,0,25,0,25,2,0 +29,00:00:01.1200000,0.98767823,0.98767823,0,0,0.702,25,0,25,2,0 +30,00:00:01.1600000,-0.98767823,-0.98767823,0,0,0,25,0,25,2,0 +31,00:00:01.2000000,0.98767823,0.98767823,0,0,0.702,25,0,25,2,0 +32,00:00:01.2400000,-0.9878043,-0.9878043,0,0,0,25,0,25,2,0 +33,00:00:01.2800000,0.9878043,0.9878043,0,0,0.702,25,0,25,2,0 +34,00:00:01.3200000,-0.9878043,-0.9878043,0,0,0,25,0,25,2,0 +35,00:00:01.3600000,0.9878043,0.9878043,0,0,0.702,25,0,25,2,0 +36,00:00:01.4000000,-0.98767823,-0.98767823,0,0,0,25,0,25,2,0 diff --git a/test/Iris.Tests/data/ExpectedVideoLogFiles/gradRed2_RELATIVE.csv b/test/Iris.Tests/data/ExpectedVideoLogFiles/gradRed2_RELATIVE.csv new file mode 100644 index 0000000..ae53368 --- /dev/null +++ b/test/Iris.Tests/data/ExpectedVideoLogFiles/gradRed2_RELATIVE.csv @@ -0,0 +1,176 @@ +Frame,TimeStamp,AverageLuminanceDiff,AverageLuminanceDiffAcc,AverageRedDiff,AverageRedDiffAcc,PatternRisk,LuminanceTransitions,RedTransitions,TotalTransitions,FlashFailedFrames,PatternFailedFrames +1,00:00:00,0,0,0,0,0,0,0,0,0,0 +2,00:00:00.0400000,0,0,0,0,0,0,0,0,0,0 +3,00:00:00.0800000,0,0,0,0,0,0,0,0,0,0 +4,00:00:00.1200000,0,0,0,0,0,0,0,0,0,0 +5,00:00:00.1600000,0,0,0,0,0,0,0,0,0,0 +6,00:00:00.2000000,0,0,0,0,0,0,0,0,0,0 +7,00:00:00.2400000,0,0,0,0,0,0,0,0,0,0 +8,00:00:00.2800000,0,0,0,0,0,0,0,0,0,0 +9,00:00:00.3200000,0,0,0,0,0,0,0,0,0,0 +10,00:00:00.3600000,0,0,0,0,0,0,0,0,0,0 +11,00:00:00.4000000,0,0,0,0,0,0,0,0,0,0 +12,00:00:00.4400000,0,0,0,0,0,0,0,0,0,0 +13,00:00:00.4800000,0,0,0,0,0,0,0,0,0,0 +14,00:00:00.5200000,0,0,0,0,0,0,0,0,0,0 +15,00:00:00.5600000,0,0,0,0,0,0,0,0,0,0 +16,00:00:00.6000000,0,0,0,0,0,0,0,0,0,0 +17,00:00:00.6400000,0,0,0,0,0,0,0,0,0,0 +18,00:00:00.6800000,0,0,0,0,0,0,0,0,0,0 +19,00:00:00.7200000,0,0,0,0,0,0,0,0,0,0 +20,00:00:00.7600000,0,0,0,0,0,0,0,0,0,0 +21,00:00:00.8000000,0,0,0,0,0,0,0,0,0,0 +22,00:00:00.8400000,0,0,0,0,0,0,0,0,0,0 +23,00:00:00.8800000,0,0,0,0,0,0,0,0,0,0 +24,00:00:00.9200000,0,0,0,0,0,0,0,0,0,0 +25,00:00:00.9600000,0,0,0,0,0,0,0,0,0,0 +26,00:00:01,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +27,00:00:01.0400000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +28,00:00:01.0800000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +29,00:00:01.1200000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +30,00:00:01.1600000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +31,00:00:01.2000000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +32,00:00:01.2400000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +33,00:00:01.2800000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +34,00:00:01.3200000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +35,00:00:01.3600000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +36,00:00:01.4000000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +37,00:00:01.4400000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +38,00:00:01.4800000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +39,00:00:01.5200000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +40,00:00:01.5600000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +41,00:00:01.6000000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +42,00:00:01.6400000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +43,00:00:01.6800000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +44,00:00:01.7200000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +45,00:00:01.7600000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +46,00:00:01.8000000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +47,00:00:01.8400000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +48,00:00:01.8800000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +49,00:00:01.9200000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +50,00:00:01.9600000,-0.002590533,-0.002590533,14.039379,14.039379,0,0,0,0,0,0 +51,00:00:02,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +52,00:00:02.0400000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +53,00:00:02.0800000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +54,00:00:02.1200000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +55,00:00:02.1600000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +56,00:00:02.2000000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +57,00:00:02.2400000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +58,00:00:02.2800000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +59,00:00:02.3200000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +60,00:00:02.3600000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +61,00:00:02.4000000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +62,00:00:02.4400000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +63,00:00:02.4800000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +64,00:00:02.5200000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +65,00:00:02.5600000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +66,00:00:02.6000000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +67,00:00:02.6400000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +68,00:00:02.6800000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +69,00:00:02.7200000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +70,00:00:02.7600000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +71,00:00:02.8000000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +72,00:00:02.8400000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +73,00:00:02.8800000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +74,00:00:02.9200000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +75,00:00:02.9600000,-0.001742497,-0.001742497,14.335419,14.335419,0,0,0,0,0,0 +76,00:00:03,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +77,00:00:03.0400000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +78,00:00:03.0800000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +79,00:00:03.1200000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +80,00:00:03.1600000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +81,00:00:03.2000000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +82,00:00:03.2400000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +83,00:00:03.2800000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +84,00:00:03.3200000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +85,00:00:03.3600000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +86,00:00:03.4000000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +87,00:00:03.4400000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +88,00:00:03.4800000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +89,00:00:03.5200000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +90,00:00:03.5600000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +91,00:00:03.6000000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +92,00:00:03.6400000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +93,00:00:03.6800000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +94,00:00:03.7200000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +95,00:00:03.7600000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +96,00:00:03.8000000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +97,00:00:03.8400000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +98,00:00:03.8800000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +99,00:00:03.9200000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +100,00:00:03.9600000,-6.92904E-06,-6.92904E-06,17.59074,17.88678,0,0,0,0,0,0 +101,00:00:04,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +102,00:00:04.0400000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +103,00:00:04.0800000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +104,00:00:04.1200000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +105,00:00:04.1600000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +106,00:00:04.2000000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +107,00:00:04.2400000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +108,00:00:04.2800000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +109,00:00:04.3200000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +110,00:00:04.3600000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +111,00:00:04.4000000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +112,00:00:04.4400000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +113,00:00:04.4800000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +114,00:00:04.5200000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +115,00:00:04.5600000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +116,00:00:04.6000000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +117,00:00:04.6400000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +118,00:00:04.6800000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +119,00:00:04.7200000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +120,00:00:04.7600000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +121,00:00:04.8000000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +122,00:00:04.8400000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +123,00:00:04.8800000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +124,00:00:04.9200000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +125,00:00:04.9600000,-0.0009937579,-0.0010006869,6.0601068,23.946886,0,0,1,1,0,0 +126,00:00:05,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +127,00:00:05.0400000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +128,00:00:05.0800000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +129,00:00:05.1200000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +130,00:00:05.1600000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +131,00:00:05.2000000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +132,00:00:05.2400000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +133,00:00:05.2800000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +134,00:00:05.3200000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +135,00:00:05.3600000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +136,00:00:05.4000000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +137,00:00:05.4400000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +138,00:00:05.4800000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +139,00:00:05.5200000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +140,00:00:05.5600000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +141,00:00:05.6000000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +142,00:00:05.6400000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +143,00:00:05.6800000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +144,00:00:05.7200000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +145,00:00:05.7600000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +146,00:00:05.8000000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +147,00:00:05.8400000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +148,00:00:05.8800000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +149,00:00:05.9200000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +150,00:00:05.9600000,0.005387768,0.005387768,13.076599,22.984106,0,0,1,1,0,0 +151,00:00:06,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +152,00:00:06.0400000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +153,00:00:06.0800000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +154,00:00:06.1200000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +155,00:00:06.1600000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +156,00:00:06.2000000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +157,00:00:06.2400000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +158,00:00:06.2800000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +159,00:00:06.3200000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +160,00:00:06.3600000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +161,00:00:06.4000000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +162,00:00:06.4400000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +163,00:00:06.4800000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +164,00:00:06.5200000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +165,00:00:06.5600000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +166,00:00:06.6000000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +167,00:00:06.6400000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +168,00:00:06.6800000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +169,00:00:06.7200000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +170,00:00:06.7600000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +171,00:00:06.8000000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +172,00:00:06.8400000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +173,00:00:06.8800000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +174,00:00:06.9200000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 +175,00:00:06.9600000,-7.502735E-05,-7.502735E-05,-65.10224,-65.10224,0,0,1,1,0,0 diff --git a/test/Iris.Tests/data/ExpectedVideoLogFiles/gray_RELATIVE.csv b/test/Iris.Tests/data/ExpectedVideoLogFiles/gray_RELATIVE.csv new file mode 100644 index 0000000..c9a0b50 Binary files /dev/null and b/test/Iris.Tests/data/ExpectedVideoLogFiles/gray_RELATIVE.csv differ diff --git a/test/Iris.Tests/data/ExpectedVideoLogFiles/intermitentEF_RELATIVE.csv b/test/Iris.Tests/data/ExpectedVideoLogFiles/intermitentEF_RELATIVE.csv new file mode 100644 index 0000000..e6e64e4 Binary files /dev/null and b/test/Iris.Tests/data/ExpectedVideoLogFiles/intermitentEF_RELATIVE.csv differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/20stripes.png b/test/Iris.Tests/data/TestImages/Patterns/20stripes.png new file mode 100644 index 0000000..4fef978 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/20stripes.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/ACO_Pattern.png b/test/Iris.Tests/data/TestImages/Patterns/ACO_Pattern.png new file mode 100644 index 0000000..07bf1cd Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/ACO_Pattern.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/ADE.png b/test/Iris.Tests/data/TestImages/Patterns/ADE.png new file mode 100644 index 0000000..1118215 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/ADE.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/ChessBoard.png b/test/Iris.Tests/data/TestImages/Patterns/ChessBoard.png new file mode 100644 index 0000000..65cd9c8 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/ChessBoard.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/Circular1.png b/test/Iris.Tests/data/TestImages/Patterns/Circular1.png new file mode 100644 index 0000000..1493885 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/Circular1.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/Circular2.png b/test/Iris.Tests/data/TestImages/Patterns/Circular2.png new file mode 100644 index 0000000..a98eec2 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/Circular2.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/ChessBoard.png b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/ChessBoard.png new file mode 100644 index 0000000..879ac3d Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/ChessBoard.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/Diagonals.png b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/Diagonals.png new file mode 100644 index 0000000..57cabc7 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/Diagonals.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/HorizontalStripes.png b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/HorizontalStripes.png new file mode 100644 index 0000000..6a50f11 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/HorizontalStripes.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/I9qihah.png b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/I9qihah.png new file mode 100644 index 0000000..5afed42 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/I9qihah.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/It Takes Two - Kaleidoscope Level_Trim_Moment.png b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/It Takes Two - Kaleidoscope Level_Trim_Moment.png new file mode 100644 index 0000000..712e889 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/It Takes Two - Kaleidoscope Level_Trim_Moment.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/It Takes Two - Kaleidoscope Level_Trim_Moment7.png b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/It Takes Two - Kaleidoscope Level_Trim_Moment7.png new file mode 100644 index 0000000..91448f9 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/It Takes Two - Kaleidoscope Level_Trim_Moment7.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/circle.png b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/circle.png new file mode 100644 index 0000000..7b01f13 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/circle.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/flower.png b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/flower.png new file mode 100644 index 0000000..98d4106 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/flower.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/shapes.png b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/shapes.png new file mode 100644 index 0000000..34e85a4 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/CircularExpectedResults/shapes.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/Diagonals.png b/test/Iris.Tests/data/TestImages/Patterns/Diagonals.png new file mode 100644 index 0000000..d242b1b Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/Diagonals.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/HorizontalLines.jpg b/test/Iris.Tests/data/TestImages/Patterns/HorizontalLines.jpg new file mode 100644 index 0000000..e471e86 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/HorizontalLines.jpg differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/HorizontalStripes.jpg b/test/Iris.Tests/data/TestImages/Patterns/HorizontalStripes.jpg new file mode 100644 index 0000000..3907a5b Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/HorizontalStripes.jpg differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/I9qihah.png b/test/Iris.Tests/data/TestImages/Patterns/I9qihah.png new file mode 100644 index 0000000..12b24ea Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/I9qihah.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/It Takes Two - Kaleidoscope Level_Trim_Moment.jpg b/test/Iris.Tests/data/TestImages/Patterns/It Takes Two - Kaleidoscope Level_Trim_Moment.jpg new file mode 100644 index 0000000..1cf8af4 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/It Takes Two - Kaleidoscope Level_Trim_Moment.jpg differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/It Takes Two - Kaleidoscope Level_Trim_Moment7.jpg b/test/Iris.Tests/data/TestImages/Patterns/It Takes Two - Kaleidoscope Level_Trim_Moment7.jpg new file mode 100644 index 0000000..0c8c0ca Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/It Takes Two - Kaleidoscope Level_Trim_Moment7.jpg differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/It Takes Two - Kaleidoscope Level_Trim_Moment8.jpg b/test/Iris.Tests/data/TestImages/Patterns/It Takes Two - Kaleidoscope Level_Trim_Moment8.jpg new file mode 100644 index 0000000..6fef21f Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/It Takes Two - Kaleidoscope Level_Trim_Moment8.jpg differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/ChessBoard.png b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/ChessBoard.png new file mode 100644 index 0000000..80cce3b Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/ChessBoard.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/Diagonals.png b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/Diagonals.png new file mode 100644 index 0000000..6693301 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/Diagonals.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/HorizontalLines.png b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/HorizontalLines.png new file mode 100644 index 0000000..6b798e0 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/HorizontalLines.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/HorizontalStripes.png b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/HorizontalStripes.png new file mode 100644 index 0000000..6a50f11 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/HorizontalStripes.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/I9qihah.png b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/I9qihah.png new file mode 100644 index 0000000..45f8bb4 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/I9qihah.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/ShapedLines.png b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/ShapedLines.png new file mode 100644 index 0000000..01f449a Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/ShapedLines.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/circle.png b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/circle.png new file mode 100644 index 0000000..b1b2b05 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/circle.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/lines1.png b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/lines1.png new file mode 100644 index 0000000..a95271a Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/LineExpectedResults/lines1.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/ShapedLines.png b/test/Iris.Tests/data/TestImages/Patterns/ShapedLines.png new file mode 100644 index 0000000..9bac601 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/ShapedLines.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/circle.png b/test/Iris.Tests/data/TestImages/Patterns/circle.png new file mode 100644 index 0000000..2180ef6 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/circle.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/flower.png b/test/Iris.Tests/data/TestImages/Patterns/flower.png new file mode 100644 index 0000000..bb8ff6c Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/flower.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/lines1.png b/test/Iris.Tests/data/TestImages/Patterns/lines1.png new file mode 100644 index 0000000..ecad653 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/lines1.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/png-clipart-sacred-geometry-circle-pattern-circle-angle-leaf.png b/test/Iris.Tests/data/TestImages/Patterns/png-clipart-sacred-geometry-circle-pattern-circle-angle-leaf.png new file mode 100644 index 0000000..bb8ff6c Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/png-clipart-sacred-geometry-circle-pattern-circle-angle-leaf.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/shapes.png b/test/Iris.Tests/data/TestImages/Patterns/shapes.png new file mode 100644 index 0000000..f9708fb Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/shapes.png differ diff --git a/test/Iris.Tests/data/TestImages/Patterns/stripes.png b/test/Iris.Tests/data/TestImages/Patterns/stripes.png new file mode 100644 index 0000000..7f7d865 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/Patterns/stripes.png differ diff --git a/test/Iris.Tests/data/TestImages/frames/FrameForTest.jpg b/test/Iris.Tests/data/TestImages/frames/FrameForTest.jpg new file mode 100644 index 0000000..a8cc8c8 Binary files /dev/null and b/test/Iris.Tests/data/TestImages/frames/FrameForTest.jpg differ diff --git a/test/Iris.Tests/data/TestVideos/2Hz_5s.mp4 b/test/Iris.Tests/data/TestVideos/2Hz_5s.mp4 new file mode 100644 index 0000000..22a5ed5 Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/2Hz_5s.mp4 differ diff --git a/test/Iris.Tests/data/TestVideos/2Hz_6s.mp4 b/test/Iris.Tests/data/TestVideos/2Hz_6s.mp4 new file mode 100644 index 0000000..33d4418 Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/2Hz_6s.mp4 differ diff --git a/test/Iris.Tests/data/TestVideos/3Hz_6s.mp4 b/test/Iris.Tests/data/TestVideos/3Hz_6s.mp4 new file mode 100644 index 0000000..b896e30 Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/3Hz_6s.mp4 differ diff --git a/test/Iris.Tests/data/TestVideos/AreaTest.avi b/test/Iris.Tests/data/TestVideos/AreaTest.avi new file mode 100644 index 0000000..3a87753 Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/AreaTest.avi differ diff --git a/test/Iris.Tests/data/TestVideos/GradualRedIncrease.mp4 b/test/Iris.Tests/data/TestVideos/GradualRedIncrease.mp4 new file mode 100644 index 0000000..30eba47 Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/GradualRedIncrease.mp4 differ diff --git a/test/Iris.Tests/data/TestVideos/Hypno Spiral Small.mp4 b/test/Iris.Tests/data/TestVideos/Hypno Spiral Small.mp4 new file mode 100644 index 0000000..646c2fc Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/Hypno Spiral Small.mp4 differ diff --git a/test/Iris.Tests/data/TestVideos/Pattern - Circular 4.avi b/test/Iris.Tests/data/TestVideos/Pattern - Circular 4.avi new file mode 100644 index 0000000..be92ece Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/Pattern - Circular 4.avi differ diff --git a/test/Iris.Tests/data/TestVideos/Pattern - Circular 5.avi b/test/Iris.Tests/data/TestVideos/Pattern - Circular 5.avi new file mode 100644 index 0000000..1fcc493 Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/Pattern - Circular 5.avi differ diff --git a/test/Iris.Tests/data/TestVideos/blackToWhite.mp4 b/test/Iris.Tests/data/TestVideos/blackToWhite.mp4 new file mode 100644 index 0000000..1f7d370 Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/blackToWhite.mp4 differ diff --git a/test/Iris.Tests/data/TestVideos/extendedFLONG.mp4 b/test/Iris.Tests/data/TestVideos/extendedFLONG.mp4 new file mode 100644 index 0000000..a18981a Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/extendedFLONG.mp4 differ diff --git a/test/Iris.Tests/data/TestVideos/flashStripes.mp4 b/test/Iris.Tests/data/TestVideos/flashStripes.mp4 new file mode 100644 index 0000000..3229ca2 Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/flashStripes.mp4 differ diff --git a/test/Iris.Tests/data/TestVideos/gradRed2.mp4 b/test/Iris.Tests/data/TestVideos/gradRed2.mp4 new file mode 100644 index 0000000..a538298 Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/gradRed2.mp4 differ diff --git a/test/Iris.Tests/data/TestVideos/gray.mp4 b/test/Iris.Tests/data/TestVideos/gray.mp4 new file mode 100644 index 0000000..e629504 Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/gray.mp4 differ diff --git a/test/Iris.Tests/data/TestVideos/intermitentEF.mp4 b/test/Iris.Tests/data/TestVideos/intermitentEF.mp4 new file mode 100644 index 0000000..6ac5385 Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/intermitentEF.mp4 differ diff --git a/test/Iris.Tests/data/TestVideos/pixel_track.mp4 b/test/Iris.Tests/data/TestVideos/pixel_track.mp4 new file mode 100644 index 0000000..f233f7b Binary files /dev/null and b/test/Iris.Tests/data/TestVideos/pixel_track.mp4 differ diff --git a/test/Iris.Tests/include/IrisLibTest.h b/test/Iris.Tests/include/IrisLibTest.h new file mode 100644 index 0000000..ed7e10a --- /dev/null +++ b/test/Iris.Tests/include/IrisLibTest.h @@ -0,0 +1,88 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once + +#include +#include +#include +#include +#include "iris/Configuration.h" +#include + +//redefine logging macros +#ifdef LOG_CORE_DEBUG + +#undef LOG_CORE_DEBUG +#undef LOG_CORE_TRACE +#undef LOG_CORE_INFO +#undef LOG_CORE_ERROR +#undef LOG_CORE_WARNING +#undef LOG_CORE_CRITICAL +#undef LOG_DATA_INFO + +#define LOG_CORE_DEBUG +#define LOG_CORE_TRACE +#define LOG_CORE_INFO +#define LOG_CORE_ERROR +#define LOG_CORE_WARNING +#define LOG_CORE_CRITICAL +#define LOG_DATA_INFO + +#endif // !LOG_CORE_DEBUG + + + +namespace iris::Tests +{ + static cv::Scalar white(255, 255, 255); + static cv::Scalar gray(128, 128, 128); + static cv::Scalar black(0, 0, 0); + static cv::Scalar blue(255, 0, 0); + static cv::Scalar red(0, 0, 255); + + /// + /// Auxiliar method to display test images + /// + /// image to display + /// window name + static void Display(cv::Mat& mat, std::string w = "Test") + { + cv::imshow(w, mat); + cv::waitKey(); + cv::destroyAllWindows(); + } + + //auxiliar method to get canny edge detection lines from image + static cv::Mat* GetCanny(const char* imageFile, int cannyTh1, int cannyTh2) + { + cv::Mat image = cv::imread(imageFile); + cv::cvtColor(image, image, cv::ColorConversionCodes::COLOR_BGR2GRAY); + cv::medianBlur(image, image, 5); + + cv::Mat* canny = new cv::Mat(); + cv::Canny(image, *canny, cannyTh1, cannyTh2); + + return canny; + } + + + static bool CompareFloat(const float& a, const float& b) + { + return fabs(a - b) < 0.0001f; + } + + + class IrisLibTest : public ::testing::Test { + protected: + Configuration configuration; + + void SetUp() override { + cv::utils::logging::setLogLevel(cv::utils::logging::LogLevel::LOG_LEVEL_ERROR); + + iris::Log::Init(false, false); + + //Load configuration + configuration.Init(); + } + }; +} \ No newline at end of file diff --git a/test/Iris.Tests/src/CDLuminanceTest.cpp b/test/Iris.Tests/src/CDLuminanceTest.cpp new file mode 100644 index 0000000..1c3102b --- /dev/null +++ b/test/Iris.Tests/src/CDLuminanceTest.cpp @@ -0,0 +1,146 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include +#include "utils/FrameConverter.h" +#include "CDLuminance.h" +#include "IrisLibTest.h" +#include "IrisFrame.h" +#include + +namespace iris::Tests +{ + class CDLuminanceTest : public IrisLibTest { + protected: + EA::EACC::Utils::FrameConverter* frameRgbConverter = nullptr; + void SetUp() override { + configuration.SetLuminanceType(Configuration::LuminanceType::CD); + IrisLibTest::SetUp(); + frameRgbConverter = new EA::EACC::Utils::FrameConverter(configuration.GetFrameCDLuminanceConverterParams()); + } + ~CDLuminanceTest() override { + delete frameRgbConverter; + } + }; + + TEST_F(CDLuminanceTest, Luminance_When_WhiteFrame_Test) + { + cv::Size size(1280, 720); + iris::CDLuminance cdLuminance(frameRgbConverter, 3, size, configuration.GetLuminanceFlashParams()); + cv::Mat imageBgr(size, CV_8UC3, white); + + cdLuminance.SetCurrentFrame(&imageBgr); + cv::Mat* luminance = cdLuminance.getCurrentFrame(); + float testLum = luminance->at(0, 0); + EXPECT_EQ(200, testLum); + + imageBgr.release(); + } + + TEST_F(CDLuminanceTest, Luminance_When_BlackFrame_Test) + { + cv::Size size(1280, 720); + iris::CDLuminance cdLuminance(frameRgbConverter, 3, size, configuration.GetLuminanceFlashParams()); + cv::Mat imageBgr(size, CV_8UC3, black); + + cdLuminance.SetCurrentFrame(&imageBgr); + cv::Mat* luminance = cdLuminance.getCurrentFrame(); + float testLum = luminance->at(0, 0); + EXPECT_EQ(0.07f, testLum); + } + + TEST_F(CDLuminanceTest, Luminance_When_GrayFrame_Test) + { + cv::Size size(1280, 720); + iris::CDLuminance cdLuminance(frameRgbConverter, 3, size, configuration.GetLuminanceFlashParams()); + cv::Mat imageBgr(size, CV_8UC3, gray); + + cdLuminance.SetCurrentFrame(&imageBgr); + cv::Mat* luminance = cdLuminance.getCurrentFrame(); + float testLum = luminance->at(0, 0); + EXPECT_EQ(46.48f, testLum); + + imageBgr.release(); + } + + TEST_F(CDLuminanceTest, Luminance_When_BlueFrame_Test) + { + cv::Size size(1280, 720); + CDLuminance cdLuminance(frameRgbConverter, 3, size, configuration.GetLuminanceFlashParams()); + cv::Mat imageBgr(size, CV_8UC3, blue); + + cdLuminance.SetCurrentFrame(&imageBgr); + cv::Mat* luminance = cdLuminance.getCurrentFrame(); + float testLum = luminance->at(0, 0); + EXPECT_EQ(2.53f, testLum); + + imageBgr.release(); + } + + TEST_F(CDLuminanceTest, SafeArea_No_Change_Threshold) + { + cv::Size size(5, 5); + CDLuminance cdLuminance(frameRgbConverter, 3, size, configuration.GetLuminanceFlashParams()); + + cv::Mat imageBgr(size, CV_8UC3, blue); + cdLuminance.SetCurrentFrame(&imageBgr); + cdLuminance.SetCurrentFrame(&imageBgr); + + cv::Mat* frameDiff = cdLuminance.FrameDifference(); + float avgDifference = cdLuminance.CheckSafeArea(frameDiff); + EXPECT_EQ(0, avgDifference); + + float flashAreaProportion = cdLuminance.GetFlashArea(); + EXPECT_EQ(0, flashAreaProportion); + + delete frameDiff; + } + + TEST_F(CDLuminanceTest, SafeArea_20_Percent_Change_Threshold) + { + cv::Size size(5, 5); + + float frameDiffArray[5][5] = { + { 200, 200, 200, 0, 0 }, + { 200, 200, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 } + }; + cv::Mat frameDiff = cv::Mat(size, CV_32FC1, &frameDiffArray); + + CDLuminance cdLuminance(frameRgbConverter, 3, size, configuration.GetLuminanceFlashParams()); + + cv::Mat imageBgr(size, CV_8UC3, blue); + cdLuminance.SetCurrentFrame(&imageBgr); + + cv::Mat imageBgr2(size, CV_8UC3, white); + cdLuminance.SetCurrentFrame(&imageBgr2); + + float avgDifference = cdLuminance.CheckSafeArea(&frameDiff); + EXPECT_EQ(0, avgDifference); + + float flashAreaProportion = cdLuminance.GetFlashArea(); + EXPECT_TRUE(CompareFloat(0.2, flashAreaProportion)); + } + + TEST_F(CDLuminanceTest, SafeArea_100_Percent_Change_Threshold) + { + cv::Size size(5, 5); + CDLuminance cdLuminance(frameRgbConverter, 3, size, configuration.GetLuminanceFlashParams()); + + cv::Mat imageBgr(size, CV_8UC3, blue); + cdLuminance.SetCurrentFrame(&imageBgr); + + cv::Mat imageBgr2(size, CV_8UC3, white); + cdLuminance.SetCurrentFrame(&imageBgr2); + + cv::Mat* frameDiff = cdLuminance.FrameDifference(); + float avgDifference = cdLuminance.CheckSafeArea(frameDiff); + EXPECT_TRUE(CompareFloat(197.47, avgDifference)); + + float flashAreaProportion = cdLuminance.GetFlashArea(); + EXPECT_EQ(1, flashAreaProportion); + + delete frameDiff; + } +} diff --git a/test/Iris.Tests/src/FlashDetectionTests.cpp b/test/Iris.Tests/src/FlashDetectionTests.cpp new file mode 100644 index 0000000..8baf3d4 --- /dev/null +++ b/test/Iris.Tests/src/FlashDetectionTests.cpp @@ -0,0 +1,173 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include +#include "IrisLibTest.h" +#include +#include "FlashDetection.h" +#include "FrameData.h" +#include "utils/FrameConverter.h" +#include "IrisFrame.h" + +namespace iris::Tests +{ + class FlashDetectionTests : public IrisLibTest { + protected: + + void SetUp() override { //call configuration loading per test to load different luminances + cv::utils::logging::setLogLevel(cv::utils::logging::LogLevel::LOG_LEVEL_ERROR); + iris::Log::Init(false, false); + } + ~FlashDetectionTests() override { + } + }; + + TEST_F(FlashDetectionTests, RELATIVE_LUMINANCE) + { + + configuration.SetLuminanceType(Configuration::LuminanceType::RELATIVE); + configuration.Init(); + + + cv::Size size(100, 100); + + cv::Mat blackFrame(size, CV_8UC3, black); //lum = 0f + cv::Mat whiteFrame(size, CV_8UC3, white); //lum = 1f + cv::Mat redFrame(size, CV_8UC3, red); //lum = 0.2126f + + FlashDetection flashDetection(&configuration, 7, size); + EA::EACC::Utils::FrameConverter sRgbConverter(configuration.GetFrameSrgbConverterParams()); + + FrameData data; + IrisFrame irisBlackFrame(&blackFrame, sRgbConverter.Convert(blackFrame), FrameData()); + IrisFrame irisWhiteFrame(&whiteFrame, sRgbConverter.Convert(whiteFrame), FrameData()); + IrisFrame irisRedFrame(&redFrame, sRgbConverter.Convert(redFrame), FrameData()); + + //add transitions + flashDetection.setLuminance(irisBlackFrame); + flashDetection.checkFrame(irisBlackFrame, 0, data); + + flashDetection.setLuminance(irisWhiteFrame); + flashDetection.checkFrame(irisWhiteFrame, 1, data); + + flashDetection.setLuminance(irisRedFrame); + flashDetection.checkFrame(irisRedFrame, 2, data); + + flashDetection.setLuminance(irisWhiteFrame); + flashDetection.checkFrame(irisWhiteFrame, 3, data); + + flashDetection.setLuminance(irisRedFrame); + flashDetection.checkFrame(irisRedFrame, 4, data); + + FrameData expectedData; + expectedData.Frame = 0; + expectedData.LuminanceAverage = 0.2126f; + expectedData.LuminanceFlashArea = "100.00%"; + expectedData.AverageLuminanceDiff = -0.7874f; + expectedData.AverageLuminanceDiffAcc = -0.7874f; + expectedData.RedAverage = 320; + expectedData.RedFlashArea = "100.00%"; + expectedData.AverageRedDiff = 320; + expectedData.AverageRedDiffAcc = 320; + expectedData.LuminanceTransitions = 4; + expectedData.RedTransitions = 3; + expectedData.LuminanceExtendedFailCount = 1; + expectedData.RedExtendedFailCount = 0; + expectedData.luminanceFrameResult = FlashResult::PassWithWarning; + expectedData.redFrameResult = FlashResult::Pass; + + EXPECT_EQ(expectedData.LuminanceAverage, data.LuminanceAverage); + EXPECT_EQ(expectedData.LuminanceFlashArea, data.LuminanceFlashArea); + EXPECT_EQ(expectedData.AverageLuminanceDiff, data.AverageLuminanceDiff); + EXPECT_EQ(expectedData.AverageLuminanceDiffAcc, data.AverageLuminanceDiffAcc); + EXPECT_EQ(expectedData.RedAverage, data.RedAverage); + EXPECT_EQ(expectedData.RedFlashArea, data.RedFlashArea); + EXPECT_EQ(expectedData.AverageRedDiff, data.AverageRedDiff); + EXPECT_EQ(expectedData.AverageRedDiffAcc, data.AverageRedDiffAcc); + EXPECT_EQ(expectedData.LuminanceTransitions, data.LuminanceTransitions); + EXPECT_EQ(expectedData.RedTransitions, data.RedTransitions); + EXPECT_EQ(expectedData.LuminanceExtendedFailCount, data.LuminanceExtendedFailCount); + EXPECT_EQ(expectedData.RedExtendedFailCount, data.RedExtendedFailCount); + EXPECT_EQ(expectedData.luminanceFrameResult, data.luminanceFrameResult); + EXPECT_EQ(expectedData.redFrameResult, data.redFrameResult); + + EXPECT_FALSE(flashDetection.isFail()); + + irisBlackFrame.Release(); + irisRedFrame.Release(); + irisWhiteFrame.Release(); + } + + TEST_F(FlashDetectionTests, CD_LUMINANCE) + { + configuration.SetLuminanceType(Configuration::LuminanceType::CD); + configuration.Init(); + + cv::Size size(100, 100); + + cv::Mat blackFrame(size, CV_8UC3, black); //lum = 0.07f + cv::Mat whiteFrame(size, CV_8UC3, white); //lum = 200f + cv::Mat redFrame(size, CV_8UC3, red); //lum = 015.93f + + FlashDetection flashDetection(&configuration, 7, size); + EA::EACC::Utils::FrameConverter sRgbConverter(configuration.GetFrameSrgbConverterParams()); + + FrameData data; + IrisFrame irisBlackFrame(&blackFrame, sRgbConverter.Convert(blackFrame), FrameData()); + IrisFrame irisWhiteFrame(&whiteFrame, sRgbConverter.Convert(whiteFrame), FrameData()); + IrisFrame irisRedFrame(&redFrame, sRgbConverter.Convert(redFrame), FrameData()); + + //add transitions + flashDetection.setLuminance(irisBlackFrame); + flashDetection.checkFrame(irisBlackFrame, 0, data); + + flashDetection.setLuminance(irisWhiteFrame); + flashDetection.checkFrame(irisWhiteFrame, 1, data); + + flashDetection.setLuminance(irisRedFrame); + flashDetection.checkFrame(irisRedFrame, 2, data); + + flashDetection.setLuminance(irisWhiteFrame); + flashDetection.checkFrame(irisWhiteFrame, 3, data); + + flashDetection.setLuminance(irisRedFrame); + flashDetection.checkFrame(irisRedFrame, 4, data); + + FrameData expectedData; + expectedData.Frame = 0; + expectedData.LuminanceAverage = 15.93f; + expectedData.LuminanceFlashArea = "100.00%"; + expectedData.AverageLuminanceDiff = -184.07f; + expectedData.AverageLuminanceDiffAcc = -184.07f; + expectedData.RedAverage = 320; + expectedData.RedFlashArea = "100.00%"; + expectedData.AverageRedDiff = 320; + expectedData.AverageRedDiffAcc = 320; + expectedData.LuminanceTransitions = 4; + expectedData.RedTransitions = 3; + expectedData.LuminanceExtendedFailCount = 1; + expectedData.RedExtendedFailCount = 0; + expectedData.luminanceFrameResult = FlashResult::PassWithWarning; + expectedData.redFrameResult = FlashResult::Pass; + + EXPECT_EQ(expectedData.LuminanceAverage, data.LuminanceAverage); + EXPECT_EQ(expectedData.LuminanceFlashArea, data.LuminanceFlashArea); + EXPECT_EQ(expectedData.AverageLuminanceDiff, data.AverageLuminanceDiff); + EXPECT_EQ(expectedData.AverageLuminanceDiffAcc, data.AverageLuminanceDiffAcc); + EXPECT_EQ(expectedData.RedAverage, data.RedAverage); + EXPECT_EQ(expectedData.RedFlashArea, data.RedFlashArea); + EXPECT_EQ(expectedData.AverageRedDiff, data.AverageRedDiff); + EXPECT_EQ(expectedData.AverageRedDiffAcc, data.AverageRedDiffAcc); + EXPECT_EQ(expectedData.LuminanceTransitions, data.LuminanceTransitions); + EXPECT_EQ(expectedData.RedTransitions, data.RedTransitions); + EXPECT_EQ(expectedData.LuminanceExtendedFailCount, data.LuminanceExtendedFailCount); + EXPECT_EQ(expectedData.RedExtendedFailCount, data.RedExtendedFailCount); + EXPECT_EQ(expectedData.luminanceFrameResult, data.luminanceFrameResult); + EXPECT_EQ(expectedData.redFrameResult, data.redFrameResult); + + EXPECT_FALSE(flashDetection.isFail()); + + irisBlackFrame.Release(); + irisRedFrame.Release(); + irisWhiteFrame.Release(); + } +} \ No newline at end of file diff --git a/test/Iris.Tests/src/FlashTest.cpp b/test/Iris.Tests/src/FlashTest.cpp new file mode 100644 index 0000000..b39ae03 --- /dev/null +++ b/test/Iris.Tests/src/FlashTest.cpp @@ -0,0 +1,81 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include "IrisLibTest.h" +#include +#include "utils/FrameConverter.h" +#include "Flash.h" +#include + +namespace iris::Tests +{ + class FlashTest : public IrisLibTest { + protected: + EA::EACC::Utils::FrameConverter* frameRgbConverter; + void SetUp() override { + IrisLibTest::SetUp(); + frameRgbConverter = new EA::EACC::Utils::FrameConverter(configuration.GetFrameSrgbConverterParams()); + } + ~FlashTest() override { + delete frameRgbConverter; + } + }; + + class FlashWrapper : public Flash { + public: + FlashWrapper(const short& fps, const cv::Size& size, FlashParams* params) : Flash(fps, size, params) { + } + }; + + TEST_F(FlashTest, Transition_Over_Dark_Threshold) + { + cv::Size size(1, 1); + Flash flash(5, size, configuration.GetLuminanceFlashParams()); + + cv::Mat frame(size, CV_32FC1, cv::Scalar(0.8f)); + cv::Mat frame2(size, CV_32FC1, cv::Scalar(1.0f)); + cv::Mat frame3(size, CV_32FC1, cv::Scalar(0.85f)); + + flash.SetCurrentFrame(&frame); + flash.SetCurrentFrame(&frame2); + + Flash::CheckTransitionResult res = flash.CheckTransition(0.2f, 0.0f); + EXPECT_FALSE(res.checkResult); + + flash.SetCurrentFrame(&frame3); + flash.CheckTransition(-0.15f, 0.2f); + EXPECT_FALSE(res.checkResult); + } + + TEST_F(FlashTest, Transition_Below_Dark_Threshold) + { + cv::Size size(1, 1); + Flash flash(5, size, configuration.GetLuminanceFlashParams()); + + cv::Mat frame(size, CV_32FC1, cv::Scalar(0.6f)); + cv::Mat frame2(size, CV_32FC1, cv::Scalar(1.0f)); + cv::Mat frame3(size, CV_32FC1, cv::Scalar(0.4f)); + + flash.SetCurrentFrame(&frame); + flash.SetCurrentFrame(&frame2); + + Flash::CheckTransitionResult res = flash.CheckTransition(0.4f, 0.0f); + EXPECT_TRUE(res.checkResult); + + flash.SetCurrentFrame(&frame3); + flash.CheckTransition(-0.6f, 0.4f); + EXPECT_TRUE(res.checkResult); + } + + //Edge case test: when a flash transition occurrs but the value continues increasing/decreasing, no new transition is occurring + TEST_F(FlashTest, CheckTransition_Same_Sign_Above_Threshold) + { + cv::Size size(1, 1); + Flash flash(2, size, configuration.GetLuminanceFlashParams()); + + flash.CheckTransition(0.01f, 0.0f); + flash.CheckTransition(0.09f, 0.01f); + Flash::CheckTransitionResult res = flash.CheckTransition(0.02f, 0.1f); + + EXPECT_FALSE(res.checkResult); + } +} \ No newline at end of file diff --git a/test/Iris.Tests/src/FrameRgbConverterTest.cpp b/test/Iris.Tests/src/FrameRgbConverterTest.cpp new file mode 100644 index 0000000..a7e27e2 --- /dev/null +++ b/test/Iris.Tests/src/FrameRgbConverterTest.cpp @@ -0,0 +1,40 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include +#include "utils/FrameConverter.h" +#include "IrisLibTest.h" +#include + +namespace iris::Tests +{ + class FrameRgbConverterTest : public IrisLibTest { + }; + + TEST_F(FrameRgbConverterTest, MemoryLeakTest) + { + uint8_t testArr[3][3] = { + { 0, 1, 2 }, + { 3, 3, 2 }, + { 1, 0, 0 } + }; + cv::Mat bgrMat = cv::Mat(3, 3, CV_8U, &testArr); + + EA::EACC::Utils::FrameConverter* converter = new EA::EACC::Utils::FrameConverter(configuration.GetFrameSrgbConverterParams()); + cv::Mat* sRgbMat = converter->Convert(bgrMat); + + for (int x = 0; x < sRgbMat->cols; x++) { + for (int y = 0; y < sRgbMat->cols; y++) { + int vSource = (int)testArr[x][y]; + cv::Mat* m = converter->GetTable(); + float fTarget = m->at(vSource, 0); + float fCurrent = sRgbMat->at(x, y); + EXPECT_EQ(fCurrent, fTarget); + } + } + + sRgbMat->release(); + delete sRgbMat; + bgrMat.release(); + delete converter; + } +} \ No newline at end of file diff --git a/test/Iris.Tests/src/PatternDetectionTests.cpp b/test/Iris.Tests/src/PatternDetectionTests.cpp new file mode 100644 index 0000000..749704c --- /dev/null +++ b/test/Iris.Tests/src/PatternDetectionTests.cpp @@ -0,0 +1,92 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include "IrisLibTest.h" +#include +#include "PatternDetection.h" +#include "FlashDetection.h" +#include "IrisFrame.h" +#include "utils/FrameConverter.h" +#include "iris/TotalFlashIncidents.h" + +namespace iris::Tests +{ + class PatternDetectionTests : public IrisLibTest { + protected: + EA::EACC::Utils::FrameConverter* frameRgbConverter = nullptr; + void SetUp() override { + configuration.SetLuminanceType(Configuration::LuminanceType::RELATIVE); + IrisLibTest::SetUp(); + + frameRgbConverter = new EA::EACC::Utils::FrameConverter(configuration.GetFrameSrgbConverterParams()); + } + + ~PatternDetectionTests() + { + if (frameRgbConverter != nullptr) + { + delete frameRgbConverter; + } + } + }; + + TEST_F(PatternDetectionTests, NoPattern_Pass) + { + cv::Mat image = cv::imread("data/TestImages/Patterns/shapes.png"); + IrisFrame irisFrame(&image, frameRgbConverter->Convert(image), FrameData()); + + FlashDetection flashDetection(&configuration, 0, image.size()); + PatternDetection patternDetection(&configuration, 5, image.size()); + flashDetection.setLuminance(irisFrame); + + FrameData data; + for (int i = 0; i < 5; i++) + { + patternDetection.checkFrame(irisFrame, i, data); + } + + EXPECT_EQ(PatternResult::Pass, data.patternFrameResult); + + irisFrame.Release(); + } + + + TEST_F(PatternDetectionTests, Straight_Lines_Fail) + { + cv::Mat image = cv::imread("data/TestImages/Patterns/20stripes.png"); + IrisFrame irisFrame(&image, frameRgbConverter->Convert(image), FrameData()); + + FlashDetection flashDetection(&configuration, 0, image.size()); + PatternDetection patternDetection(&configuration, 5, image.size()); + flashDetection.setLuminance(irisFrame); + + FrameData data; + for (int i = 0; i < 5; i++) + { + patternDetection.checkFrame(irisFrame, i, data); + } + + EXPECT_EQ(PatternResult::Fail, data.patternFrameResult); + + irisFrame.Release(); + } + + TEST_F(PatternDetectionTests, Straight_Lines_Pass) + { + cv::Mat image = cv::imread("data/TestImages/Patterns/ACO_Pattern.png"); + IrisFrame irisFrame(&image, frameRgbConverter->Convert(image), FrameData()); + + FlashDetection flashDetection(&configuration, 0, image.size()); + PatternDetection patternDetection(&configuration, 5, image.size()); + flashDetection.setLuminance(irisFrame); + + FrameData data; + for (int i = 0; i < 5; i++) + { + patternDetection.checkFrame(irisFrame, i, data); + } + + EXPECT_EQ(PatternResult::Pass, data.patternFrameResult); + + irisFrame.Release(); + } +} \ No newline at end of file diff --git a/test/Iris.Tests/src/RedSaturationTests.cpp b/test/Iris.Tests/src/RedSaturationTests.cpp new file mode 100644 index 0000000..2102fb9 --- /dev/null +++ b/test/Iris.Tests/src/RedSaturationTests.cpp @@ -0,0 +1,387 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include +#include "utils/FrameConverter.h" +#include "RedSaturation.h" +#include "IrisLibTest.h" +#include + +namespace iris::Tests +{ + class RedSaturationTests : public IrisLibTest { + protected: + EA::EACC::Utils::FrameConverter* frameRgbConverter = nullptr; + + void SetUp() override { + IrisLibTest::SetUp(); + frameRgbConverter = new EA::EACC::Utils::FrameConverter(configuration.GetFrameSrgbConverterParams()); + } + ~RedSaturationTests() override { + delete frameRgbConverter; + } + }; + + TEST_F(RedSaturationTests, Positive_FrameDifference) + { + cv::Size size(100, 100); + RedSaturation redSaturation(0, size, configuration.GetRedSaturationFlashParams()); + cv::Mat redImageBgr(size, CV_8UC3, red); + cv::Mat* pRedSrgb = frameRgbConverter->Convert(redImageBgr); + + cv::Mat blackImageBgr(size, CV_8UC3, black); + cv::Mat* pBlackSrgb = frameRgbConverter->Convert(blackImageBgr); + + redSaturation.SetCurrentFrame(pBlackSrgb); + redSaturation.SetCurrentFrame(pRedSrgb); + + cv::Mat* matDiff = redSaturation.FrameDifference(); + + float testChangeValues = matDiff->at(0, 0); + + EXPECT_EQ(320, testChangeValues); + + if (matDiff != nullptr) { + matDiff->release(); + delete matDiff; + } + + delete pRedSrgb; + delete pBlackSrgb; + } + + TEST_F(RedSaturationTests, Negative_FrameDifference) + { + cv::Size size(2000, 2000); + RedSaturation redSaturation(0, size, configuration.GetRedSaturationFlashParams()); + cv::Mat redImageBgr(size, CV_8UC3, red); + cv::Mat* pRedSrgb = frameRgbConverter->Convert(redImageBgr); + + cv::Mat blackImageBgr(size, CV_8UC3, black); + cv::Mat* pBlackSrgb = frameRgbConverter->Convert(blackImageBgr); + + redSaturation.SetCurrentFrame(pRedSrgb); + redSaturation.SetCurrentFrame(pBlackSrgb); + + cv::Mat* matDiff = redSaturation.FrameDifference(); + + float testChangeValues = matDiff->at(0, 0); + + EXPECT_EQ(-320, testChangeValues); + + if (matDiff != nullptr) { + matDiff->release(); + delete matDiff; + } + + delete pRedSrgb; + delete pBlackSrgb; + } + + TEST_F(RedSaturationTests, Positive_FrameDifference_PartialRed) + { + cv::Size size(100, 100); + RedSaturation redSaturation(0, size, configuration.GetRedSaturationFlashParams()); + + cv::Mat blackImageBgr(size, CV_8UC3, black); + cv::Mat* pBlackSrgb = frameRgbConverter->Convert(blackImageBgr); + + cv::Mat redImageBgr(size, CV_8UC3, black); + cv::rectangle(redImageBgr, cv::Rect(20, 20, 30, 30), red, -1); + cv::Mat* pRedSrgb = frameRgbConverter->Convert(redImageBgr); + + redSaturation.SetCurrentFrame(pBlackSrgb); + redSaturation.SetCurrentFrame(pRedSrgb); + + cv::Mat* matDiff = redSaturation.FrameDifference(); + + float testChangeValues = matDiff->at(21, 21); + float testNullChangeValues = matDiff->at(0, 0); + + EXPECT_EQ(320, testChangeValues); + EXPECT_EQ(0, testNullChangeValues); + + if (matDiff != nullptr) { + matDiff->release(); + delete matDiff; + } + + delete pRedSrgb; + delete pBlackSrgb; + } + + TEST_F(RedSaturationTests, Negative_FrameDifference_PartialRed) + { + cv::Size size(100, 100); + RedSaturation redSaturation(0, size, configuration.GetRedSaturationFlashParams()); + + cv::Mat blackImageBgr(size, CV_8UC3, black); + cv::Mat* pBlackSrgb = frameRgbConverter->Convert(blackImageBgr); + + cv::Mat redImageBgr(size, CV_8UC3, black); + cv::rectangle(redImageBgr, cv::Rect(20, 20, 30, 30), red, -1); + cv::Mat* pRedSrgb = frameRgbConverter->Convert(redImageBgr); + + + redSaturation.SetCurrentFrame(pRedSrgb); + redSaturation.SetCurrentFrame(pBlackSrgb); + + cv::Mat* matDiff = redSaturation.FrameDifference(); + + float testChangeValues = matDiff->at(21, 21); + float testNullChangeValues = matDiff->at(0, 0); + + EXPECT_EQ(-320, testChangeValues); + EXPECT_EQ(0, testNullChangeValues); + + if (matDiff != nullptr) { + matDiff->release(); + delete matDiff; + } + + delete pRedSrgb; + delete pBlackSrgb; + } + + TEST_F(RedSaturationTests, CheckTransition_Positive_Value_To_Zero) + { + RedSaturation redSaturation (5, cv::Size(), configuration.GetRedSaturationFlashParams()); + + float testLastAvg = 5.2f; + + //SameSign (positive) 0 case && !newTransition + + Flash::CheckTransitionResult transitionResult = redSaturation.CheckTransition(0, testLastAvg); + + EXPECT_EQ(5.2f, transitionResult.lastAvgDiffAcc); + EXPECT_FALSE(transitionResult.checkResult); + } + + TEST_F(RedSaturationTests, CheckTransition_Positive_Value_Increment_NoTransition) + { + RedSaturation redSaturation(5, cv::Size(), configuration.GetRedSaturationFlashParams()); + + + float testLastAvg = 5.2f; + + //SameSign (positive) && !newTransition + + Flash::CheckTransitionResult transitionResult = redSaturation.CheckTransition(10.0f, testLastAvg); + + EXPECT_EQ(15.2f, transitionResult.lastAvgDiffAcc); + EXPECT_FALSE(transitionResult.checkResult); + } + + TEST_F(RedSaturationTests, CheckTransition_Positive_Value_Increment_Transition) + { + RedSaturation redSaturation(5, cv::Size(), configuration.GetRedSaturationFlashParams()); + + + float testLastAvg = 15.2f; + + //SameSign (positive) && newTransition + + Flash::CheckTransitionResult transitionResult = redSaturation.CheckTransition(5.0f, testLastAvg); + + EXPECT_EQ(20.2f, transitionResult.lastAvgDiffAcc); + EXPECT_TRUE(transitionResult.checkResult); + } + + TEST_F(RedSaturationTests, CheckTransition_Positive_Value_Decrement_NoTransition) + { + RedSaturation redSaturation(5, cv::Size(), configuration.GetRedSaturationFlashParams()); + + + float testLastAvg = 20.2f; + + //SameSign (positive) && !newTransition (last was a transition) + + Flash::CheckTransitionResult transitionResult = redSaturation.CheckTransition(10.3f, testLastAvg); + + EXPECT_EQ(30.5f, transitionResult.lastAvgDiffAcc); + EXPECT_FALSE(transitionResult.checkResult); + } + + TEST_F(RedSaturationTests, CheckTransition_ToNegative_Transition) + { + RedSaturation redSaturation(5, cv::Size(), configuration.GetRedSaturationFlashParams()); + + + float testLastAvg = 30.5f; + + //!SameSign (negative) && newTransition + + Flash::CheckTransitionResult transitionResult = redSaturation.CheckTransition(-20.0f, testLastAvg); + + EXPECT_EQ(-20.0f, transitionResult.lastAvgDiffAcc); + EXPECT_TRUE(transitionResult.checkResult); + } + + TEST_F(RedSaturationTests, CheckTransition_ToPositive_NoTransition) + { + RedSaturation redSaturation(5, cv::Size(), configuration.GetRedSaturationFlashParams()); + + + float testLastAvg = -20.0f; + + //SameSign (positive) && !newTransition (last was a transition) + + Flash::CheckTransitionResult transitionResult = redSaturation.CheckTransition(25.0f, testLastAvg); + + EXPECT_EQ(25.0f, transitionResult.lastAvgDiffAcc); + EXPECT_TRUE(transitionResult.checkResult); + } + + TEST_F(RedSaturationTests, CheckTransition_ToNegative_NoTransition) + { + RedSaturation redSaturation(5, cv::Size(), configuration.GetRedSaturationFlashParams()); + + + float testLastAvg = 25.0f; + + //!SameSign (negative) && !newTransition + + Flash::CheckTransitionResult transitionResult = redSaturation.CheckTransition(-10.0f, testLastAvg); + + EXPECT_EQ(-10.0f, transitionResult.lastAvgDiffAcc); + EXPECT_FALSE(transitionResult.checkResult); + } + + TEST_F(RedSaturationTests, CheckTransition_Negative_Value_To_Zero) + { + RedSaturation redSaturation(5, cv::Size(), configuration.GetRedSaturationFlashParams()); + + + float testLastAvg = -10.0f; + + //SameSign (negative) 0 case && !newTransition + + Flash::CheckTransitionResult transitionResult = redSaturation.CheckTransition(0.0f, testLastAvg); + + EXPECT_EQ(-10.0f, transitionResult.lastAvgDiffAcc); + EXPECT_FALSE(transitionResult.checkResult); + } + + TEST_F(RedSaturationTests, CheckTransition_Negative_Value_Decrement_Transition) + { + RedSaturation redSaturation(5, cv::Size(), configuration.GetRedSaturationFlashParams()); + + float testLastAvg = -10.0f; + + //SameSign (negative) && newTransition + Flash::CheckTransitionResult transitionResult = redSaturation.CheckTransition(-15.0f, testLastAvg); + + EXPECT_EQ(-25.0f, transitionResult.lastAvgDiffAcc); + EXPECT_TRUE(transitionResult.checkResult); + } + + TEST_F(RedSaturationTests, CheckTransition_Negative_Value_Decrement_NoTransition) + { + RedSaturation redSaturation(5, cv::Size(), configuration.GetRedSaturationFlashParams()); + + + float testLastAvg = -25.0f; + + //SameSign (negative) && !newTransition (last was a transition) + + Flash::CheckTransitionResult transitionResult = redSaturation.CheckTransition(-30.6f, testLastAvg); + + EXPECT_EQ(-55.6f, transitionResult.lastAvgDiffAcc); + EXPECT_FALSE(transitionResult.checkResult); + } + + TEST_F(RedSaturationTests, CheckTransition_Negative_Decrements_NoTransition) + { + RedSaturation redSaturation(2, cv::Size(), configuration.GetRedSaturationFlashParams()); + + float testLastAvg = 0.0f; + + //SameSign (negative) && !newTransition (last was a transition) + + Flash::CheckTransitionResult transitionResult = redSaturation.CheckTransition(-25.6f, testLastAvg); + transitionResult = redSaturation.CheckTransition(-30.6f, transitionResult.lastAvgDiffAcc); + transitionResult = redSaturation.CheckTransition(-10.6f, transitionResult.lastAvgDiffAcc); + + EXPECT_EQ(-41.2f, transitionResult.lastAvgDiffAcc); + EXPECT_FALSE(transitionResult.checkResult); + } + + TEST_F(RedSaturationTests, SafeArea_No_Change_Threshold) + { + cv::Size size(5, 5); + RedSaturation redSaturation(3, size, configuration.GetRedSaturationFlashParams()); + + cv::Mat imageBgr(size, CV_8UC3, red); + cv::Mat* imageSbgr = frameRgbConverter->Convert(imageBgr); + + redSaturation.SetCurrentFrame(imageSbgr); + redSaturation.SetCurrentFrame(imageSbgr); + + cv::Mat* frameDiff = redSaturation.FrameDifference(); + float avgDifference = redSaturation.CheckSafeArea(frameDiff); + EXPECT_EQ(0, avgDifference); + + float flashAreaProportion = redSaturation.GetFlashArea(); + EXPECT_EQ(0, flashAreaProportion); + + delete imageSbgr; + delete frameDiff; + } + + TEST_F(RedSaturationTests, SafeArea_20_Percent_Change_Threshold) + { + cv::Size size(5, 5); + + float frameDiffArray[5][5] = { + { 200, 200, 200, 0, 0 }, + { 200, 200, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 } + }; + cv::Mat frameDiff = cv::Mat(size, CV_32FC1, &frameDiffArray); + + RedSaturation redSaturation(3, size, configuration.GetRedSaturationFlashParams()); + + cv::Mat imageBgr(size, CV_8UC3, blue); + cv::Mat* imageSbgr = frameRgbConverter->Convert(imageBgr); + redSaturation.SetCurrentFrame(imageSbgr); + + cv::Mat imageBgr2(size, CV_8UC3, red); + cv::Mat* imageSbgr2 = frameRgbConverter->Convert(imageBgr2); + redSaturation.SetCurrentFrame(imageSbgr2); + + float avgDifference = redSaturation.CheckSafeArea(&frameDiff); + EXPECT_EQ(0, avgDifference); + + float flashAreaProportion = redSaturation.GetFlashArea(); + EXPECT_TRUE(CompareFloat(0.2, flashAreaProportion)); + + delete imageSbgr; + delete imageSbgr2; + } + + TEST_F(RedSaturationTests, SafeArea_100_Percent_Change_Threshold) + { + cv::Size size(5, 5); + RedSaturation redSaturation(3, size, configuration.GetRedSaturationFlashParams()); + + cv::Mat imageBgr(size, CV_8UC3, blue); + cv::Mat* imageSbgr = frameRgbConverter->Convert(imageBgr); + redSaturation.SetCurrentFrame(imageSbgr); + + cv::Mat imageBgr2(size, CV_8UC3, red); + cv::Mat* imageSbgr2 = frameRgbConverter->Convert(imageBgr2); + redSaturation.SetCurrentFrame(imageSbgr2); + + cv::Mat* frameDiff = redSaturation.FrameDifference(); + float avgDifference = redSaturation.CheckSafeArea(frameDiff); + EXPECT_EQ(320, avgDifference); + + float flashAreaProportion = redSaturation.GetFlashArea(); + EXPECT_EQ(1, flashAreaProportion); + + delete imageSbgr; + delete imageSbgr2; + delete frameDiff; + } +} \ No newline at end of file diff --git a/test/Iris.Tests/src/RelativeLuminanceTest.cpp b/test/Iris.Tests/src/RelativeLuminanceTest.cpp new file mode 100644 index 0000000..2a7a710 --- /dev/null +++ b/test/Iris.Tests/src/RelativeLuminanceTest.cpp @@ -0,0 +1,420 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include +#include "utils/FrameConverter.h" +#include "IrisLibTest.h" +#include +#include "RelativeLuminance.h" +#include "IrisFrame.h" +#include + +namespace iris::Tests +{ + class RelativeLuminanceTest : public IrisLibTest { + protected: + EA::EACC::Utils::FrameConverter* frameRgbConverter = nullptr; + void SetUp() override { + configuration.SetLuminanceType(Configuration::LuminanceType::RELATIVE); + IrisLibTest::SetUp(); + frameRgbConverter = new EA::EACC::Utils::FrameConverter(configuration.GetFrameSrgbConverterParams()); + } + ~RelativeLuminanceTest() override { + delete frameRgbConverter; + } + }; + + TEST_F(RelativeLuminanceTest, Luminance_When_WhiteFrame_Test) + { + cv::Size size(1280, 720); + RelativeLuminance relativeLuminance(3, size, configuration.GetLuminanceFlashParams()); + cv::Mat imageBgr(size, CV_8UC3, white); + + IrisFrame imagesRgb; + imagesRgb.sRgbFrame = frameRgbConverter->Convert(imageBgr); + + relativeLuminance.SetCurrentFrame(imagesRgb); + cv::Mat* luminance = relativeLuminance.getCurrentFrame(); + float testLum = luminance->at(0, 0); + EXPECT_EQ(1, testLum); + + imagesRgb.Release(); + } + + TEST_F(RelativeLuminanceTest, Luminance_WhenBlackFrame_Test) + { + cv::Size size(1280, 720); + RelativeLuminance relativeLuminance(3, size, configuration.GetLuminanceFlashParams()); + cv::Mat imageBgr(size, CV_8UC3, black); + + IrisFrame imagesRgb; + imagesRgb.sRgbFrame = frameRgbConverter->Convert(imageBgr); + + relativeLuminance.SetCurrentFrame(imagesRgb); + cv::Mat* luminance = relativeLuminance.getCurrentFrame(); + float testLum = luminance->at(0, 0); + EXPECT_EQ(0, testLum); + + imagesRgb.Release(); + } + + TEST_F(RelativeLuminanceTest, Luminance_When_GrayFrame_Test) + { + cv::Size size(1280, 720); + RelativeLuminance relativeLuminance(3, size, configuration.GetLuminanceFlashParams()); + cv::Mat imageBgr(size, CV_8UC3, gray); + + IrisFrame imagesRgb; + imagesRgb.sRgbFrame = frameRgbConverter->Convert(imageBgr); + + relativeLuminance.SetCurrentFrame(imagesRgb); + cv::Mat* luminance = relativeLuminance.getCurrentFrame(); + float testLum = luminance->at(0, 0); + testLum = RelativeLuminance::roundoff(testLum, 3); + EXPECT_EQ(0.216f, testLum); + + imagesRgb.Release(); + } + + TEST_F(RelativeLuminanceTest, Luminance_When_BlueFrame_Test) + { + cv::Size size(1280, 720); + RelativeLuminance relativeLuminance(3, size, configuration.GetLuminanceFlashParams()); + cv::Mat imageBgr(size, CV_8UC3, blue); + + IrisFrame imagesRgb; + imagesRgb.sRgbFrame = frameRgbConverter->Convert(imageBgr); + + relativeLuminance.SetCurrentFrame(imagesRgb); + cv::Mat* luminance = relativeLuminance.getCurrentFrame(); + float testLum = luminance->at(0, 0); + testLum = RelativeLuminance::roundoff(testLum, 3); + EXPECT_EQ(0.072f, testLum); + + imagesRgb.Release(); + } + + TEST_F(RelativeLuminanceTest, TransitionBlackWhite_FrameDifference_Test) + { + cv::Size size(1280, 720); + RelativeLuminance relativeLuminance(3, size, configuration.GetLuminanceFlashParams()); + cv::Mat imageWhiteBgr(size, CV_8UC3, white); + IrisFrame pImageWhitesRgb; + pImageWhitesRgb.sRgbFrame = frameRgbConverter->Convert(imageWhiteBgr); + cv::Mat imageBlackBgr(size, CV_8UC3, black); + IrisFrame pImagesBlackRgb; + pImagesBlackRgb.sRgbFrame = frameRgbConverter->Convert(imageBlackBgr); + + relativeLuminance.SetCurrentFrame(pImagesBlackRgb); + relativeLuminance.SetCurrentFrame(pImageWhitesRgb); + cv::Mat* diff = relativeLuminance.FrameDifference(); + float testLum = diff->at(0, 0); + EXPECT_EQ(1, testLum); + + delete diff; + pImagesBlackRgb.Release(); + pImageWhitesRgb.Release(); + } + + TEST_F(RelativeLuminanceTest, TransitionWhiteBlack_FrameDifference_Test) + { + cv::Size size(1280, 720); + RelativeLuminance relativeLuminance(3, size, configuration.GetLuminanceFlashParams()); + cv::Mat imageWhiteBgr(size, CV_8UC3, white); + IrisFrame pImageWhitesRgb; + pImageWhitesRgb.sRgbFrame = frameRgbConverter->Convert(imageWhiteBgr); + cv::Mat imageBlackBgr(size, CV_8UC3, black); + IrisFrame pImagesBlackRgb; + pImagesBlackRgb.sRgbFrame = frameRgbConverter->Convert(imageBlackBgr); + + relativeLuminance.SetCurrentFrame(pImageWhitesRgb); + relativeLuminance.SetCurrentFrame(pImagesBlackRgb); + cv::Mat* diff = relativeLuminance.FrameDifference(); + float testLum = diff->at(0, 0); + EXPECT_EQ(-1, testLum); + + delete diff; + pImagesBlackRgb.Release(); + pImageWhitesRgb.Release(); + } + + TEST_F(RelativeLuminanceTest, TransitionWhiteBlackBlack_FrameDifference_Test) + { + cv::Size size(1280, 720); + RelativeLuminance relativeLuminance(3, size, configuration.GetLuminanceFlashParams()); + cv::Mat imageWhiteBgr(size, CV_8UC3, white); + IrisFrame pImageWhitesRgb; + pImageWhitesRgb.sRgbFrame = frameRgbConverter->Convert(imageWhiteBgr); + cv::Mat imageBlackBgr(size, CV_8UC3, black); + IrisFrame pImagesBlackRgb; + pImagesBlackRgb.sRgbFrame = frameRgbConverter->Convert(imageBlackBgr); + + relativeLuminance.SetCurrentFrame(pImageWhitesRgb); + relativeLuminance.SetCurrentFrame(pImagesBlackRgb); + relativeLuminance.SetCurrentFrame(pImagesBlackRgb); + cv::Mat* diff = relativeLuminance.FrameDifference(); + float testLum = diff->at(0, 0); + EXPECT_EQ(0, testLum); + + delete diff; + pImagesBlackRgb.Release(); + pImageWhitesRgb.Release(); + } + + TEST_F(RelativeLuminanceTest,CheckTransition_WhenFalse_From_value_to_zero_Test) + { + RelativeLuminance relativeLuminance(5, cv::Size(), configuration.GetLuminanceFlashParams()); + float testLastAvg = 0.02f; + + //SameSign (positive) 0 case && !newTransition + Flash::CheckTransitionResult transitionResult = relativeLuminance.CheckTransition(0, testLastAvg); + + EXPECT_FALSE(transitionResult.checkResult); + EXPECT_EQ(0.02f, transitionResult.lastAvgDiffAcc); + } + + TEST_F(RelativeLuminanceTest, CheckTransition_WhenFalse_From_value_to_value_Test) + { + RelativeLuminance relativeLuminance(5, cv::Size(), configuration.GetLuminanceFlashParams()); + float testLastAvg = 0.02f; + + //SameSign (positive) 0 case && !newTransition + Flash::CheckTransitionResult transitionResult = relativeLuminance.CheckTransition(0.05f, testLastAvg); + + EXPECT_FALSE(transitionResult.checkResult); + EXPECT_EQ(0.07f, transitionResult.lastAvgDiffAcc); + } + + TEST_F(RelativeLuminanceTest, CheckTransition_WhenTrue_From_value_to_value_Test) + { + RelativeLuminance relativeLuminance(5, cv::Size(), configuration.GetLuminanceFlashParams()); + float testLastAvg = 0.07f; + + //SameSign (positive) 0 case && !newTransition + Flash::CheckTransitionResult transitionResult = relativeLuminance.CheckTransition(0.03f, testLastAvg); + + EXPECT_TRUE(transitionResult.checkResult); + EXPECT_EQ(0.1f, transitionResult.lastAvgDiffAcc); + } + + TEST_F(RelativeLuminanceTest, CheckTransition_WhenFalse_From_threshold_value_to_value_Test) + { + RelativeLuminance relativeLuminance(5, cv::Size(), configuration.GetLuminanceFlashParams()); + float testLastAvg = 0.1f; + + //SameSign (positive) 0 case && !newTransition + Flash::CheckTransitionResult transitionResult = relativeLuminance.CheckTransition(0.1f, testLastAvg); + + EXPECT_FALSE(transitionResult.checkResult); + EXPECT_EQ(0.2f, transitionResult.lastAvgDiffAcc); + } + + TEST_F(RelativeLuminanceTest, CheckTransition_WhenTrue_From_value_to_negative_value_Test) + { + RelativeLuminance relativeLuminance(5, cv::Size(), configuration.GetLuminanceFlashParams()); + float testLastAvg = 0.2f; + + //SameSign (positive) 0 case && !newTransition + Flash::CheckTransitionResult transitionResult = relativeLuminance.CheckTransition(-0.1f, testLastAvg); + + EXPECT_TRUE(transitionResult.checkResult); + EXPECT_EQ(-0.1f, transitionResult.lastAvgDiffAcc); + } + + TEST_F(RelativeLuminanceTest, CheckTransition_WhenTrue_From_negative_value_to_value_Test) + { + RelativeLuminance relativeLuminance(5, cv::Size(), configuration.GetLuminanceFlashParams()); + float testLastAvg = -0.1f; + + //SameSign (positive) 0 case && !newTransition + Flash::CheckTransitionResult transitionResult = relativeLuminance.CheckTransition(0.1f, testLastAvg); + + EXPECT_TRUE(transitionResult.checkResult); + EXPECT_EQ(0.1f, transitionResult.lastAvgDiffAcc); + } + + TEST_F(RelativeLuminanceTest, CheckTransition_WhenFalse_From_value_to_negative_value_Test) + { + RelativeLuminance relativeLuminance(5, cv::Size(), configuration.GetLuminanceFlashParams()); + float testLastAvg = 0.1f; + + //SameSign (positive) 0 case && !newTransition + Flash::CheckTransitionResult transitionResult = relativeLuminance.CheckTransition(-0.05f, testLastAvg); + + EXPECT_FALSE(transitionResult.checkResult); + EXPECT_EQ(-0.05f, transitionResult.lastAvgDiffAcc); + } + + TEST_F(RelativeLuminanceTest, CheckTransition_WhenFalse_From_negative_value_to_zero_Test) + { + RelativeLuminance relativeLuminance(5, cv::Size(), configuration.GetLuminanceFlashParams()); + float testLastAvg = -0.05f; + + //SameSign (positive) 0 case && !newTransition + Flash::CheckTransitionResult transitionResult = relativeLuminance.CheckTransition(0, testLastAvg); + + EXPECT_FALSE(transitionResult.checkResult); + EXPECT_EQ(-0.05f, transitionResult.lastAvgDiffAcc); + } + + TEST_F(RelativeLuminanceTest, CheckTransition_WhenTrue_From_negative_value_to_negative_value_Test) + { + RelativeLuminance relativeLuminance(5, cv::Size(), configuration.GetLuminanceFlashParams()); + float testLastAvg = -0.05f; + + //SameSign (positive) 0 case && !newTransition + Flash::CheckTransitionResult transitionResult = relativeLuminance.CheckTransition(-0.06f, testLastAvg); + + EXPECT_TRUE(transitionResult.checkResult); + EXPECT_EQ(-0.11f, transitionResult.lastAvgDiffAcc); + } + + TEST_F(RelativeLuminanceTest, CheckTransition_WhenFalse_From_negative_threshold_value_to_negative_value_Test) + { + RelativeLuminance relativeLuminance(5, cv::Size(), configuration.GetLuminanceFlashParams()); + float testLastAvg = -0.11f; + + //SameSign (positive) 0 case && !newTransition + Flash::CheckTransitionResult transitionResult = relativeLuminance.CheckTransition(-0.1f, testLastAvg); + + EXPECT_FALSE(transitionResult.checkResult); + EXPECT_EQ(-0.21f, Flash::roundoff(transitionResult.lastAvgDiffAcc, 2)); + } + + TEST_F(RelativeLuminanceTest, CheckTransition_WhenFalse_From_zero_to_negative_threshold_value_to_negative_threshold_value_Test) + { + RelativeLuminance relativeLuminance(2, cv::Size(), configuration.GetLuminanceFlashParams()); + float testLastAvg = 0; + + //SameSign (positive) 0 case && !newTransition + Flash::CheckTransitionResult transitionResult = relativeLuminance.CheckTransition(-0.11f, testLastAvg); + testLastAvg = transitionResult.lastAvgDiffAcc; + transitionResult = relativeLuminance.CheckTransition(-0.1f, testLastAvg); + testLastAvg = transitionResult.lastAvgDiffAcc; + transitionResult = relativeLuminance.CheckTransition(-0.1f, testLastAvg); + + EXPECT_FALSE(transitionResult.checkResult); + EXPECT_EQ(-0.2f, Flash::roundoff(transitionResult.lastAvgDiffAcc, 2)); + } + + TEST_F(RelativeLuminanceTest, AverageLuminanca_WhenWhite_Test) + { + RelativeLuminance relativeLuminance(3, cv::Size(), configuration.GetLuminanceFlashParams()); + cv::Mat imageWhiteBgr(1280, 720, CV_8UC3, white); + IrisFrame pImageWhitesRgb; + pImageWhitesRgb.sRgbFrame = frameRgbConverter->Convert(imageWhiteBgr); + + relativeLuminance.SetCurrentFrame(pImageWhitesRgb); + float avgLum = relativeLuminance.FrameMean(); + EXPECT_EQ(1, avgLum); + + pImageWhitesRgb.Release(); + } + + TEST_F(RelativeLuminanceTest, AverageLuminanca_WhenGray_Test) + { + RelativeLuminance relativeLuminance(3, cv::Size(), configuration.GetLuminanceFlashParams()); + cv::Mat imageWhiteBgr(1280, 720, CV_8UC3, gray); + IrisFrame pImageWhitesRgb; + pImageWhitesRgb.sRgbFrame = frameRgbConverter->Convert(imageWhiteBgr); + + relativeLuminance.SetCurrentFrame(pImageWhitesRgb); + float avgLum = relativeLuminance.roundoff(relativeLuminance.FrameMean(), 2); + EXPECT_EQ(0.22f, avgLum); + + pImageWhitesRgb.Release(); + } + + TEST_F(RelativeLuminanceTest, AverageLuminanca_WhenRealFrame_Test) + { + RelativeLuminance relativeLuminance(3, cv::Size(), configuration.GetLuminanceFlashParams()); + cv::Mat frameBgr = cv::imread("data/TestImages/frames/FrameForTest.jpg"); + IrisFrame pFramesRgb; + pFramesRgb.sRgbFrame = frameRgbConverter->Convert(frameBgr); + + relativeLuminance.SetCurrentFrame(pFramesRgb); + float avgLum = relativeLuminance.roundoff(relativeLuminance.FrameMean(), 2); + EXPECT_EQ(0.33f, avgLum); + + pFramesRgb.Release(); + } + + TEST_F(RelativeLuminanceTest, SafeArea_No_Change_Threshold) + { + cv::Size size(5, 5); + RelativeLuminance luminance(3, size, configuration.GetLuminanceFlashParams()); + + cv::Mat imageBgr(size, CV_8UC3, blue); + cv::Mat* imageSbgr = frameRgbConverter->Convert(imageBgr); + + luminance.SetCurrentFrame(imageSbgr); + luminance.SetCurrentFrame(imageSbgr); + + cv::Mat* frameDiff = luminance.FrameDifference(); + float avgDifference = luminance.CheckSafeArea(frameDiff); + EXPECT_EQ(0, avgDifference); + + float flashAreaProportion = luminance.GetFlashArea(); + EXPECT_EQ(0, flashAreaProportion); + + delete imageSbgr; + delete frameDiff; + } + + TEST_F(RelativeLuminanceTest, SafeArea_20_Percent_Change_Threshold) + { + cv::Size size(5, 5); + + float frameDiffArray[5][5] = { + { 200, 200, 200, 0, 0 }, + { 200, 200, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 } + }; + cv::Mat frameDiff = cv::Mat(size, CV_32FC1, &frameDiffArray); + + RelativeLuminance luminance(3, size, configuration.GetLuminanceFlashParams()); + + cv::Mat imageBgr(size, CV_8UC3, blue); + cv::Mat* imageSbgr = frameRgbConverter->Convert(imageBgr); + luminance.SetCurrentFrame(imageSbgr); + + cv::Mat imageBgr2(size, CV_8UC3, white); + cv::Mat* imageSbgr2 = frameRgbConverter->Convert(imageBgr2); + luminance.SetCurrentFrame(imageSbgr2); + + float avgDifference = luminance.CheckSafeArea(&frameDiff); + EXPECT_EQ(0, avgDifference); + + float flashAreaProportion = luminance.GetFlashArea(); + EXPECT_TRUE(CompareFloat(0.2, flashAreaProportion)); + + delete imageSbgr; + delete imageSbgr2; + } + + TEST_F(RelativeLuminanceTest, SafeArea_100_Percent_Change_Threshold) + { + cv::Size size(5, 5); + RelativeLuminance luminance(3, size, configuration.GetLuminanceFlashParams()); + + cv::Mat imageBgr(size, CV_8UC3, blue); + cv::Mat* imageSbgr = frameRgbConverter->Convert(imageBgr); + luminance.SetCurrentFrame(imageSbgr); + + cv::Mat imageBgr2(size, CV_8UC3, white); + cv::Mat* imageSbgr2 = frameRgbConverter->Convert(imageBgr2); + luminance.SetCurrentFrame(imageSbgr2); + + cv::Mat* frameDiff = luminance.FrameDifference(); + float avgDifference = luminance.CheckSafeArea(frameDiff); + EXPECT_TRUE(CompareFloat(0.9278, avgDifference)); + + float flashAreaProportion = luminance.GetFlashArea(); + EXPECT_EQ(1, flashAreaProportion); + + delete imageSbgr; + delete imageSbgr2; + delete frameDiff; + } +} \ No newline at end of file diff --git a/test/Iris.Tests/src/TransitionEvaluatorTest.cpp b/test/Iris.Tests/src/TransitionEvaluatorTest.cpp new file mode 100644 index 0000000..0af6203 --- /dev/null +++ b/test/Iris.Tests/src/TransitionEvaluatorTest.cpp @@ -0,0 +1,248 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include "IrisLibTest.h" +#include +#include "TransitionEvaluator.h" +#include "FrameData.h" + +namespace iris::Tests +{ + class TransitionEvaluatorTest : public IrisLibTest { + protected: + + }; + + TEST_F(TransitionEvaluatorTest, Transition_WhenOnlyLuminance_Test) + { + //Only luminance transitions + FrameData testData = FrameData(); + TransitionEvaluator transitionEvaluator(8, configuration.GetTransitionEvaluatorParams()); + + transitionEvaluator.SetTransitions(true, false, testData); + transitionEvaluator.SetTransitions(true, false, testData); + transitionEvaluator.SetTransitions(false, false, testData); + transitionEvaluator.SetTransitions(true, false, testData); + transitionEvaluator.SetTransitions(false, false, testData); + transitionEvaluator.SetTransitions(false, false, testData); + transitionEvaluator.SetTransitions(true, false, testData); + + EXPECT_EQ(4, testData.LuminanceTransitions); + EXPECT_EQ(0, testData.RedTransitions); + } + + TEST_F(TransitionEvaluatorTest, Transition_WhenOnlyRed_Test) + { + //Only luminance transitions + FrameData testData = FrameData(); + TransitionEvaluator transitionEvaluator(8, configuration.GetTransitionEvaluatorParams()); + + transitionEvaluator.SetTransitions(false, false, testData); + transitionEvaluator.SetTransitions(false, true, testData); + transitionEvaluator.SetTransitions(false, false, testData); + transitionEvaluator.SetTransitions(false, true, testData); + transitionEvaluator.SetTransitions(false, false, testData); + transitionEvaluator.SetTransitions(false, true, testData); + transitionEvaluator.SetTransitions(false, true, testData); + + EXPECT_EQ(0, testData.LuminanceTransitions); + EXPECT_EQ(4, testData.RedTransitions); + } + + TEST_F(TransitionEvaluatorTest, Transition_WhenLuminanceAndRed_Test) + { + //Only luminance transitions + FrameData testData = FrameData(); + TransitionEvaluator transitionEvaluator(8, configuration.GetTransitionEvaluatorParams()); + + transitionEvaluator.SetTransitions(false, false, testData); + transitionEvaluator.SetTransitions(true, true, testData); + transitionEvaluator.SetTransitions(false, false, testData); + transitionEvaluator.SetTransitions(false, true, testData); + transitionEvaluator.SetTransitions(true, false, testData); + transitionEvaluator.SetTransitions(false, true, testData); + transitionEvaluator.SetTransitions(true, true, testData); + + EXPECT_EQ(3, testData.LuminanceTransitions); + EXPECT_EQ(4, testData.RedTransitions); + } + + TEST_F(TransitionEvaluatorTest, EvaluateSecond_WhenLuminanceFlashFail_Test) + { + //Only luminance transitions + FrameData testData = FrameData(); + TransitionEvaluator transitionEvaluator(8, configuration.GetTransitionEvaluatorParams()); + + for (int i = 0; i < 8; i++) + { + transitionEvaluator.SetTransitions(true, false, testData); + } + transitionEvaluator.EvaluateSecond(8, 8, testData); + EXPECT_TRUE(transitionEvaluator.getFlashFail()); + EXPECT_TRUE(transitionEvaluator.getLumFlashFail()); + EXPECT_EQ(FlashResult::FlashFail, testData.luminanceFrameResult); + EXPECT_EQ(1, transitionEvaluator.getLuminanceIncidents().flashFailFrames); + } + + TEST_F(TransitionEvaluatorTest, EvaluateSecond_WhenRedFlashFail_Test) + { + //Only luminance transitions + FrameData testData = FrameData(); + TransitionEvaluator transitionEvaluator(8, configuration.GetTransitionEvaluatorParams()); + + for (int i = 0; i < 8; i++) + { + transitionEvaluator.SetTransitions(false, true, testData); + } + transitionEvaluator.EvaluateSecond(8, 8, testData); + EXPECT_TRUE(transitionEvaluator.getFlashFail()); + EXPECT_TRUE(transitionEvaluator.getRedFlashFail()); + EXPECT_EQ(FlashResult::FlashFail, testData.redFrameResult); + EXPECT_EQ(1, transitionEvaluator.getRedIncidents().flashFailFrames); + + } + + TEST_F(TransitionEvaluatorTest, EvaluateSecond_WhenLuminanceAndRedFlashFail_Test) + { + //Only luminance transitions + FrameData testData = FrameData(); + TransitionEvaluator transitionEvaluator(10, configuration.GetTransitionEvaluatorParams()); + + for (int i = 0; i < 2; i++) //add 2 luminance and red transitions + { + transitionEvaluator.SetTransitions(true, true, testData); + } + for (int i = 0; i < 3; i++) //add 3 red transitions + { + transitionEvaluator.SetTransitions(false, true, testData); + } + for (int i = 0; i < 5; i++) //add 5 luminance transitions + { + transitionEvaluator.SetTransitions(true, false, testData); + } + transitionEvaluator.EvaluateSecond(10, 10, testData); + EXPECT_TRUE(transitionEvaluator.getFlashFail()); + EXPECT_TRUE(transitionEvaluator.getLumFlashFail()); + EXPECT_FALSE(transitionEvaluator.getRedFlashFail()); + + EXPECT_EQ(FlashResult::FlashFail, testData.luminanceFrameResult); + EXPECT_EQ(FlashResult::PassWithWarning, testData.redFrameResult); + EXPECT_EQ(1, transitionEvaluator.getLuminanceIncidents().flashFailFrames); + EXPECT_EQ(1, transitionEvaluator.getRedIncidents().passWithWarningFrames); + + } + + TEST_F(TransitionEvaluatorTest, EvaluateSecond_WhenLuminanceFlashPass_Test) + { + //Only luminance transitions + FrameData testData = FrameData(); + TransitionEvaluator transitionEvaluator(8, configuration.GetTransitionEvaluatorParams()); + + for (int i = 0; i < 3; i++) //add 3 luminance transitions + { + transitionEvaluator.SetTransitions(true, false, testData); + } + + for (int i = 0; i < 5; i++) //add 5 frames, no transitions + { + transitionEvaluator.SetTransitions(false, false, testData); + } + transitionEvaluator.EvaluateSecond(8, 8, testData); + EXPECT_FALSE(transitionEvaluator.getFlashFail()); + EXPECT_EQ(FlashResult::Pass, testData.luminanceFrameResult); + EXPECT_EQ(FlashResult::Pass, testData.redFrameResult); + } + + TEST_F(TransitionEvaluatorTest, EvaluateSecond_WhenRedFlashPass_Test) + { + //Only luminance transitions + FrameData testData = FrameData(); + TransitionEvaluator transitionEvaluator(8, configuration.GetTransitionEvaluatorParams()); + + for (int i = 0; i < 3; i++) //add 3 red transitions + { + transitionEvaluator.SetTransitions(false, true, testData); + } + + for (int i = 0; i < 5; i++) //add 5 frames, no transitions + { + transitionEvaluator.SetTransitions(false, false, testData); + } + transitionEvaluator.EvaluateSecond(8, 8, testData); + EXPECT_FALSE(transitionEvaluator.getFlashFail()); + EXPECT_EQ(FlashResult::Pass, testData.luminanceFrameResult); + EXPECT_EQ(FlashResult::Pass, testData.redFrameResult); + } + + TEST_F(TransitionEvaluatorTest, EvaluateSecond_WhenLuminanceAndRedFlashPass_Test) + { + //Only luminance transitions + FrameData testData = FrameData(); + TransitionEvaluator transitionEvaluator(8, configuration.GetTransitionEvaluatorParams()); + + for (int i = 0; i < 3; i++) //add 3 luminance and red transitions + { + transitionEvaluator.SetTransitions(true, true, testData); + } + + for (int i = 0; i < 5; i++) //add 5 frames, no transitions + { + transitionEvaluator.SetTransitions(false, false, testData); + } + transitionEvaluator.EvaluateSecond(8, 8, testData); + EXPECT_FALSE(transitionEvaluator.getFlashFail()); + EXPECT_EQ(FlashResult::Pass, testData.luminanceFrameResult); + EXPECT_EQ(FlashResult::Pass, testData.redFrameResult); + } + + TEST_F(TransitionEvaluatorTest, EvaluateSecond_WhenExtendedFailure_Test) + { + //Only luminance transitions + FrameData testData; + TransitionEvaluator transitionEvaluator(5, configuration.GetTransitionEvaluatorParams()); + + for (int i = 0; i < 23; i++) //add luminance and red transitions + { + transitionEvaluator.SetTransitions(true, true, testData); + transitionEvaluator.EvaluateSecond(i, 5, testData); + } + EXPECT_TRUE(transitionEvaluator.getExtendedFailure()); + EXPECT_TRUE(transitionEvaluator.getLumExtendedFailure()); + EXPECT_TRUE(transitionEvaluator.getRedExtendedFailure()); + EXPECT_EQ(FlashResult::ExtendedFail, testData.luminanceFrameResult); + EXPECT_EQ(FlashResult::ExtendedFail, testData.redFrameResult); + + EXPECT_EQ(0, transitionEvaluator.getLuminanceIncidents().flashFailFrames); + EXPECT_EQ(1, transitionEvaluator.getLuminanceIncidents().extendedFailFrames); + EXPECT_EQ(19, transitionEvaluator.getLuminanceIncidents().passWithWarningFrames); + EXPECT_EQ(0, transitionEvaluator.getRedIncidents().flashFailFrames); + EXPECT_EQ(1, transitionEvaluator.getRedIncidents().extendedFailFrames); + EXPECT_EQ(19, transitionEvaluator.getRedIncidents().passWithWarningFrames); + } + + TEST_F(TransitionEvaluatorTest, TotalTransictionCount_WhenLuminanceAndRed_Test) + { + //Only luminance transitions + FrameData testData = FrameData(); + TransitionEvaluator transitionEvaluator(4, configuration.GetTransitionEvaluatorParams()); + + for (int i = 0; i < 5; i++) //add 5 luminance and red transitions + { + transitionEvaluator.SetTransitions(true, true, testData); + } + for (int i = 0; i < 3; i++) //add 3 frames, no transitions + { + transitionEvaluator.SetTransitions(false, false, testData); + } + for (int i = 0; i < 4; i++) //add 4 luminance transitions + { + transitionEvaluator.SetTransitions(true, false, testData); + } + for (int i = 0; i < 6; i++) //add 6 red transitions + { + transitionEvaluator.SetTransitions(false, true, testData); + } + + EXPECT_EQ(9, testData.LuminanceTransitions); + EXPECT_EQ(11, testData.RedTransitions); + } +} \ No newline at end of file diff --git a/test/Iris.Tests/src/VideoAnalysisTests.cpp b/test/Iris.Tests/src/VideoAnalysisTests.cpp new file mode 100644 index 0000000..49ab06f --- /dev/null +++ b/test/Iris.Tests/src/VideoAnalysisTests.cpp @@ -0,0 +1,194 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include "IrisLibTest.h" +#include "VideoAnalyser.cpp" +#include +#include +#include +#include "FrameData.h" + +namespace iris::Tests +{ + class VideoAnalysisTests : public IrisLibTest { + protected: + void SetUp() override { + configuration.SetLuminanceType(Configuration::LuminanceType::RELATIVE); + IrisLibTest::SetUp(); + } + + void TestVideoAnalysis(VideoAnalyser& videoAnalyser, cv::VideoCapture& video, const char* sourceLog) + { + std::ifstream logFile; + logFile.open(sourceLog); + + std::string line; + std::getline(logFile, line); //discard first line + std::string testVideo = "testVideo"; + videoAnalyser.Init(video.get(cv::CAP_PROP_FPS), cv::Size(video.get(cv::CAP_PROP_FRAME_WIDTH), video.get(cv::CAP_PROP_FRAME_HEIGHT)), testVideo); + cv::Mat frame; + video.read(frame); + int numFrames = 0; + + while (!frame.empty()) + { + FrameData data(numFrames + 1, video.get(cv::CAP_PROP_FRAME_COUNT)); + videoAnalyser.AnalyseFrame(frame, numFrames, data); + + video.read(frame); //obtain new frame + numFrames++; + + //check framedata + std::getline(logFile, line); + CheckFrameData(line, data); + } + + logFile.close(); + videoAnalyser.DeInit(); + } + + void CheckFrameData(std::string& line, FrameData& data) + { + std::vector logFrameData; + std::stringstream ss(line); + while (ss.good()) + { + std::string substr; + getline(ss, substr, ','); + logFrameData.push_back(substr); + } + + EXPECT_EQ(std::stoi(logFrameData[0]), data.Frame) << "Frame: " << data.Frame << '\n'; + + EXPECT_TRUE(CompareFloat(std::stof(logFrameData[2]), data.LuminanceAverage)) << "Frame: " << data.Frame << '\n'; + std::string str = data.proportionToPercentage(std::stof(logFrameData[3])); + EXPECT_EQ(str, data.LuminanceFlashArea) << "Frame: " << data.Frame << '\n'; + EXPECT_TRUE(CompareFloat(std::stof(logFrameData[4]), data.AverageLuminanceDiff)) << "Frame: " << data.Frame << '\n'; + EXPECT_TRUE(CompareFloat(std::stof(logFrameData[5]), data.AverageLuminanceDiffAcc)) << "Frame: " << data.Frame << '\n'; + + EXPECT_TRUE(CompareFloat(std::stof(logFrameData[6]), data.RedAverage)) << "Frame: " << data.Frame << '\n'; + str = data.proportionToPercentage(std::stof(logFrameData[7])); + EXPECT_EQ(str, data.RedFlashArea) << "Frame: " << data.Frame << '\n'; + EXPECT_TRUE(CompareFloat(std::stof(logFrameData[8]), data.AverageRedDiff)) << "Frame: " << data.Frame << '\n'; + EXPECT_TRUE(CompareFloat(std::stof(logFrameData[9]), data.AverageRedDiffAcc)) << "Frame: " << data.Frame << '\n'; + + EXPECT_EQ(std::stof(logFrameData[10]), data.PatternRisk) << "Frame: " << data.Frame << '\n'; + + EXPECT_EQ(std::stoi(logFrameData[11]), data.LuminanceTransitions) << "Frame: " << data.Frame << '\n'; + EXPECT_EQ(std::stoi(logFrameData[12]), data.RedTransitions) << "Frame: " << data.Frame << '\n'; + EXPECT_EQ(std::stoi(logFrameData[13]), data.LuminanceExtendedFailCount) << "Frame: " << data.Frame << '\n'; + EXPECT_EQ(std::stoi(logFrameData[14]), data.RedExtendedFailCount) << "Frame: " << data.Frame << '\n'; + + EXPECT_EQ(std::stoi(logFrameData[15]), (int)data.luminanceFrameResult) << "Frame: " << data.Frame << '\n'; + EXPECT_EQ(std::stoi(logFrameData[16]), (int)data.redFrameResult) << "Frame: " << data.Frame << '\n'; + EXPECT_EQ(std::stoi(logFrameData[17]), (int)data.patternFrameResult) << "Frame: " << data.Frame << '\n'; + } + }; + + TEST_F(VideoAnalysisTests, 2Hz_5s_Video_Test) + { + const char* sourceVideo = "data/TestVideos/2Hz_5s.mp4"; + VideoAnalyser videoAnalyser(&configuration); + + cv::VideoCapture video(sourceVideo); + if (videoAnalyser.VideoIsOpen(video, nullptr)) + { + TestVideoAnalysis(videoAnalyser, video, "data/ExpectedVideoLogFiles/2Hz_5s_RELATIVE.csv"); + } + } + + TEST_F(VideoAnalysisTests, 2Hz_6s_Video_Test) + { + const char* sourceVideo = "data/TestVideos/2Hz_6s.mp4"; + VideoAnalyser videoAnalyser(&configuration); + + cv::VideoCapture video(sourceVideo); + if (videoAnalyser.VideoIsOpen(video, nullptr)) + { + TestVideoAnalysis(videoAnalyser, video, "data/ExpectedVideoLogFiles/2Hz_6s_RELATIVE.csv"); + } + } + + TEST_F(VideoAnalysisTests, 3Hz_6s_Video_Test) + { + const char* sourceVideo = "data/TestVideos/3Hz_6s.mp4"; + VideoAnalyser videoAnalyser(&configuration); + + cv::VideoCapture video(sourceVideo); + if (videoAnalyser.VideoIsOpen(video, nullptr)) + { + TestVideoAnalysis(videoAnalyser, video, "data/ExpectedVideoLogFiles/3Hz_6s_RELATIVE.csv"); + } + } + + TEST_F(VideoAnalysisTests, extendedFLONG_Video_Test) + { + const char* sourceVideo = "data/TestVideos/extendedFLONG.mp4"; + VideoAnalyser videoAnalyser(&configuration); + + cv::VideoCapture video(sourceVideo); + if (videoAnalyser.VideoIsOpen(video, nullptr)) + { + TestVideoAnalysis(videoAnalyser, video, "data/ExpectedVideoLogFiles/extendedFLONG_RELATIVE.csv"); + } + } + + TEST_F(VideoAnalysisTests, GradualRedIncrease_Video_Test) + { + const char* sourceVideo = "data/TestVideos/GradualRedIncrease.mp4"; + VideoAnalyser videoAnalyser(&configuration); + + cv::VideoCapture video(sourceVideo); + if (videoAnalyser.VideoIsOpen(video, nullptr)) + { + TestVideoAnalysis(videoAnalyser, video, "data/ExpectedVideoLogFiles/GradualRedIncrease_RELATIVE.csv"); + } + } + + TEST_F(VideoAnalysisTests, gray_Video_Test) + { + const char* sourceVideo = "data/TestVideos/gray.mp4"; + VideoAnalyser videoAnalyser(&configuration); + + cv::VideoCapture video(sourceVideo); + if (videoAnalyser.VideoIsOpen(video, nullptr)) + { + TestVideoAnalysis(videoAnalyser, video, "data/ExpectedVideoLogFiles/gray_RELATIVE.csv"); + } + } + + TEST_F(VideoAnalysisTests, intermitentEF_Video_Test) + { + const char* sourceVideo = "data/TestVideos/intermitentEF.mp4"; + VideoAnalyser videoAnalyser(&configuration); + + cv::VideoCapture video(sourceVideo); + if (videoAnalyser.VideoIsOpen(video, nullptr)) + { + TestVideoAnalysis(videoAnalyser, video, "data/ExpectedVideoLogFiles/intermitentEF_RELATIVE.csv"); + } + } + + //TEST_F(VideoAnalysisTests, PatternCircular4_Video_Test) + //{ + // const char* sourceVideo = "data/TestVideos/Pattern - Circular 4.avi"; + // VideoAnalyser videoAnalyser(&configuration); + + // cv::VideoCapture video(sourceVideo); + // if (videoAnalyser.VideoIsOpen(video, nullptr)) + // { + // TestVideoAnalysis(videoAnalyser, video, "data/ExpectedVideoLogFiles/Pattern - Circular 4_RELATIVE.csv"); + // } + //} + + //TEST_F(VideoAnalysisTests, PatternCircular5_Video_Test) + //{ + // const char* sourceVideo = "data/TestVideos/Pattern - Circular 5.avi"; + // VideoAnalyser videoAnalyser(&configuration); + + // cv::VideoCapture video(sourceVideo); + // if (videoAnalyser.VideoIsOpen(video, nullptr)) + // { + // TestVideoAnalysis(videoAnalyser, video, "data/ExpectedVideoLogFiles/Pattern - Circular 5_RELATIVE.csv"); + // } + //} +} \ No newline at end of file diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100644 index 0000000..420ecc0 --- /dev/null +++ b/utils/CMakeLists.txt @@ -0,0 +1,141 @@ +# Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +cmake_minimum_required (VERSION 3.21.0) +project(utils VERSION 1.0.0 LANGUAGES CXX) + +message("BUILD UTILS LIBRARY") + +# --------------------------------------------------------------------------------------- +# Options +# --------------------------------------------------------------------------------------- +option(BUILD_SHARED_LIBS "Build as a shared library" OFF) +option(EXPORT_UTILS "Export and install library" ON) + +# --------------------------------------------------------------------------------------- +# Compiler config +# --------------------------------------------------------------------------------------- + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +if(BUILD_SHARED_LIBS) + message("Build as shared library") + if(WIN32) + message("Export DLL symbols on Windows") + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + endif() +endif() + +# --------------------------------------------------------------------------------------- +# Utils library +# --------------------------------------------------------------------------------------- + +set(PUBLIC_HEADERS + "include/utils/BaseLog.h" + "include/utils/FrameConverter.h" + "include/utils/JsonWrapper.h" +) +source_group("Header Files" FILES ${PUBLIC_HEADERS}) + +set(SOURCE_FILES + "src/BaseLog.cpp" + "src/FrameConverter.cpp" +) +source_group("Source Files" FILES ${SOURCE_FILES}) + + +# --------------------------------------------------------------------------------------- +# Dependencies +# ---------------------------------------------------------------------------------------s + +find_package(nlohmann_json CONFIG REQUIRED) +find_package(spdlog CONFIG REQUIRED) +find_package(OpenCV CONFIG REQUIRED) + + +# --------------------------------------------------------------------------------------- +# Utils library +# --------------------------------------------------------------------------------------- + +add_library(${PROJECT_NAME} ${SOURCE_FILES}) + +if(BUILD_SHARED_LIBS) + if(WIN32) + target_compile_definitions(${PROJECT_NAME} PUBLIC UTILS_SHARED PRIVATE UTILS_EXPORT) + endif() +endif() +set_target_properties(${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX "d") + +set_target_properties(${PROJECT_NAME} PROPERTIES + PUBLIC_HEADER "${PUBLIC_HEADERS}" +) + +target_link_libraries(${PROJECT_NAME} PUBLIC spdlog::spdlog + PRIVATE nlohmann_json::nlohmann_json opencv_core +) + +target_include_directories(${PROJECT_NAME} PUBLIC + # where the top-level project will look for the library's public headers + "$" + # where external projects will look for the library's public headers + "$" +) + + +# --------------------------------------------------------------------------------------- +# Export & Install +# --------------------------------------------------------------------------------------- + +if(EXPORT_UTILS) +message("Export Utils") + +include(GNUInstallDirs) +set(namespace ${PROJECT_NAME}) + +# install the target and create export-set +install(TARGETS ${PROJECT_NAME} + EXPORT "${PROJECT_NAME}Targets" + # these variable get default values from GNUInstallDirs + #RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # bin + #LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # lib + #ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # lib + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # include +) + +# generate and install export file +install(EXPORT "${PROJECT_NAME}Targets" + FILE "${PROJECT_NAME}Targets.cmake" + NAMESPACE ${namespace}:: + DESTINATION cmake +) + +include(CMakePackageConfigHelpers) + +# generate the version file for the config file +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + VERSION "${version}" + COMPATIBILITY AnyNewerVersion +) + +# create config file +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION cmake +) + +# install config files +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION cmake +) + +# generate the export targets for the build tree +export(EXPORT "${PROJECT_NAME}Targets" + FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}Targets.cmake" + NAMESPACE ${namespace}:: +) + +endif() \ No newline at end of file diff --git a/utils/cmake/Config.cmake.in b/utils/cmake/Config.cmake.in new file mode 100644 index 0000000..7cb8364 --- /dev/null +++ b/utils/cmake/Config.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") + +check_required_components(@PROJECT_NAME@) \ No newline at end of file diff --git a/utils/include/utils/BaseLog.h b/utils/include/utils/BaseLog.h new file mode 100644 index 0000000..47d7ff2 --- /dev/null +++ b/utils/include/utils/BaseLog.h @@ -0,0 +1,95 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +// Implements a small wrapper of a logging library to use logging in C++ projects. +// To use ILog, implement a Log class that inherits from ILog and define the sinks +// needed for the loggers. + +#pragma once +#include +#include +#include +#include + +#ifdef UTILS_SHARED + #ifdef UTILS_EXPORT + #define UTILS_API __declspec(dllexport) + #else + #define UTILS_API __declspec(dllimport) +#endif +#else + #define UTILS_API +#endif + +namespace EA::EACC::Utils +{ + class BaseLog + { + public: + + /// + /// Call at the end of the application to flush all pending messages + /// + static void ShutDown(){ spdlog::shutdown(); } + + inline static std::shared_ptr GetCoreLogger() { return m_CoreLogger; } + + static void Init() { InitCoreLogger(true, false, 0, BaseLog::RotatingFileSinkParams(""), "%^[%T] [%t] [%l]: %v%$"); }; + + struct RotatingFileSinkParams + { + RotatingFileSinkParams(const char* fileName) : fileName(fileName){} + + const char* fileName = nullptr; + std::size_t max_files = 5; + bool rotate_on_open = true; + std::size_t max_size = 1024 * 1024; + }; + protected: + + /// + /// Initializes Core Logger + /// + /// Enable/Disable logging to console + /// Enable/Disable logging to file + /// Define a level to filter logging messages by severity + /// If file logging enabled, provide params to configure file sink + /// Provide logging pattern, if pattern is nullptr, default spdlog logging pattern will be used + static void InitCoreLogger(bool console, bool file, int level, RotatingFileSinkParams params, const char* pattern); + + /// + /// Removes previous file sink in logger and adds a new one + /// + /// logger object to modift + /// Params to create new file sink + /// Logging pattern, if nullptr, default spdlog logging pattern will be used + static void SetLoggerFile(std::shared_ptr logger, RotatingFileSinkParams params, const char* pattern = nullptr); + + /// + /// Create and return a thread safe console sink + /// + static std::shared_ptr ConsoleSink(); + + /// + /// Create and return a thread safe rotating file sink + /// + /// Params to create file sink + static std::shared_ptr RotatingFileSink(RotatingFileSinkParams params); + + /// + /// Create and return a thread safe basic file sink + /// + /// Name of log file + static std::shared_ptr BasicFileSink(const char* fileName); + + UTILS_API static std::shared_ptr m_CoreLogger; + + }; +} + +//Core Logger macros +#define LOG_CORE_TRACE(...) EA::EACC::Utils::BaseLog::GetCoreLogger()->trace(__VA_ARGS__) +#define LOG_CORE_DEBUG(...) EA::EACC::Utils::BaseLog::GetCoreLogger()->debug(__VA_ARGS__) +#define LOG_CORE_INFO(...) EA::EACC::Utils::BaseLog::GetCoreLogger()->info(__VA_ARGS__) +#define LOG_CORE_WARNING(...) EA::EACC::Utils::BaseLog::GetCoreLogger()->warn(__VA_ARGS__) +#define LOG_CORE_ERROR(...) EA::EACC::Utils::BaseLog::GetCoreLogger()->error(__VA_ARGS__) +#define LOG_CORE_CRITICAL(...) EA::EACC::Utils::BaseLog::GetCoreLogger()->critical(__VA_ARGS__) \ No newline at end of file diff --git a/utils/include/utils/FrameConverter.h b/utils/include/utils/FrameConverter.h new file mode 100644 index 0000000..ffb0f9d --- /dev/null +++ b/utils/include/utils/FrameConverter.h @@ -0,0 +1,67 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +// Implements a helper class to convert the values of a Matrix into other +// values (e.g. color conversions, luminance) using a Loop Up Table + +#pragma once +#include + +namespace cv +{ + class Mat; +} + +namespace EA::EACC::Utils +{ + struct FrameConverterParams + { + FrameConverterParams(std::vector values) : values(values) {}; + std::vector values; //The input array containing the values for the convertion + }; + + class FrameConverter + { + public: + /// + /// Create an instance of the Rgb converter + /// + /// The array of decimal values for Bgr to sRgb convertion + FrameConverter(std::vector& c); + + /// + /// Create an instance of the Rgb converter + /// + /// A FrameRgbConverterParams struct containing the vector of decimal values for Bgr to sRgb convertion + FrameConverter(FrameConverterParams* params); + + /// + /// + /// + /// + /// + cv::Mat* Convert(cv::Mat& mat); + + /// + /// Returns current values to convert to + /// + /// + cv::Mat* GetTable() + { + return m_pMatValues; + }; + + /// + /// Destructor for frame converter + /// + ~FrameConverter(); + + private: + + /// + /// The input array containing the values for the mat values (e.g. color space) convertion + /// + cv::Mat* m_pMatValues = nullptr; + + FrameConverterParams* m_params = nullptr; //config params + }; +} \ No newline at end of file diff --git a/utils/include/utils/JsonWrapper.h b/utils/include/utils/JsonWrapper.h new file mode 100644 index 0000000..f5ed49b --- /dev/null +++ b/utils/include/utils/JsonWrapper.h @@ -0,0 +1,206 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#pragma once +#include +#include +#include +#include +#include "BaseLog.h" +#include +using json = nlohmann::json; + +namespace EA::EACC::Utils +{ + class JsonWrapper + { + private: + json root; + + public: + JsonWrapper(){} + ~JsonWrapper() { } + + //Open a single json file and parse it + void OpenFile(const char* path) { OpenFiles({ path }); } + + //Open multiple files and parse them, mergin them in the order of the vector + void OpenFiles(const std::vector& paths) + { + for (std::string path : paths) { + std::ifstream file(path); + if (file) + { + try + { + //Read contents of file in local parsed json + json parsed = json::parse(file, nullptr, true, true); //ignores comments on json files + + if (root.empty()) + { + root = parsed; + } + else + { + root.update(parsed); + } + } + catch (json::exception& e) + { + LOG_CORE_ERROR(e.what()); + } + } + else + { + LOG_CORE_ERROR("{0} file could not be found", path); + } + } + } + + void WriteFile(const char* path) + { + std::ofstream file(path); + + try + { + file << root; + file.close(); + } + catch(...) + { + LOG_CORE_ERROR("{0} file could not be written", path); + } + + } + + template + void SetVector(const char* section, const char* param, std::vector value) + { + try + { + root[section][param] = value; + } + catch (...) + { + LOG_CORE_ERROR("Could not serialize {0} param from {1] section", param, section); + throw; + } + } + + template + void SetParam(const char* section, const char* param, T value) + { + try + { + root[section][param] = value; + } + catch (...) + { + LOG_CORE_ERROR("Could not serialize {0} param from {1] section", param, section); + throw; + } + } + + template + void SetParam(const char* param, T value) + { + try + { + root[param] = value; + } + catch (...) + { + LOG_CORE_ERROR("Could not serialize {0} param", param); + throw; + } + } + + /// + /// Obtains a specific section of the Json file + /// + /// Section to obtain from the Json file + /// Pointer to the desired section + json* GetSection(const char* section) { return &(root)[section]; } + + /// + /// Loads a vector from Json file + /// + /// + /// Section to read from + /// Parameter from section to load + /// Loaded vector + template + std::vector GetVector(const char* section, const char* param) + { + try + { + return root[section][param].get>(); + } + catch (...) + { + LOG_CORE_ERROR("Could not load {0} vector from {1] section", param, section); + throw; + } + } + + /// + /// Loads a parameter from Json file + /// + /// + /// Section to read from + /// Parameter from section to load + /// Loaded parameter + template + T GetParam(const char* section, const char* param) + { + try + { + return root[section][param]; + } + catch (...) + { + LOG_CORE_ERROR("Could not load {0} param from {1] section", param, section); + throw; + } + } + + /// + /// Loads a parameter from Json file + /// + /// + /// Parameter from section to load + /// Loaded parameter + template + T GetParam(const char* param) + { + try + { + return root[param]; + } + catch (...) + { + LOG_CORE_ERROR("Could not load {0} param", param); + throw; + } + } + + /// + /// Returns true if json entry is defined in json file + /// + /// json entry to check if it's contained + bool ContainsParam(const char* param) + { + return root.contains(param); + } + + /// + /// Returns true if json entry is defined in json file + /// + /// json section in which to check entry + /// json entry to check if it's contained + bool ContainsParam(const char* section, const char* param) + { + return root[section].contains(param); + } + }; + +} diff --git a/utils/src/BaseLog.cpp b/utils/src/BaseLog.cpp new file mode 100644 index 0000000..162fa39 --- /dev/null +++ b/utils/src/BaseLog.cpp @@ -0,0 +1,61 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include "utils/BaseLog.h" + + +namespace EA::EACC::Utils +{ + std::shared_ptr BaseLog::m_CoreLogger; + + void BaseLog::InitCoreLogger(bool console, bool file, int level, RotatingFileSinkParams params, const char* pattern) + { + //Init Core Logger + m_CoreLogger = std::make_shared("CoreLogger"); + + //add sinks to logger + if (console) + { + m_CoreLogger->sinks().push_back(ConsoleSink()); + } + if (file) + { + m_CoreLogger->sinks().push_back(RotatingFileSink(params)); + } + if (pattern != nullptr) //otherwise spdlog default log messages + { + m_CoreLogger->set_pattern(pattern); + } + m_CoreLogger->set_level((spdlog::level::level_enum)level); + } + + void BaseLog::SetLoggerFile(std::shared_ptr logger, RotatingFileSinkParams params, const char* pattern) + { + if (logger != nullptr && !logger->sinks().empty()) + { + logger->sinks().pop_back(); + } + auto sink = RotatingFileSink(params); + + if (pattern != nullptr) + { + sink->set_pattern(pattern); + } + logger->sinks().push_back(sink); + } + + std::shared_ptr BaseLog::ConsoleSink() + { + return std::make_shared(); + } + + std::shared_ptr + BaseLog::RotatingFileSink(RotatingFileSinkParams params) + { + return std::make_shared(params.fileName, params.max_size, params.max_files, params.rotate_on_open); + } + + std::shared_ptr BaseLog::BasicFileSink(const char* fileName) + { + return std::make_shared(fileName); + } +} \ No newline at end of file diff --git a/utils/src/FrameConverter.cpp b/utils/src/FrameConverter.cpp new file mode 100644 index 0000000..3ac6b3c --- /dev/null +++ b/utils/src/FrameConverter.cpp @@ -0,0 +1,32 @@ +//Copyright (C) 2023 Electronic Arts, Inc. All rights reserved. + +#include "utils/FrameConverter.h" +#include + +namespace EA::EACC::Utils +{ + FrameConverter::FrameConverter(std::vector& sRgbValues) + { + m_pMatValues = new cv::Mat(sRgbValues, true); + } + + FrameConverter::FrameConverter(FrameConverterParams* params) : m_params(params) + { + m_pMatValues = new cv::Mat(params->values, true); + } + + FrameConverter::~FrameConverter() + { + if (m_pMatValues != nullptr) { + m_pMatValues->release(); + delete m_pMatValues; + } + } + + cv::Mat* FrameConverter::Convert(cv::Mat& bgrMat) + { + cv::Mat* sRgbMat = new cv::Mat(); + cv::LUT(bgrMat, *m_pMatValues, *sRgbMat); + return sRgbMat; + } +} \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..c162d58 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,14 @@ +{ + "name": "iris", + "version-string": "1.0.0", + "dependencies": [ + "gtest", + "nlohmann-json", + "spdlog", + "giflib", + { + "name": "opencv", + "features": [ "ffmpeg", "contrib" ] + } + ] +} \ No newline at end of file