├── .github └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── Dockerfile ├── LICENSE ├── README.md ├── animeloop-cli.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── animeloop-cli ├── algorithm.cpp ├── algorithm.hpp ├── child_process.cpp ├── child_process.hpp ├── filter.cpp ├── filter.hpp ├── loop_video.cpp ├── loop_video.hpp ├── main.cpp ├── models.cpp ├── models.hpp ├── progress_bar.hpp ├── thread_pool.h ├── utils.cpp └── utils.hpp └── research ├── color.cpp ├── cuts_switch.cpp ├── figure_display.py └── similar_and_same.cpp /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Build the Docker image 11 | run: docker build -t animeloop-cli . 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Custom 2 | cmake-build-debug 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # IntelliJ IDEA 11 | .idea 12 | 13 | # Precompiled Headers 14 | *.gch 15 | *.pch 16 | 17 | # Compiled Dynamic libraries 18 | *.so 19 | *.dylib 20 | *.dll 21 | 22 | # Fortran module files 23 | *.mod 24 | *.smod 25 | 26 | # Compiled Static libraries 27 | *.lai 28 | *.la 29 | *.a 30 | *.lib 31 | 32 | # Executables 33 | *.exe 34 | *.out 35 | *.app 36 | 37 | # Xcode 38 | # 39 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 40 | 41 | ## Build generated 42 | build/ 43 | DerivedData 44 | 45 | ## Various settings 46 | *.pbxuser 47 | !default.pbxuser 48 | *.mode1v3 49 | !default.mode1v3 50 | *.mode2v3 51 | !default.mode2v3 52 | *.perspectivev3 53 | !default.perspectivev3 54 | xcuserdata 55 | 56 | ## Other 57 | *.xccheckout 58 | *.moved-aside 59 | *.xcuserstate 60 | *.xcscmblueprint 61 | 62 | ## CMake 63 | CMakeCache.txt 64 | CMakeFiles 65 | CMakeScripts 66 | Testing 67 | Makefile 68 | cmake_install.cmake 69 | install_manifest.txt 70 | compile_commands.json 71 | CTestTestfile.cmake 72 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "cxxopts"] 2 | path = cxxopts 3 | url = https://github.com/jarro2783/cxxopts.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(animeloop-cli) 3 | 4 | find_package(PkgConfig REQUIRED) 5 | 6 | add_subdirectory(${PROJECT_SOURCE_DIR}/cxxopts) 7 | 8 | include_directories(${PROJECT_SOURCE_DIR}/cxxopts/include) 9 | include_directories(${PROJECT_SOURCE_DIR}/research) 10 | 11 | set(CMAKE_CXX_STANDARD 11) 12 | 13 | IF (CMAKE_SYSTEM_NAME MATCHES "Linux") 14 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") 15 | ENDIF(CMAKE_SYSTEM_NAME MATCHES "Linux") 16 | 17 | set(CLI_SOURCE_FILES 18 | animeloop-cli/algorithm.cpp 19 | animeloop-cli/algorithm.hpp 20 | animeloop-cli/filter.cpp 21 | animeloop-cli/filter.hpp 22 | animeloop-cli/loop_video.cpp 23 | animeloop-cli/loop_video.hpp 24 | animeloop-cli/main.cpp 25 | animeloop-cli/models.cpp 26 | animeloop-cli/models.hpp 27 | animeloop-cli/utils.cpp 28 | animeloop-cli/utils.hpp 29 | animeloop-cli/thread_pool.h 30 | animeloop-cli/progress_bar.hpp 31 | animeloop-cli/child_process.cpp 32 | animeloop-cli/child_process.hpp) 33 | 34 | add_executable(animeloop-cli ${CLI_SOURCE_FILES}) 35 | 36 | set(SIMILAR_SAME_SOURCE_FILES 37 | animeloop-cli/algorithm.cpp 38 | animeloop-cli/algorithm.hpp 39 | research/similar_and_same.cpp) 40 | 41 | add_executable(similar-and-same ${SIMILAR_SAME_SOURCE_FILES}) 42 | 43 | set(CUTS_SWITCH_SOURCE_FILES 44 | animeloop-cli/algorithm.cpp 45 | animeloop-cli/algorithm.hpp 46 | animeloop-cli/utils.cpp 47 | animeloop-cli/utils.hpp 48 | research/cuts_switch.cpp) 49 | 50 | add_executable(cuts-switch ${CUTS_SWITCH_SOURCE_FILES}) 51 | 52 | set(COLOR_SOURCE_FILES 53 | research/color.cpp) 54 | 55 | add_executable(color ${COLOR_SOURCE_FILES}) 56 | 57 | # Jsoncpp 58 | pkg_check_modules(JSONCPP jsoncpp) 59 | target_link_libraries(animeloop-cli ${JSONCPP_LIBRARIES}) 60 | ### temp fix for Ubuntu ### 61 | include_directories(animeloop-cli /usr/include/jsoncpp) 62 | 63 | # OpenCV 64 | find_package( OpenCV 3.2 REQUIRED ) 65 | target_link_libraries( animeloop-cli ${OpenCV_LIBS} ) 66 | target_link_libraries( similar-and-same ${OpenCV_LIBS}) 67 | target_link_libraries( cuts-switch ${OpenCV_LIBS}) 68 | target_link_libraries( color ${OpenCV_LIBS}) 69 | 70 | # Boost 71 | find_package(Boost COMPONENTS filesystem date_time REQUIRED) 72 | target_link_libraries( animeloop-cli ${Boost_LIBRARIES} ) 73 | target_link_libraries( cuts-switch ${Boost_LIBRARIES} ) 74 | 75 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | RUN apt-get update \ 4 | && apt-get upgrade -y \ 5 | && apt-get install -y --no-install-recommends python python-dev python-pip build-essential cmake git pkg-config libjpeg8-dev libtiff5-dev libjasper-dev libpng12-dev libgtk2.0-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libatlas-base-dev gfortran libavresample-dev libgphoto2-dev libgstreamer-plugins-base1.0-dev libdc1394-22-dev \ 6 | && cd /opt \ 7 | && git clone https://github.com/opencv/opencv.git \ 8 | && cd opencv \ 9 | && git checkout 3.4.0 \ 10 | && mkdir build \ 11 | && cd build \ 12 | && cmake \ 13 | -D CMAKE_BUILD_TYPE=RELEASE \ 14 | -D CMAKE_INSTALL_PREFIX=/usr/local \ 15 | -D INSTALL_C_EXAMPLES=OFF \ 16 | -D INSTALL_PYTHON_EXAMPLES=OFF \ 17 | -D BUILD_EXAMPLES=OFF /opt/opencv \ 18 | && make -j $(nproc) \ 19 | && make install \ 20 | && ldconfig \ 21 | && rm -rf /opt/opencv* 22 | 23 | # Install animeloop-cli required packages 24 | RUN apt-get install -y cmake git pkg-config \ 25 | && apt-get install -y libboost-all-dev \ 26 | && apt-get install -y ffmpeg x264 x265 \ 27 | && apt-get install -y libjsoncpp-dev \ 28 | && apt-get clean && rm -rf /var/lib/apt/lists/* 29 | 30 | # Build animeloop-cli 31 | RUN git clone https://github.com/moeoverflow/animeloop-cli.git \ 32 | && cd animeloop-cli \ 33 | && git submodule init && git submodule update \ 34 | && mkdir build && cd build \ 35 | && cmake .. \ 36 | && make animeloop-cli \ 37 | && cp /animeloop-cli/build/animeloop-cli /usr/bin \ 38 | && chmod +x /usr/bin/animeloop-cli 39 | 40 | ENTRYPOINT [ "/usr/bin/animeloop-cli"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 moeoverflow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # animeloop-cli 2 | ![Animeloop Logo](https://s3.moeoverflow.com/animeloop-production/static/animeloop.gif) 3 | 4 | Animeloop command line tool. The goal of this project is to automatically find and cut out the looping fragments in Japanese anime. 5 | 6 | For details, please see my [technical report(English)](https://s3.moeoverflow.com/animeloop-production/static/technical_report_en.pdf), [technical report(Chinese)](https://s3.moeoverflow.com/animeloop-production/static/technical_report_zh.pdf). ([technical_report_output_example](https://s3.moeoverflow.com/animeloop-production/static/technical_report_output_example.zip)) ([technical_report_resources](https://s3.moeoverflow.com/animeloop-production/static/technical_report_resources.zip)) 7 | 8 | ## Installation 9 | 10 | ### Install via Arch Linux User Repository (aur) 11 | #### with [yaourt](https://wiki.archlinux.org/index.php/Yaourt) 12 | ``` Shell 13 | yaourt -S animeloop-cli 14 | ``` 15 | 16 | ### Docker 17 | 18 | ```Shell 19 | docker build -t animeloop-cli . 20 | 21 | # You should change these path: /path/to/input/file.mp4 and /path/to/ouput 22 | docker run \ 23 | -v /path/to/input/file.mp4:/data/file.mp4 \ 24 | -v /path/to/ouput:/loops \ 25 | --rm \ 26 | animeloop-cli \ 27 | -i /data/file.mp4 \ 28 | -o /loops 29 | ``` 30 | 31 | ### Install manually 32 | 33 | #### Modules Init 34 | 35 | ``` Shell 36 | git submodule init 37 | git submodule update 38 | ``` 39 | 40 | 41 | #### Dependencies 42 | 43 | ##### macOS 44 | 45 | ``` Shell 46 | # OpenCV 47 | brew tap homebrew/science 48 | brew install opencv3 --with-contrib --with-ffmpeg --c++11 49 | 50 | # Boost 51 | brew install boost 52 | 53 | # FFmpeg 54 | brew install ffmpeg 55 | 56 | # Jsoncpp 57 | brew install jsoncpp 58 | ``` 59 | 60 | ##### Debian/Ubuntu 61 | 62 | ``` Shell 63 | # OpenCV 64 | 65 | # compiler 66 | sudo apt-get install build-essential 67 | # required 68 | sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev 69 | # optional 70 | # sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev 71 | 72 | mkdir opencv 73 | cd opencv 74 | 75 | wget https://github.com/opencv/opencv/archive/3.3.1.zip -O opencv-3.3.1.zip 76 | unzip opencv-3.3.1.zip 77 | cd opencv-3.3.1 78 | 79 | mkdir build 80 | cd build 81 | cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local .. 82 | 83 | make -j $(nproc) 84 | 85 | # Boost 86 | sudo apt-get install libboost-all-dev 87 | 88 | # FFmpeg !!!! ffmpeg version 3.x required !!!! 89 | sudo add-apt-repository ppa:jonathonf/ffmpeg-3 90 | sudo apt update && sudo apt install ffmpeg libav-tools x264 x265 91 | ## downgrade FFmpeg 92 | sudo apt install ppa-purge && sudo ppa-purge ppa:jonathonf/ffmpeg-3 93 | 94 | # Jsoncpp 95 | sudo apt-get install libjsoncpp-dev 96 | ``` 97 | 98 | ##### Arch Linux 99 | 100 | ```Shell 101 | # Boost 102 | pacman -S boost 103 | 104 | # OpenCV 105 | pacman -S opencv 106 | pacman -S hdf5 107 | 108 | # Jsoncpp 109 | pacman -S jsoncpp 110 | ``` 111 | 112 | ##### Windows MinGW 113 | 114 | ```shell 115 | # - MinGW build require tool MSYS2, you sould download and install it first, then use pacman in MSYS2 Enviroment 116 | # 117 | # - Assume you're building to mingw64, if you'd like to build to mingw32, replace `mingw-w64-x86_64` with `mingw-w64-i686` 118 | # 119 | # - Some packages are updated and not compatible, so some of the steps are using `pacman -U` to install specific version 120 | # packages with USTC Mirrors, you can change `https://mirrors.ustc.edu.cn/msys2/mingw/x86_64/` to other sources. 121 | 122 | # Build deps 123 | pacman -S libintl mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-make 124 | 125 | # Boost 126 | # - It seems that when using Boost 1.66+ will cause build fail 127 | pacman -U https://mirrors.ustc.edu.cn/msys2/mingw/x86_64/mingw-w64-x86_64-boost-1.64.0-3-any.pkg.tar.xz 128 | 129 | # OpenCV 130 | pacman -U https://mirrors.ustc.edu.cn/msys2/mingw/x86_64/mingw-w64-x86_64-opencv-3.4.3-2-any.pkg.tar.xz 131 | pacman -S mingw-w64-x86_64-hdf5 132 | 133 | # Jsoncpp 134 | # - JSONCPP comes with cmake, so if you've install cmake manually, you're not needed to install JSONCPP again 135 | pacman -S mingw-w64-x86_64-jsoncpp jsoncpp-devel 136 | 137 | # FFmpeg 138 | # - Some dll files are missing in MSYS2 MinGW64 FFmpeg package, so you should download it from other builds 139 | # - You can also download it later, as it's not build dependency (but runtime dependency) 140 | # pacman -U https://mirrors.ustc.edu.cn/msys2/mingw/x86_64/mingw-w64-x86_64-ffmpeg-3.4.2-1-any.pkg.tar.xz 141 | wget https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-3.4.2-win64-static.zip 142 | unzip -p ffmpeg-3.4.2-win64-static.zip ffmpeg-3.4.2-win64-static/bin/ffmpeg.exe > /mingw64/bin/ffmpeg.exe 143 | ``` 144 | 145 | ##### Windows via Microsoft Visual Studio 146 | 147 | - Download and install [Microsoft Visual Studio](https://visualstudio.microsoft.com/) 148 | 149 | > Will uses Visual Studio 2015 Enterprise (VC14) as example for below. 150 | 151 | - Download [CMake-GUI](https://cmake.org/download/) 152 | 153 | - Download [Boost prebuilt library](https://sourceforge.net/projects/boost/files/boost-binaries/) for VC14 154 | 155 | > You may need a older version (< 1.66.0) in case of some unknown bug. 156 | 157 | - Download [OpenCV prebuilt library](https://opencv.org/releases/) for Windows 158 | 159 | > OpenCV should be 3.2+ but not 4.x 160 | 161 | - Download [HDF5 prebuilt library](https://www.hdfgroup.org/downloads/hdf5) for VC14 162 | > You may need an account to download the file, you can register for free or build your own. 163 | > If you don't want to install the msi package, run `msiexec /a drive:\path\to\file.msi /qb TARGETDIR=drive:\path\to\extract` 164 | 165 | - Download [JSONCPP prebuilt library](http://access.osvr.com/binary/deps/jsoncpp) for VC14 166 | 167 | > You should grab a FFmpeg 3.x binary from https://ffmpeg.zeranoe.com/ to make it works. 168 | 169 | > You should download OpenH264 1.7.0+ library file from https://github.com/cisco/openh264/releases manually. 170 | 171 | > If you want to use it on Windows, always use backslash to set file path like `D:\path\to\video.mp4`, even under MSYS2 or Cygwin, or boost cannot find the specific path. 172 | 173 | > You may also need Visual C++ 2015 Redistributable. 174 | 175 | #### Compile & Build 176 | 177 | ##### Xcode 178 | 179 | Open `animeloop-cli.xcodeproj` and run it. 180 | 181 | (if you compile opencv source code by yourself, you maybe need edit `Linked Frameworks and Libraries` settings in Xcode project.) 182 | 183 | ##### CMake (*nix) 184 | 185 | ``` Shell 186 | cd animeloop-cli 187 | mkdir build && cd build 188 | cmake .. 189 | make animeloop-cli 190 | ``` 191 | 192 | ##### MSYS2 + MinGW + CMake (Windows) 193 | 194 | ``` Shell 195 | cd animeloop-cli 196 | mkdir build && cd build 197 | cmake -G "MinGw Makefiles" .. 198 | mingw32-make.exe animeloop-cli 199 | ``` 200 | 201 | ## Usage 202 | 203 | ```Shell 204 | anime loop video generator. 205 | 206 | Usage: 207 | animeloop [OPTION...] 208 | 209 | -h, --help Show animeloop help 210 | -v, --version Show animeloop version 211 | -i, --input arg Input video file path 212 | -o, --output arg Output video directory path (default: .) 213 | --title arg Title name of the source video (default: 214 | ) 215 | -t, --thread arg Program run in n threads. (default: ) 217 | --min-duration arg Minimum duration (second) of loop video (default: 218 | 0.6) 219 | --max-duration arg Maximum duration (second) of loop video (default: 220 | 6.0) 221 | --cover Output loop video cover image. 222 | 223 | 224 | # Example 225 | ./animeloop-cli -i ~/your-video-file --max-duration 4 --min-duration 1.0 --cover -o ~/Downloads/ 226 | ``` 227 | 228 | ## Special Thanks 229 | 230 | * [@ccloli](https://github.com/ccloli) for Windows build support 231 | 232 | ## License 233 | 234 | This project is available under the MIT License. See the LICENSE file for more info. -------------------------------------------------------------------------------- /animeloop-cli.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 88010DFF1E52EA6F001CC77B /* loop_video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8812810E1E4DC8EA00C5BAD8 /* loop_video.cpp */; }; 11 | 88088C111E4E28B900601DBD /* libboost_filesystem.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 88088C101E4E28B900601DBD /* libboost_filesystem.dylib */; }; 12 | 88088C121E4E294600601DBD /* libopencv_core.3.2.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 88537EA71E4C52CC00385BCB /* libopencv_core.3.2.0.dylib */; }; 13 | 88088C131E4E294600601DBD /* libopencv_imgcodecs.3.2.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 88537EAA1E4C667500385BCB /* libopencv_imgcodecs.3.2.0.dylib */; }; 14 | 88088C141E4E294600601DBD /* libopencv_imgproc.3.2.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 88537EAC1E4C671F00385BCB /* libopencv_imgproc.3.2.0.dylib */; }; 15 | 88088C151E4E294600601DBD /* libopencv_video.3.2.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 881280F61E4C8E2000C5BAD8 /* libopencv_video.3.2.0.dylib */; }; 16 | 88088C161E4E294600601DBD /* libopencv_videoio.3.2.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 881280F71E4C8E2000C5BAD8 /* libopencv_videoio.3.2.0.dylib */; }; 17 | 88088C181E4E29E400601DBD /* libboost_system.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 88088C171E4E29E400601DBD /* libboost_system.dylib */; }; 18 | 88088C1A1E4E333A00601DBD /* libboost_date_time.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 88088C191E4E333A00601DBD /* libboost_date_time.dylib */; }; 19 | 8818F2EC1E920F260085F368 /* models.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8818F2EA1E920F260085F368 /* models.cpp */; }; 20 | 8818F2EF1E920F3E0085F368 /* utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8818F2ED1E920F3E0085F368 /* utils.cpp */; }; 21 | 8818F2F51E9242310085F368 /* filter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8818F2F31E9242310085F368 /* filter.cpp */; }; 22 | 88537E9D1E4C501900385BCB /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 88537E9C1E4C501900385BCB /* main.cpp */; }; 23 | 88C992831F947CF70012ED53 /* libjsoncpp.1.8.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 88C992821F947CF70012ED53 /* libjsoncpp.1.8.3.dylib */; }; 24 | 88DC61451E585CCE0062B914 /* algorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 88DC61431E585CCE0062B914 /* algorithm.cpp */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXCopyFilesBuildPhase section */ 28 | 88537E971E4C501900385BCB /* CopyFiles */ = { 29 | isa = PBXCopyFilesBuildPhase; 30 | buildActionMask = 2147483647; 31 | dstPath = /usr/share/man/man1/; 32 | dstSubfolderSpec = 0; 33 | files = ( 34 | ); 35 | runOnlyForDeploymentPostprocessing = 1; 36 | }; 37 | /* End PBXCopyFilesBuildPhase section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 88088C001E4E1BEF00601DBD /* cxxopts.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cxxopts.hpp; sourceTree = ""; }; 41 | 88088C101E4E28B900601DBD /* libboost_filesystem.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libboost_filesystem.dylib; path = ../../../../../usr/local/Cellar/boost/1.63.0/lib/libboost_filesystem.dylib; sourceTree = ""; }; 42 | 88088C171E4E29E400601DBD /* libboost_system.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libboost_system.dylib; path = ../../../../../usr/local/Cellar/boost/1.63.0/lib/libboost_system.dylib; sourceTree = ""; }; 43 | 88088C191E4E333A00601DBD /* libboost_date_time.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libboost_date_time.dylib; path = ../../../../../usr/local/Cellar/boost/1.63.0/lib/libboost_date_time.dylib; sourceTree = ""; }; 44 | 881280F61E4C8E2000C5BAD8 /* libopencv_video.3.2.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libopencv_video.3.2.0.dylib; path = ../../../../../usr/local/lib/libopencv_video.3.2.0.dylib; sourceTree = ""; }; 45 | 881280F71E4C8E2000C5BAD8 /* libopencv_videoio.3.2.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libopencv_videoio.3.2.0.dylib; path = ../../../../../usr/local/lib/libopencv_videoio.3.2.0.dylib; sourceTree = ""; }; 46 | 881281001E4CC93D00C5BAD8 /* libopencv_highgui.3.2.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libopencv_highgui.3.2.0.dylib; path = ../../../../../usr/local/lib/libopencv_highgui.3.2.0.dylib; sourceTree = ""; }; 47 | 8812810E1E4DC8EA00C5BAD8 /* loop_video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = loop_video.cpp; path = "animeloop-cli/loop_video.cpp"; sourceTree = ""; }; 48 | 8812810F1E4DC8EA00C5BAD8 /* loop_video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = loop_video.hpp; path = "animeloop-cli/loop_video.hpp"; sourceTree = ""; }; 49 | 8818F2EA1E920F260085F368 /* models.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = models.cpp; path = "animeloop-cli/models.cpp"; sourceTree = ""; }; 50 | 8818F2EB1E920F260085F368 /* models.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = models.hpp; path = "animeloop-cli/models.hpp"; sourceTree = ""; }; 51 | 8818F2ED1E920F3E0085F368 /* utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = utils.cpp; path = "animeloop-cli/utils.cpp"; sourceTree = ""; }; 52 | 8818F2EE1E920F3E0085F368 /* utils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = utils.hpp; path = "animeloop-cli/utils.hpp"; sourceTree = ""; }; 53 | 8818F2F31E9242310085F368 /* filter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = filter.cpp; path = "animeloop-cli/filter.cpp"; sourceTree = ""; }; 54 | 8818F2F41E9242310085F368 /* filter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = filter.hpp; path = "animeloop-cli/filter.hpp"; sourceTree = ""; }; 55 | 883E5A5C1E5C346C004E1554 /* libcrypto.1.0.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.1.0.0.dylib; path = ../../../../../usr/local/Cellar/openssl/1.0.2j/lib/libcrypto.1.0.0.dylib; sourceTree = ""; }; 56 | 884E2E241F30D769001EA03E /* libcrypto.1.0.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.1.0.0.dylib; path = ../../../../../usr/local/Cellar/openssl/1.0.2l/lib/libcrypto.1.0.0.dylib; sourceTree = ""; }; 57 | 88537E991E4C501900385BCB /* animeloop-cli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "animeloop-cli"; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | 88537E9C1E4C501900385BCB /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 59 | 88537EA71E4C52CC00385BCB /* libopencv_core.3.2.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libopencv_core.3.2.0.dylib; path = ../../../../../usr/local/lib/libopencv_core.3.2.0.dylib; sourceTree = ""; }; 60 | 88537EAA1E4C667500385BCB /* libopencv_imgcodecs.3.2.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libopencv_imgcodecs.3.2.0.dylib; path = ../../../../../usr/local/lib/libopencv_imgcodecs.3.2.0.dylib; sourceTree = ""; }; 61 | 88537EAC1E4C671F00385BCB /* libopencv_imgproc.3.2.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libopencv_imgproc.3.2.0.dylib; path = ../../../../../usr/local/lib/libopencv_imgproc.3.2.0.dylib; sourceTree = ""; }; 62 | 889337CA1E9E318C004C69AD /* libopencv_face.3.2.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libopencv_face.3.2.0.dylib; path = ../../../../../usr/local/lib/libopencv_face.3.2.0.dylib; sourceTree = ""; }; 63 | 889337CC1E9E31AE004C69AD /* libopencv_objdetect.3.2.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libopencv_objdetect.3.2.0.dylib; path = ../../../../../usr/local/lib/libopencv_objdetect.3.2.0.dylib; sourceTree = ""; }; 64 | 88C992821F947CF70012ED53 /* libjsoncpp.1.8.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libjsoncpp.1.8.3.dylib; path = ../../../../../usr/local/Cellar/jsoncpp/1.8.3/lib/libjsoncpp.1.8.3.dylib; sourceTree = ""; }; 65 | 88DC61431E585CCE0062B914 /* algorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = algorithm.cpp; path = "animeloop-cli/algorithm.cpp"; sourceTree = ""; }; 66 | 88DC61441E585CCE0062B914 /* algorithm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = algorithm.hpp; path = "animeloop-cli/algorithm.hpp"; sourceTree = ""; }; 67 | 88DC91531E7FD737005C122D /* libMagick++-7.Q16HDRI.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libMagick++-7.Q16HDRI.2.dylib"; path = "../../../../../usr/local/Cellar/imagemagick/7.0.5-3/lib/libMagick++-7.Q16HDRI.2.dylib"; sourceTree = ""; }; 68 | 88DC91551E7FD7BF005C122D /* libMagick++-7.Q16HDRI.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libMagick++-7.Q16HDRI.a"; path = "../../../../../usr/local/Cellar/imagemagick/7.0.5-3/lib/libMagick++-7.Q16HDRI.a"; sourceTree = ""; }; 69 | /* End PBXFileReference section */ 70 | 71 | /* Begin PBXFrameworksBuildPhase section */ 72 | 88537E961E4C501900385BCB /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | 88C992831F947CF70012ED53 /* libjsoncpp.1.8.3.dylib in Frameworks */, 77 | 88088C1A1E4E333A00601DBD /* libboost_date_time.dylib in Frameworks */, 78 | 88088C181E4E29E400601DBD /* libboost_system.dylib in Frameworks */, 79 | 88088C111E4E28B900601DBD /* libboost_filesystem.dylib in Frameworks */, 80 | 88088C121E4E294600601DBD /* libopencv_core.3.2.0.dylib in Frameworks */, 81 | 88088C131E4E294600601DBD /* libopencv_imgcodecs.3.2.0.dylib in Frameworks */, 82 | 88088C141E4E294600601DBD /* libopencv_imgproc.3.2.0.dylib in Frameworks */, 83 | 88088C151E4E294600601DBD /* libopencv_video.3.2.0.dylib in Frameworks */, 84 | 88088C161E4E294600601DBD /* libopencv_videoio.3.2.0.dylib in Frameworks */, 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | /* End PBXFrameworksBuildPhase section */ 89 | 90 | /* Begin PBXGroup section */ 91 | 88088BF91E4DF6B700601DBD /* loop-video */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 8812810E1E4DC8EA00C5BAD8 /* loop_video.cpp */, 95 | 8812810F1E4DC8EA00C5BAD8 /* loop_video.hpp */, 96 | 88DC61431E585CCE0062B914 /* algorithm.cpp */, 97 | 88DC61441E585CCE0062B914 /* algorithm.hpp */, 98 | 8818F2F31E9242310085F368 /* filter.cpp */, 99 | 8818F2F41E9242310085F368 /* filter.hpp */, 100 | 8818F2ED1E920F3E0085F368 /* utils.cpp */, 101 | 8818F2EE1E920F3E0085F368 /* utils.hpp */, 102 | 8818F2EA1E920F260085F368 /* models.cpp */, 103 | 8818F2EB1E920F260085F368 /* models.hpp */, 104 | ); 105 | name = "loop-video"; 106 | sourceTree = ""; 107 | }; 108 | 88088BFA1E4E1BEF00601DBD /* cxxopts */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 88088BFF1E4E1BEF00601DBD /* include */, 112 | ); 113 | path = cxxopts; 114 | sourceTree = ""; 115 | }; 116 | 88088BFF1E4E1BEF00601DBD /* include */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 88088C001E4E1BEF00601DBD /* cxxopts.hpp */, 120 | ); 121 | path = include; 122 | sourceTree = ""; 123 | }; 124 | 88537E901E4C501900385BCB = { 125 | isa = PBXGroup; 126 | children = ( 127 | 88537E9B1E4C501900385BCB /* animeloop-cli */, 128 | 88088BF91E4DF6B700601DBD /* loop-video */, 129 | 88088BFA1E4E1BEF00601DBD /* cxxopts */, 130 | 88537E9A1E4C501900385BCB /* Products */, 131 | 88537EA61E4C52CC00385BCB /* Frameworks */, 132 | ); 133 | sourceTree = ""; 134 | }; 135 | 88537E9A1E4C501900385BCB /* Products */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 88537E991E4C501900385BCB /* animeloop-cli */, 139 | ); 140 | name = Products; 141 | sourceTree = ""; 142 | }; 143 | 88537E9B1E4C501900385BCB /* animeloop-cli */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 88537E9C1E4C501900385BCB /* main.cpp */, 147 | ); 148 | path = "animeloop-cli"; 149 | sourceTree = ""; 150 | }; 151 | 88537EA61E4C52CC00385BCB /* Frameworks */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 88C992821F947CF70012ED53 /* libjsoncpp.1.8.3.dylib */, 155 | 884E2E241F30D769001EA03E /* libcrypto.1.0.0.dylib */, 156 | 889337CC1E9E31AE004C69AD /* libopencv_objdetect.3.2.0.dylib */, 157 | 889337CA1E9E318C004C69AD /* libopencv_face.3.2.0.dylib */, 158 | 88DC91551E7FD7BF005C122D /* libMagick++-7.Q16HDRI.a */, 159 | 88DC91531E7FD737005C122D /* libMagick++-7.Q16HDRI.2.dylib */, 160 | 883E5A5C1E5C346C004E1554 /* libcrypto.1.0.0.dylib */, 161 | 88088C191E4E333A00601DBD /* libboost_date_time.dylib */, 162 | 88088C171E4E29E400601DBD /* libboost_system.dylib */, 163 | 88088C101E4E28B900601DBD /* libboost_filesystem.dylib */, 164 | 881281001E4CC93D00C5BAD8 /* libopencv_highgui.3.2.0.dylib */, 165 | 881280F61E4C8E2000C5BAD8 /* libopencv_video.3.2.0.dylib */, 166 | 881280F71E4C8E2000C5BAD8 /* libopencv_videoio.3.2.0.dylib */, 167 | 88537EAC1E4C671F00385BCB /* libopencv_imgproc.3.2.0.dylib */, 168 | 88537EAA1E4C667500385BCB /* libopencv_imgcodecs.3.2.0.dylib */, 169 | 88537EA71E4C52CC00385BCB /* libopencv_core.3.2.0.dylib */, 170 | ); 171 | name = Frameworks; 172 | sourceTree = ""; 173 | }; 174 | /* End PBXGroup section */ 175 | 176 | /* Begin PBXNativeTarget section */ 177 | 88537E981E4C501900385BCB /* animeloop-cli */ = { 178 | isa = PBXNativeTarget; 179 | buildConfigurationList = 88537EA01E4C501900385BCB /* Build configuration list for PBXNativeTarget "animeloop-cli" */; 180 | buildPhases = ( 181 | 88537E951E4C501900385BCB /* Sources */, 182 | 88537E961E4C501900385BCB /* Frameworks */, 183 | 88537E971E4C501900385BCB /* CopyFiles */, 184 | ); 185 | buildRules = ( 186 | ); 187 | dependencies = ( 188 | ); 189 | name = "animeloop-cli"; 190 | productName = "animeloop-cli"; 191 | productReference = 88537E991E4C501900385BCB /* animeloop-cli */; 192 | productType = "com.apple.product-type.tool"; 193 | }; 194 | /* End PBXNativeTarget section */ 195 | 196 | /* Begin PBXProject section */ 197 | 88537E911E4C501900385BCB /* Project object */ = { 198 | isa = PBXProject; 199 | attributes = { 200 | LastUpgradeCheck = 0900; 201 | ORGANIZATIONNAME = ShinCurry; 202 | TargetAttributes = { 203 | 88537E981E4C501900385BCB = { 204 | CreatedOnToolsVersion = 8.2.1; 205 | ProvisioningStyle = Automatic; 206 | }; 207 | }; 208 | }; 209 | buildConfigurationList = 88537E941E4C501900385BCB /* Build configuration list for PBXProject "animeloop-cli" */; 210 | compatibilityVersion = "Xcode 3.2"; 211 | developmentRegion = English; 212 | hasScannedForEncodings = 0; 213 | knownRegions = ( 214 | en, 215 | ); 216 | mainGroup = 88537E901E4C501900385BCB; 217 | productRefGroup = 88537E9A1E4C501900385BCB /* Products */; 218 | projectDirPath = ""; 219 | projectRoot = ""; 220 | targets = ( 221 | 88537E981E4C501900385BCB /* animeloop-cli */, 222 | ); 223 | }; 224 | /* End PBXProject section */ 225 | 226 | /* Begin PBXSourcesBuildPhase section */ 227 | 88537E951E4C501900385BCB /* Sources */ = { 228 | isa = PBXSourcesBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | 8818F2F51E9242310085F368 /* filter.cpp in Sources */, 232 | 8818F2EC1E920F260085F368 /* models.cpp in Sources */, 233 | 88DC61451E585CCE0062B914 /* algorithm.cpp in Sources */, 234 | 8818F2EF1E920F3E0085F368 /* utils.cpp in Sources */, 235 | 88537E9D1E4C501900385BCB /* main.cpp in Sources */, 236 | 88010DFF1E52EA6F001CC77B /* loop_video.cpp in Sources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | /* End PBXSourcesBuildPhase section */ 241 | 242 | /* Begin XCBuildConfiguration section */ 243 | 88537E9E1E4C501900385BCB /* Debug */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | ALWAYS_SEARCH_USER_PATHS = NO; 247 | CLANG_ANALYZER_NONNULL = YES; 248 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 249 | CLANG_CXX_LIBRARY = "libc++"; 250 | CLANG_ENABLE_MODULES = YES; 251 | CLANG_ENABLE_OBJC_ARC = YES; 252 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 253 | CLANG_WARN_BOOL_CONVERSION = YES; 254 | CLANG_WARN_COMMA = YES; 255 | CLANG_WARN_CONSTANT_CONVERSION = YES; 256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 257 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 258 | CLANG_WARN_EMPTY_BODY = YES; 259 | CLANG_WARN_ENUM_CONVERSION = YES; 260 | CLANG_WARN_INFINITE_RECURSION = YES; 261 | CLANG_WARN_INT_CONVERSION = YES; 262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 263 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 264 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 265 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 266 | CLANG_WARN_STRICT_PROTOTYPES = YES; 267 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 268 | CLANG_WARN_UNREACHABLE_CODE = YES; 269 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 270 | CODE_SIGN_IDENTITY = "-"; 271 | COPY_PHASE_STRIP = NO; 272 | DEBUG_INFORMATION_FORMAT = dwarf; 273 | ENABLE_STRICT_OBJC_MSGSEND = YES; 274 | ENABLE_TESTABILITY = YES; 275 | GCC_C_LANGUAGE_STANDARD = gnu99; 276 | GCC_DYNAMIC_NO_PIC = NO; 277 | GCC_NO_COMMON_BLOCKS = YES; 278 | GCC_OPTIMIZATION_LEVEL = 0; 279 | GCC_PREPROCESSOR_DEFINITIONS = ( 280 | "DEBUG=1", 281 | "$(inherited)", 282 | ); 283 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 284 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 285 | GCC_WARN_UNDECLARED_SELECTOR = YES; 286 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 287 | GCC_WARN_UNUSED_FUNCTION = YES; 288 | GCC_WARN_UNUSED_VARIABLE = YES; 289 | MACOSX_DEPLOYMENT_TARGET = 10.12; 290 | MTL_ENABLE_DEBUG_INFO = YES; 291 | ONLY_ACTIVE_ARCH = YES; 292 | SDKROOT = macosx; 293 | }; 294 | name = Debug; 295 | }; 296 | 88537E9F1E4C501900385BCB /* Release */ = { 297 | isa = XCBuildConfiguration; 298 | buildSettings = { 299 | ALWAYS_SEARCH_USER_PATHS = NO; 300 | CLANG_ANALYZER_NONNULL = YES; 301 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 302 | CLANG_CXX_LIBRARY = "libc++"; 303 | CLANG_ENABLE_MODULES = YES; 304 | CLANG_ENABLE_OBJC_ARC = YES; 305 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 306 | CLANG_WARN_BOOL_CONVERSION = YES; 307 | CLANG_WARN_COMMA = YES; 308 | CLANG_WARN_CONSTANT_CONVERSION = YES; 309 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 310 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 311 | CLANG_WARN_EMPTY_BODY = YES; 312 | CLANG_WARN_ENUM_CONVERSION = YES; 313 | CLANG_WARN_INFINITE_RECURSION = YES; 314 | CLANG_WARN_INT_CONVERSION = YES; 315 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 316 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 317 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 318 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 319 | CLANG_WARN_STRICT_PROTOTYPES = YES; 320 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 321 | CLANG_WARN_UNREACHABLE_CODE = YES; 322 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 323 | CODE_SIGN_IDENTITY = "-"; 324 | COPY_PHASE_STRIP = NO; 325 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 326 | ENABLE_NS_ASSERTIONS = NO; 327 | ENABLE_STRICT_OBJC_MSGSEND = YES; 328 | GCC_C_LANGUAGE_STANDARD = gnu99; 329 | GCC_NO_COMMON_BLOCKS = YES; 330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 332 | GCC_WARN_UNDECLARED_SELECTOR = YES; 333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 334 | GCC_WARN_UNUSED_FUNCTION = YES; 335 | GCC_WARN_UNUSED_VARIABLE = YES; 336 | MACOSX_DEPLOYMENT_TARGET = 10.12; 337 | MTL_ENABLE_DEBUG_INFO = NO; 338 | SDKROOT = macosx; 339 | }; 340 | name = Release; 341 | }; 342 | 88537EA11E4C501900385BCB /* Debug */ = { 343 | isa = XCBuildConfiguration; 344 | buildSettings = { 345 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 346 | CLANG_WARN_DOCUMENTATION_COMMENTS = NO; 347 | HEADER_SEARCH_PATHS = /usr/local/include; 348 | LIBRARY_SEARCH_PATHS = ( 349 | /usr/local/lib, 350 | /usr/local/Cellar/ffmpeg/3.2.3/lib, 351 | /usr/local/Cellar/boost/1.63.0/lib, 352 | /usr/local/Cellar/jsoncpp/1.8.3/lib, 353 | ); 354 | MACOSX_DEPLOYMENT_TARGET = 10.10; 355 | PRODUCT_NAME = "$(TARGET_NAME)"; 356 | }; 357 | name = Debug; 358 | }; 359 | 88537EA21E4C501900385BCB /* Release */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 363 | CLANG_WARN_DOCUMENTATION_COMMENTS = NO; 364 | HEADER_SEARCH_PATHS = /usr/local/include; 365 | LIBRARY_SEARCH_PATHS = ( 366 | /usr/local/lib, 367 | /usr/local/Cellar/ffmpeg/3.2.3/lib, 368 | /usr/local/Cellar/boost/1.63.0/lib, 369 | /usr/local/Cellar/jsoncpp/1.8.3/lib, 370 | ); 371 | MACOSX_DEPLOYMENT_TARGET = 10.10; 372 | PRODUCT_NAME = "$(TARGET_NAME)"; 373 | }; 374 | name = Release; 375 | }; 376 | /* End XCBuildConfiguration section */ 377 | 378 | /* Begin XCConfigurationList section */ 379 | 88537E941E4C501900385BCB /* Build configuration list for PBXProject "animeloop-cli" */ = { 380 | isa = XCConfigurationList; 381 | buildConfigurations = ( 382 | 88537E9E1E4C501900385BCB /* Debug */, 383 | 88537E9F1E4C501900385BCB /* Release */, 384 | ); 385 | defaultConfigurationIsVisible = 0; 386 | defaultConfigurationName = Release; 387 | }; 388 | 88537EA01E4C501900385BCB /* Build configuration list for PBXNativeTarget "animeloop-cli" */ = { 389 | isa = XCConfigurationList; 390 | buildConfigurations = ( 391 | 88537EA11E4C501900385BCB /* Debug */, 392 | 88537EA21E4C501900385BCB /* Release */, 393 | ); 394 | defaultConfigurationIsVisible = 0; 395 | defaultConfigurationName = Release; 396 | }; 397 | /* End XCConfigurationList section */ 398 | }; 399 | rootObject = 88537E911E4C501900385BCB /* Project object */; 400 | } 401 | -------------------------------------------------------------------------------- /animeloop-cli.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /animeloop-cli/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // algorithm.cpp 3 | // animeloop-cli 4 | // 5 | // Created by ShinCurry on 2017/2/14. 6 | // Copyright © 2017年 ShinCurry. All rights reserved. 7 | // 8 | 9 | #include "algorithm.hpp" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace std; 18 | using namespace cv; 19 | 20 | void cvt_image(cv::Mat &image, int length) { 21 | if(3 == image.channels()) { 22 | cvtColor(image, image, CV_RGB2GRAY); 23 | } 24 | if (length != 0) { 25 | resize(image, image, Size(length, length)); 26 | } 27 | } 28 | 29 | string al::aHash(Mat image, int length) { 30 | cvt_image(image, length); 31 | 32 | int sum = accumulate(image.begin(), image.end(), 0.00); 33 | int average = sum / image.cols / image.rows; 34 | 35 | std::string hash_string = ""; 36 | 37 | for (auto it = image.begin(); it != image.end(); ++it) { 38 | hash_string += *it > average ? "1" : "0"; 39 | } 40 | 41 | return hash_string; 42 | } 43 | 44 | 45 | string al::dHash(Mat image, int length) { 46 | cvt_image(image, length); 47 | 48 | string hash_string = ""; 49 | for (int row = 0; row < length; ++row) { 50 | int row_start_index = row * length; 51 | for (int col = 0; col < length-1; ++col) { 52 | int col_left_index = row_start_index + col; 53 | bool diff = image.at(col_left_index) > image.at(col_left_index+1); 54 | hash_string += diff ? "1" : "0"; 55 | } 56 | } 57 | return hash_string; 58 | } 59 | 60 | string al::pHash(Mat image, int length, int dct_length) { 61 | cvt_image(image, length); 62 | 63 | Mat dct = Mat_(image); 64 | cv::dct(dct, dct); 65 | 66 | string hash_string = ""; 67 | 68 | double mean = 0.0; 69 | for (int i = 0; i < dct_length; ++i) 70 | { 71 | for (int j = 0; j < dct_length; ++j) 72 | { 73 | mean += dct.at(i, j); 74 | } 75 | } 76 | mean /= length * length; 77 | 78 | for (int i = 0; i < dct_length; ++i) { 79 | for (int j = 0; j < dct_length; ++j) { 80 | hash_string += (dct.at(i, j) >= mean ? "1" : "0"); 81 | } 82 | } 83 | return hash_string; 84 | } 85 | 86 | 87 | unsigned int al::hamming_distance(string str1, string str2) { 88 | if (str1.empty() || str2.empty()) { return 0; } 89 | 90 | unsigned long len1 = str1.length(); 91 | unsigned long len2 = str2.length(); 92 | 93 | if (len1 != len2) { return 0; } 94 | 95 | unsigned int dist = 0; 96 | for (int i = 0; i < len1; ++i) { 97 | dist += (str1.at(i) != str2.at(i)) ? 1 : 0; 98 | } 99 | return dist; 100 | } 101 | 102 | unsigned int al::hamming_distance(int64_t n1, int64_t n2) { 103 | int64_t difference; 104 | 105 | unsigned int count = 0; 106 | for (difference = n1 ^ n2; difference; difference >>= 1) { 107 | count += difference & 1; 108 | } 109 | return count; 110 | } -------------------------------------------------------------------------------- /animeloop-cli/algorithm.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // algorithm.hpp 3 | // animeloop-cli 4 | // 5 | // Created by ShinCurry on 2017/2/14. 6 | // Copyright © 2017年 ShinCurry. All rights reserved. 7 | // 8 | 9 | #ifndef algorithm_hpp 10 | #define algorithm_hpp 11 | 12 | #include 13 | 14 | #include 15 | 16 | namespace al { 17 | /** 18 | calculate aHash string of an image. 19 | 20 | @param image target image 21 | @param length resize height and width 22 | @return aHash tring 23 | */ 24 | std::string aHash(cv::Mat image, int length); 25 | 26 | /** 27 | calculate dHash string of an image. 28 | 29 | @param image target image 30 | @param length resize height and width 31 | @return dHash string 32 | */ 33 | std::string dHash(cv::Mat image, int length); 34 | 35 | /** 36 | calculate pHash string of an image. 37 | 38 | @param image target image 39 | @param length resize height and width (recommend length > 32) 40 | @param dct_length dct size (recommend length > 8) 41 | @return pHash string 42 | */ 43 | std::string pHash(cv::Mat image, int length, int dct_length); 44 | 45 | 46 | /** 47 | Calculate hamming distance between two image hash strings. 48 | 49 | @param str1 a image hash string 50 | @param str2 another image hash string 51 | @return hamming distance value 52 | */ 53 | unsigned int hamming_distance(std::string str1, std::string str2); 54 | 55 | /** 56 | Calculate hamming distance between two image hash int64 value. 57 | 58 | @param str1 a image hash int64 value 59 | @param str2 another image hash int64 value 60 | @return hamming distance value 61 | */ 62 | unsigned int hamming_distance(int64_t n1, int64_t n2); 63 | } 64 | 65 | 66 | #endif /* algorithm_hpp */ 67 | -------------------------------------------------------------------------------- /animeloop-cli/child_process.cpp: -------------------------------------------------------------------------------- 1 | #include "child_process.hpp" 2 | 3 | #ifdef _WIN32 4 | #include 5 | #else 6 | #include 7 | #include 8 | #endif 9 | 10 | #include 11 | 12 | using namespace std; 13 | 14 | void child_process(string cli) { 15 | #ifdef _WIN32 16 | 17 | STARTUPINFO si; 18 | PROCESS_INFORMATION pi; 19 | DWORD exit_code = 0; 20 | LPSTR exec = const_cast(cli.c_str()); 21 | 22 | ZeroMemory(&si, sizeof(si)); 23 | si.cb = sizeof(si); 24 | ZeroMemory(&pi, sizeof(pi)); 25 | 26 | // Start the child process. 27 | if (!CreateProcess( 28 | NULL, // No module name (use command line) 29 | exec, // Command line 30 | NULL, // Process handle not inheritable 31 | NULL, // Thread handle not inheritable 32 | FALSE, // Set handle inheritance to FALSE 33 | 0, // No creation flags 34 | NULL, // Use parent's environment block 35 | NULL, // Use parent's starting directory 36 | &si, // Pointer to STARTUPINFO structure 37 | &pi // Pointer to PROCESS_INFORMATION structure 38 | )) { 39 | printf("CreateProcess failed (%d)\n", GetLastError()); 40 | return; 41 | } 42 | 43 | // Wait until child process exits. 44 | WaitForSingleObject(pi.hProcess, INFINITE); 45 | 46 | // Get exit code 47 | GetExitCodeProcess(pi.hProcess, &exit_code); 48 | if (exit_code) { 49 | cerr << "The child was killed or segfaulted or something." << endl; 50 | } 51 | 52 | // Close process and thread handles. 53 | CloseHandle(pi.hProcess); 54 | CloseHandle(pi.hThread); 55 | 56 | #else 57 | 58 | int pid = fork(); 59 | 60 | if(pid == -1) { 61 | /* Failed to fork */ 62 | cerr << "Fork failed" << endl; 63 | throw; 64 | } else if(pid == 0) { 65 | int status = system(cli.c_str()); 66 | if (status) { 67 | cerr << "The child was killed or segfaulted or something." << endl; 68 | } 69 | exit(100); 70 | } 71 | 72 | /* Optionally, wait for the child to exit and get 73 | the exit status. */ 74 | int status; 75 | waitpid(pid, &status, 0); 76 | if(! WIFEXITED(status)) { 77 | cerr << "The child was killed or segfaulted or something." << endl; 78 | } 79 | 80 | status = WEXITSTATUS(status); 81 | 82 | #endif 83 | 84 | 85 | } -------------------------------------------------------------------------------- /animeloop-cli/child_process.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by shin on 12/2/17. 3 | // 4 | 5 | #ifndef ANIMELOOP_CLI_CHILD_PROCESS_H 6 | #define ANIMELOOP_CLI_CHILD_PROCESS_H 7 | 8 | #include 9 | 10 | void child_process(std::string cli); 11 | 12 | #endif //ANIMELOOP_CLI_CHILD_PROCESS_H 13 | -------------------------------------------------------------------------------- /animeloop-cli/filter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // filter.cpp 3 | // animeloop-cli 4 | // 5 | // Created by ShinCurry on 2017/4/3. 6 | // Copyright © 2017年 ShinCurry. All rights reserved. 7 | // 8 | 9 | #include "loop_video.hpp" 10 | #include "filter.hpp" 11 | #include "algorithm.hpp" 12 | #include "utils.hpp" 13 | #include 14 | 15 | using namespace al; 16 | using namespace std; 17 | using namespace cv; 18 | 19 | /** 20 | filter all_loops 21 | Find all loop fragments that the begin frame is same as end one, then remove the repetition. 22 | 23 | @param loop LoopVideo instance 24 | @param durations reference of durations value 25 | */ 26 | void al::filter::all_loops(al::LoopVideo *loop, al::LoopDurations &durations) { 27 | LoopDurations _durations; 28 | int min_duration_frame = loop->min_duration * loop->info.fps; 29 | int max_duration_frame = loop->max_duration * loop->info.fps; 30 | auto hashs = loop->phash_strings; 31 | 32 | if (hashs.size() == 0) return; 33 | 34 | for (auto it = hashs.begin(); it != (hashs.end() - min_duration_frame); ++it) { 35 | long begin = distance(hashs.begin(), it); 36 | 37 | long end = -1; 38 | for (int i = min_duration_frame; i < max_duration_frame; ++i) { 39 | if (it+i+1 == hashs.end()) { break; } 40 | 41 | int distance = hamming_distance(*it, *(it+i)); 42 | double similar = 1.00 - double(distance) / (*it).size(); 43 | if (similar >= 0.98) { 44 | end = begin + i; 45 | } 46 | } 47 | if (end != -1) { 48 | _durations.push_back(make_tuple(begin, end)); 49 | } 50 | } 51 | 52 | auto end_unique = unique(_durations.begin(), _durations.end(), [](LoopDuration prev, LoopDuration next) { 53 | return get<0>(prev) == get<0>(next) || get<1>(prev) == get<1>(next); 54 | }); 55 | _durations.erase(end_unique, _durations.end()); 56 | 57 | durations = _durations; 58 | } 59 | 60 | /** 61 | filter cut_in_loop 62 | Check if there is a cut in loop. 63 | 64 | @param loop LoopVideo instance 65 | @param durations reference of durations value 66 | */ 67 | void al::filter::cut_in_loop(al::LoopVideo * loop, LoopDurations &durations) { 68 | if (durations.size() == 0) return; 69 | 70 | LoopDurations _durations; 71 | 72 | for (auto duration : durations) { 73 | long begin, end; 74 | tie(begin, end) = duration; 75 | 76 | bool cut_in_loop = false; 77 | for (auto cut : loop->cuts) { 78 | if (cut >= begin && cut <= end) { 79 | cut_in_loop = true; 80 | break; 81 | } 82 | } 83 | 84 | if (!cut_in_loop) { 85 | _durations.push_back(duration); 86 | } 87 | } 88 | 89 | durations = _durations; 90 | } 91 | 92 | /** 93 | filter loop_nearby 94 | find the loop fragments that is same as nearby one, then remove it. 95 | 96 | @param loop LoopVideo instance 97 | @param durations reference of durations value 98 | */ 99 | void al::filter::loop_nearby(al::LoopVideo *loop, al::LoopDurations &durations) { 100 | if (durations.size() == 0) return; 101 | 102 | LoopDurations _durations; 103 | auto hashs = loop->phash_strings; 104 | 105 | 106 | for (auto it = durations.begin(); it != durations.end()-1; ++it) { 107 | long prev_begin, prev_end, next_begin, next_end; 108 | tie(prev_begin, prev_end) = *it; 109 | tie(next_begin, next_end) = *(it+1); 110 | 111 | int distance_begin = hamming_distance(hashs[prev_begin], hashs[next_begin]); 112 | int distance_end = hamming_distance(hashs[prev_end], hashs[next_end]); 113 | 114 | int filter_n = hashs[0].size() / 2.5; 115 | if (distance_begin >= filter_n && distance_end >= filter_n) { 116 | _durations.push_back(*it); 117 | } 118 | } 119 | durations = _durations; 120 | } 121 | 122 | /** 123 | filter loop_frame_tiny_change 124 | find the loop fragments with a small frame pixels change. 125 | 126 | @param loop LoopVideo instance 127 | @param durations reference of durations value 128 | */ 129 | void al::filter::loop_tiny_frame_change(al::LoopVideo * loop, al::LoopDurations &durations) { 130 | if (durations.size() == 0) return; 131 | 132 | LoopDurations _durations; 133 | auto hashs = loop->phash_strings; 134 | 135 | for (auto it = durations.begin(); it != durations.end()-1; ++it) { 136 | long begin_frame, end_frame; 137 | tie(begin_frame, end_frame) = *it; 138 | 139 | std::vector distances; 140 | for (long i = begin_frame; i < end_frame-1; ++i) { 141 | int distance = hamming_distance(hashs[i], hashs[i+1]); 142 | distances.push_back(distance); 143 | } 144 | 145 | double variance = get_variance(distances); 146 | const double filter_n = 2.0; 147 | if (variance > filter_n) { 148 | _durations.push_back(*it); 149 | } 150 | } 151 | durations = _durations; 152 | } 153 | 154 | 155 | /** 156 | filter loop_white_or_black 157 | find the loop fragments that the color of begin/end frame is most black or white, then remove it. 158 | 159 | @param loop LoopVideo instance 160 | @param durations reference of durations value 161 | */ 162 | void al::filter::loop_white_or_black(al::LoopVideo *loop, al::LoopDurations &durations) { 163 | if (durations.size() == 0) return; 164 | 165 | LoopDurations _durations; 166 | 167 | VideoCapture capture; 168 | capture.open(loop->resized_video_filepath.string()); 169 | 170 | for (auto it = durations.begin(); it != durations.end(); ++it) { 171 | long start_frame, end_frame; 172 | tie(start_frame, end_frame) = *it; 173 | cv::Mat begin_image; 174 | cv::Mat end_image; 175 | 176 | 177 | capture.set(CV_CAP_PROP_POS_FRAMES, start_frame); 178 | capture.read(begin_image); 179 | capture.set(CV_CAP_PROP_POS_FRAMES, end_frame); 180 | capture.read(end_image); 181 | 182 | double mean_begin = get_mean(begin_image); 183 | double mean_end = get_mean(end_image); 184 | 185 | const int magic_number = 20; 186 | if (mean_begin < magic_number || mean_begin > 255 - magic_number) { 187 | continue; 188 | } 189 | if (mean_end < magic_number || mean_end > 255 - magic_number) { 190 | continue; 191 | } 192 | 193 | _durations.push_back(*it); 194 | } 195 | capture.release(); 196 | 197 | durations = _durations; 198 | } 199 | 200 | /** 201 | filter loop_same_color 202 | check if the color in the begin frame is the same as the end one. 203 | 204 | @param loop LoopVideo instance 205 | @param durations reference of durations value 206 | */ 207 | void al::filter::loop_same_color(al::LoopVideo * loop, al::LoopDurations &durations) { 208 | if (durations.size() == 0) return; 209 | 210 | LoopDurations _durations; 211 | 212 | for (auto duration : durations) { 213 | long start_frame, end_frame; 214 | tie(start_frame, end_frame) = duration; 215 | 216 | Vec3b mean_rgb_start = get_mean_rgb(loop->resized_frames[start_frame]); 217 | Vec3b mean_rgb_end = get_mean_rgb(loop->resized_frames[end_frame]); 218 | 219 | if (mean_rgb_start[0] == mean_rgb_end[0] && 220 | mean_rgb_start[1] == mean_rgb_end[1] && 221 | mean_rgb_start[2] == mean_rgb_end[2] ) { 222 | _durations.push_back(duration); 223 | } 224 | } 225 | 226 | durations = _durations; 227 | } 228 | -------------------------------------------------------------------------------- /animeloop-cli/filter.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // filter.hpp 3 | // animeloop-cli 4 | // 5 | // Created by ShinCurry on 2017/4/3. 6 | // Copyright © 2017年 ShinCurry. All rights reserved. 7 | // 8 | 9 | #ifndef filter_hpp 10 | #define filter_hpp 11 | 12 | #include 13 | #include "models.hpp" 14 | 15 | namespace al { 16 | namespace filter { 17 | void all_loops(al::LoopVideo * loop, al::LoopDurations &durations); 18 | void cut_in_loop(al::LoopVideo * loop, al::LoopDurations &durations); 19 | void loop_nearby(al::LoopVideo * loop, al::LoopDurations &durations); 20 | void loop_tiny_frame_change(al::LoopVideo * loop, al::LoopDurations &durations); 21 | void loop_white_or_black(al::LoopVideo * loop, al::LoopDurations &durations); 22 | void loop_same_color(al::LoopVideo * loop, al::LoopDurations &durations); 23 | } 24 | } 25 | 26 | #endif /* filter_hpp */ 27 | -------------------------------------------------------------------------------- /animeloop-cli/loop_video.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // loop_video.cpp 3 | // animeloop-cli 4 | // 5 | // Created by ShinCurry on 2017/2/10. 6 | // Copyright © 2017年 ShinCurry. All rights reserved. 7 | // 8 | 9 | #include "loop_video.hpp" 10 | #include "algorithm.hpp" 11 | #include "utils.hpp" 12 | #include "filter.hpp" 13 | #include "thread_pool.h" 14 | #include "progress_bar.hpp" 15 | #include "child_process.hpp" 16 | 17 | #include 18 | 19 | using namespace std; 20 | using namespace boost::filesystem; 21 | using namespace cv; 22 | using namespace al; 23 | 24 | 25 | al::LoopVideo::LoopVideo(std::string input, std::string output_path) { 26 | this->filename = path(input).stem().string(); 27 | this->title = this->filename; 28 | 29 | this->input_filepath = path(input); 30 | this->output_path = path(output_path); 31 | this->loops_dirpath = path(output_path).append("loops").append(this->filename); 32 | this->caches_dirpath = path(output_path).append("caches").append(this->filename); 33 | 34 | string phash_filename = this->filename + "_pHash.txt"; 35 | this->phash_filepath = path(caches_dirpath).append(phash_filename); 36 | string resized_video_filename = this->filename + "_" + to_string(this->resize_length) + "x" + to_string(this->resize_length) + "." + this->output_type; 37 | this->resized_video_filepath = path(caches_dirpath).append(resized_video_filename); 38 | string cuts_filename = this->filename + "_cuts.txt"; 39 | this->cuts_filepath = path(caches_dirpath).append(cuts_filename); 40 | 41 | if (!exists(this->output_path)) { 42 | create_directories(this->output_path); 43 | } 44 | if (!exists(this->caches_dirpath)) { 45 | create_directories(this->caches_dirpath); 46 | } 47 | if (!exists(this->loops_dirpath)) { 48 | create_directories(this->loops_dirpath); 49 | } 50 | } 51 | 52 | void al::LoopVideo::init() { 53 | cout << ":: init" << endl; 54 | resize_video(this->input_filepath, this->resized_video_filepath, cv::Size(this->resize_length, this->resize_length)); 55 | get_frames(this->resized_video_filepath, this->resized_frames); 56 | get_hash_strings(this->resized_video_filepath, "pHash", this->hash_length, this->phash_dct_length, this->phash_strings, this->phash_filepath); 57 | get_cuts(this->resized_video_filepath, this->cuts, this->cuts_filepath); 58 | this->info = get_info(this->resized_video_filepath.string()); 59 | } 60 | 61 | void al::LoopVideo::filter() { 62 | cout << ":: finding loops..." << endl; 63 | filter::all_loops(this, this->durations); 64 | LoopDurations filtered_durations(this->durations); 65 | 66 | filter::cut_in_loop(this, filtered_durations); 67 | filter::loop_nearby(this, filtered_durations); 68 | filter::loop_tiny_frame_change(this, filtered_durations); 69 | filter::loop_white_or_black(this, filtered_durations); 70 | filter::loop_same_color(this, filtered_durations); 71 | 72 | this->filtered_durations = filtered_durations; 73 | cout << "[o] done." << endl; 74 | 75 | } 76 | 77 | void al::LoopVideo::print(LoopDurations durations) { 78 | VideoInfo info = get_info(this->input_filepath); 79 | 80 | cout << ":: total " << durations.size() << " loops:" << endl; 81 | for (auto duration : durations) { 82 | long start_frame, end_frame; 83 | tie(start_frame, end_frame) = duration; 84 | cout << "[o] " << al::time_string(start_frame / info.fps) << " ~ " << al::time_string(end_frame / info.fps) << endl; 85 | } 86 | } 87 | 88 | void al::LoopVideo::generate(const LoopDurations durations) { 89 | VideoInfo info = get_info(this->input_filepath); 90 | 91 | Json::Value videos_json; 92 | videos_json["title"] = this->title; 93 | videos_json["version"] = kOutputVersion; 94 | Json::Value source_json; 95 | source_json["filename"] = this->filename; 96 | videos_json["source"].append(source_json); 97 | 98 | 99 | ThreadPool pool(threads); 100 | vector> futures; 101 | 102 | ProgressBar progressBar1(durations.size()+1, 35); 103 | ++progressBar1; 104 | cout << ":: saving loop video files..." << endl; 105 | /* 106 | * Multi-threads support for generating result video files. 107 | * */ 108 | for_each(durations.begin(), durations.end(), [&](const LoopDuration duration) { 109 | long start_frame, end_frame; 110 | tie(start_frame, end_frame) = duration; 111 | 112 | string base_filename = "frame_from_" + to_string(start_frame) + "_to_" + to_string(end_frame); 113 | auto video_filename = 114 | base_filename + "_" + to_string(info.size.width) + "x" + to_string(info.size.height) + "." + 115 | this->output_type; 116 | auto video_filepath = path(this->loops_dirpath).append(video_filename).string(); 117 | auto cover_filename = base_filename + "_cover.jpg"; 118 | auto cover_filepath = path(this->loops_dirpath).append(cover_filename).string(); 119 | auto input_filename = this->input_filepath.string(); 120 | 121 | if (!exists(video_filepath)) { 122 | futures.push_back(pool.enqueue([=, &progressBar1]() -> void { 123 | VideoCapture capture; 124 | capture.open(input_filename); 125 | capture.set(CV_CAP_PROP_POS_FRAMES, start_frame); 126 | auto fourcc = CV_FOURCC('a', 'v', 'c', '1'); 127 | cv::VideoWriter writer(video_filepath, fourcc, info.fps, info.size); 128 | 129 | cv::Mat image; 130 | long frame = start_frame; 131 | while (capture.read(image)) { 132 | // Exclude the last frame. 133 | if (frame >= end_frame) { 134 | break; 135 | } 136 | 137 | writer.write(image); 138 | frame++; 139 | } 140 | writer.release(); 141 | capture.release(); 142 | 143 | ++progressBar1; 144 | progressBar1.display(); 145 | })); 146 | } else { 147 | ++progressBar1; 148 | progressBar1.display(); 149 | } 150 | }); 151 | 152 | /* 153 | * Waiting for all thread jobs done. 154 | * */ 155 | for_each(futures.begin(), futures.end(), [](future& f) { 156 | f.get(); 157 | }); 158 | 159 | progressBar1.done(); 160 | cout << "[o] done." << endl; 161 | 162 | ProgressBar progressBar2(durations.size()+1, 35); 163 | ++progressBar2; 164 | 165 | cout << ":: saving loop cover files..." << endl; 166 | /* 167 | * generate cover files and info json file. 168 | * */ 169 | for_each(durations.begin(), durations.end(), [&](const LoopDuration duration) { 170 | long start_frame, end_frame; 171 | tie(start_frame, end_frame) = duration; 172 | auto video_duration = (end_frame - start_frame) / info.fps; 173 | 174 | string base_filename = "frame_from_" + to_string(start_frame) + "_to_" + to_string(end_frame); 175 | auto video_filename = base_filename + "_" + to_string(info.size.width) + "x" + to_string(info.size.height) + "." + this->output_type; 176 | auto video_filepath = path(this->loops_dirpath).append(video_filename).string(); 177 | auto cover_filename = base_filename + "_cover.jpg"; 178 | auto cover_filepath = path(this->loops_dirpath).append(cover_filename).string(); 179 | auto input_filename = this->input_filepath.string(); 180 | 181 | if (cover_enabled && !exists(cover_filepath)) { 182 | futures.push_back(pool.enqueue([=]() -> void { 183 | const string i = video_filepath; 184 | const string o = cover_filepath; 185 | const string cli = "ffmpeg -loglevel panic -i \"" + i + "\" -vframes 1 -f image2 \"" + o + "\""; 186 | child_process(cli); 187 | })); 188 | } 189 | 190 | ++progressBar2; 191 | progressBar2.display(); 192 | 193 | // Save video info json file. 194 | Json::Value video_json; 195 | 196 | Json::Value files_json; 197 | files_json["mp4_1080p"] = video_filename; 198 | files_json["jpg_1080p"] = cover_filename; 199 | video_json["files"] = files_json; 200 | 201 | video_json["duration"] = video_duration; 202 | 203 | Json::Value frame_json; 204 | frame_json["begin"] = to_string(start_frame); 205 | frame_json["end"] = to_string(end_frame); 206 | video_json["frame"] = frame_json; 207 | 208 | Json::Value period_json; 209 | period_json["begin"] = al::time_string(start_frame / info.fps); 210 | period_json["end"] = al::time_string(end_frame / info.fps); 211 | video_json["period"] = period_json; 212 | 213 | videos_json["loops"].append(video_json); 214 | }); 215 | 216 | progressBar2.done(); 217 | cout << "[o] done." << endl; 218 | 219 | cout << ":: saving info json file..." << endl; 220 | string json_string = videos_json.toStyledString(); 221 | std::ofstream out(path(this->loops_dirpath).append(this->filename + ".json").string()); 222 | out << json_string; 223 | out.close(); 224 | 225 | cout << "[o] done." << endl; 226 | } 227 | -------------------------------------------------------------------------------- /animeloop-cli/loop_video.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // loop_video.hpp 3 | // animeloop-cli 4 | // 5 | // Created by ShinCurry on 2017/2/10. 6 | // Copyright © 2017年 ShinCurry. All rights reserved. 7 | // 8 | 9 | #ifndef loop_video_hpp 10 | #define loop_video_hpp 11 | 12 | #include "models.hpp" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace al { 19 | const std::string kVersion = "2.0.3"; 20 | const std::string kOutputVersion = "2.0.0"; 21 | 22 | class LoopVideo { 23 | public: 24 | // infomation 25 | std::string filename; 26 | std::string title; 27 | 28 | // I/O path 29 | boost::filesystem::path input_filepath; 30 | boost::filesystem::path output_path; 31 | boost::filesystem::path loops_dirpath; 32 | boost::filesystem::path caches_dirpath; 33 | boost::filesystem::path phash_filepath; 34 | boost::filesystem::path cuts_filepath; 35 | boost::filesystem::path resized_video_filepath; 36 | std::string output_type = "mp4"; 37 | 38 | // settings 39 | double min_duration = 0.6; 40 | double max_duration = 6.0; 41 | int resize_length = 64; 42 | int hash_length = 64; 43 | int phash_dct_length = 16; 44 | bool cover_enabled = false; 45 | 46 | unsigned int threads = 1; 47 | 48 | // Data 49 | VideoInfo info; 50 | HashVector phash_strings; 51 | LoopDurations durations; 52 | LoopDurations filtered_durations; 53 | FrameVector resized_frames; 54 | CutVector cuts; 55 | 56 | /** 57 | Class LoopVideo constructor. 58 | 59 | @param input input file path 60 | @param output output file path 61 | */ 62 | LoopVideo(std::string input, std::string output_path); 63 | 64 | /** 65 | Initial resized video file, pHash and dHash file for cache. 66 | */ 67 | void init(); 68 | 69 | /** 70 | Animeloop algorithm filter workflow. 71 | */ 72 | void filter(); 73 | 74 | /** 75 | Print durations data. 76 | 77 | @param durations durations data 78 | */ 79 | void print(LoopDurations durations); 80 | 81 | /** 82 | Generate loop video files from durations data. 83 | 84 | @param durations durations data 85 | */ 86 | void generate(const al::LoopDurations filtered_durations); 87 | 88 | private: 89 | bool ffmpeg_available = false; 90 | }; 91 | } 92 | 93 | #endif /* loop_video_hpp */ 94 | -------------------------------------------------------------------------------- /animeloop-cli/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // animeloop-cli 4 | // 5 | // Created by ShinCurry on 2017/2/9. 6 | // Copyright © 2017年 ShinCurry. All rights reserved. 7 | // 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include "cxxopts.hpp" 15 | #include "loop_video.hpp" 16 | #include "utils.hpp" 17 | 18 | using namespace std; 19 | using namespace boost::filesystem; 20 | using namespace cxxopts; 21 | 22 | int main(int argc, char * argv[]) { 23 | auto rpath = path(argv[0]).parent_path(); 24 | double min_duration, max_duration; 25 | 26 | try { 27 | cxxopts::Options options("animeloop", "anime loop video generator."); 28 | 29 | unsigned int threads = 0; 30 | string input, output, title; 31 | 32 | options.add_options() 33 | ("h,help", "Show animeloop help") 34 | ("v,version", "Show animeloop version") 35 | ("i,input", "Input video file path", value(input)) 36 | ("o,output", "Output video directory path", value(output)->default_value(rpath.string())) 37 | ("title", "Title name of the source video (default: )", value(title)) 38 | ("t,thread", "Program run in n threads. (default: )", value(threads)) 39 | ("min-duration", "Minimum duration (second) of loop video", value(min_duration)->default_value("0.6")) 40 | ("max-duration", "Maximum duration (second) of loop video", value(max_duration)->default_value("6.0")) 41 | ("cover", "Output loop video cover image.") 42 | ; 43 | 44 | options.parse(argc, argv); 45 | 46 | if (options.count("version")) { 47 | cout << "version: " << al::kVersion << endl; 48 | } 49 | 50 | if (options.count("help")) { 51 | cout << options.help() << endl; 52 | } 53 | 54 | if (options.count("input")) { 55 | cout << "Animeloop (´・ω・`)" << endl; 56 | cout << endl; 57 | cout << ":: detecting external program..." << endl; 58 | 59 | if (detect_ffmpeg()) { 60 | cout << "[o] detected ffmpeg." << endl; 61 | } else { 62 | cout << "[x] not detect ffmpeg." << endl; 63 | } 64 | 65 | if (title == "") { 66 | title = path(input).stem().string(); 67 | } 68 | 69 | if (threads == 0) { 70 | threads = 1; 71 | } 72 | 73 | al::LoopVideo loop_video(input, output); 74 | loop_video.title = title; 75 | loop_video.min_duration = min_duration; 76 | loop_video.max_duration = max_duration; 77 | loop_video.threads = threads; 78 | 79 | if (options.count("cover")) { 80 | loop_video.cover_enabled = true; 81 | } 82 | 83 | loop_video.init(); 84 | loop_video.filter(); 85 | loop_video.print(loop_video.filtered_durations); 86 | loop_video.generate(loop_video.filtered_durations); 87 | 88 | cout << ":: output file in: " << loop_video.output_path << endl; 89 | cout << endl; 90 | cout << "(:3[____] " << endl; 91 | } 92 | } catch (const OptionException& e) { 93 | cout << "[x] error parsing options: " << e.what() << endl; 94 | exit(1); 95 | } 96 | 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /animeloop-cli/models.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // models.cpp 3 | // animeloop-cli 4 | // 5 | // Created by ShinCurry on 2017/4/3. 6 | // Copyright © 2017年 ShinCurry. All rights reserved. 7 | // 8 | 9 | #include "models.hpp" 10 | -------------------------------------------------------------------------------- /animeloop-cli/models.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // models.hpp 3 | // animeloop-cli 4 | // 5 | // Created by ShinCurry on 2017/4/3. 6 | // Copyright © 2017年 ShinCurry. All rights reserved. 7 | // 8 | 9 | #ifndef models_hpp 10 | #define models_hpp 11 | 12 | #include 13 | #include 14 | 15 | namespace al { 16 | typedef std::tuple LoopDuration; 17 | typedef std::vector LoopDurations; 18 | typedef std::vector HashVector; 19 | typedef std::vector FrameVector; 20 | typedef std::vector CutVector; 21 | 22 | struct VideoInfo { 23 | double fps; 24 | double fourcc; 25 | cv::Size size; 26 | int frame_count; 27 | }; 28 | } 29 | 30 | 31 | #endif /* models_hpp */ 32 | -------------------------------------------------------------------------------- /animeloop-cli/progress_bar.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File from https://github.com/prakhar1989/progress-cpp 3 | * */ 4 | 5 | #ifndef PROGRESSBAR_PROGRESSBAR_HPP 6 | #define PROGRESSBAR_PROGRESSBAR_HPP 7 | 8 | #include 9 | #include 10 | 11 | class ProgressBar { 12 | private: 13 | unsigned int ticks = 0; 14 | 15 | const unsigned int total_ticks; 16 | const unsigned int bar_width; 17 | const char complete_char = '='; 18 | const char incomplete_char = ' '; 19 | const std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now(); 20 | 21 | public: 22 | ProgressBar(unsigned int total, unsigned int width, char complete, char incomplete) : 23 | total_ticks {total}, bar_width {width}, complete_char {complete}, incomplete_char {incomplete} {} 24 | 25 | ProgressBar(unsigned int total, unsigned int width) : total_ticks {total}, bar_width {width} {} 26 | 27 | unsigned int operator++() { return ++ticks; } 28 | 29 | void display() const 30 | { 31 | float progress = (float) ticks / total_ticks; 32 | int pos = (int) (bar_width * progress); 33 | 34 | std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); 35 | auto time_elapsed = std::chrono::duration_cast(now-start_time).count(); 36 | 37 | std::cout << "["; 38 | 39 | for (int i = 0; i < bar_width; ++i) { 40 | if (i < pos) std::cout << complete_char; 41 | else if (i == pos) std::cout << ">"; 42 | else std::cout << incomplete_char; 43 | } 44 | std::cout << "] " << int(progress * 100.0) << "%\r"; 45 | std::cout.flush(); 46 | } 47 | 48 | void done() const 49 | { 50 | display(); 51 | std::cout << std::endl; 52 | } 53 | }; 54 | 55 | #endif //PROGRESSBAR_PROGRESSBAR_HPP -------------------------------------------------------------------------------- /animeloop-cli/thread_pool.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File from https://github.com/progschj/ThreadPool 3 | * */ 4 | 5 | 6 | #ifndef THREAD_POOL_H 7 | #define THREAD_POOL_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | class ThreadPool { 20 | public: 21 | ThreadPool(size_t); 22 | template 23 | auto enqueue(F&& f, Args&&... args) 24 | -> std::future::type>; 25 | ~ThreadPool(); 26 | private: 27 | // need to keep track of threads so we can join them 28 | std::vector< std::thread > workers; 29 | // the task queue 30 | std::queue< std::function > tasks; 31 | 32 | // synchronization 33 | std::mutex queue_mutex; 34 | std::condition_variable condition; 35 | bool stop; 36 | }; 37 | 38 | // the constructor just launches some amount of workers 39 | inline ThreadPool::ThreadPool(size_t threads) 40 | : stop(false) 41 | { 42 | for(size_t i = 0;i task; 49 | 50 | { 51 | std::unique_lock lock(this->queue_mutex); 52 | this->condition.wait(lock, 53 | [this]{ return this->stop || !this->tasks.empty(); }); 54 | if(this->stop && this->tasks.empty()) 55 | return; 56 | task = std::move(this->tasks.front()); 57 | this->tasks.pop(); 58 | } 59 | 60 | task(); 61 | } 62 | } 63 | ); 64 | } 65 | 66 | // add new work item to the pool 67 | template 68 | auto ThreadPool::enqueue(F&& f, Args&&... args) 69 | -> std::future::type> 70 | { 71 | using return_type = typename std::result_of::type; 72 | 73 | auto task = std::make_shared< std::packaged_task >( 74 | std::bind(std::forward(f), std::forward(args)...) 75 | ); 76 | 77 | std::future res = task->get_future(); 78 | { 79 | std::unique_lock lock(queue_mutex); 80 | 81 | // don't allow enqueueing after stopping the pool 82 | if(stop) 83 | throw std::runtime_error("enqueue on stopped ThreadPool"); 84 | 85 | tasks.emplace([task](){ (*task)(); }); 86 | } 87 | condition.notify_one(); 88 | return res; 89 | } 90 | 91 | // the destructor joins all threads 92 | inline ThreadPool::~ThreadPool() 93 | { 94 | { 95 | std::unique_lock lock(queue_mutex); 96 | stop = true; 97 | } 98 | condition.notify_all(); 99 | for(std::thread &worker: workers) 100 | worker.join(); 101 | } 102 | 103 | #endif -------------------------------------------------------------------------------- /animeloop-cli/utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // utils.cpp 3 | // animeloop-cli 4 | // 5 | // Created by ShinCurry on 2017/4/3. 6 | // Copyright © 2017年 ShinCurry. All rights reserved. 7 | // 8 | 9 | #include "utils.hpp" 10 | #include "algorithm.hpp" 11 | #include "progress_bar.hpp" 12 | #include "child_process.hpp" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | using namespace std; 21 | using namespace boost::filesystem; 22 | using namespace cv; 23 | using namespace al; 24 | 25 | VideoInfo al::get_info(VideoCapture &capture) { 26 | VideoInfo info; 27 | info.fps = capture.get(CV_CAP_PROP_FPS); 28 | info.fourcc = capture.get(CV_CAP_PROP_FOURCC); 29 | auto height = capture.get(CV_CAP_PROP_FRAME_HEIGHT); 30 | auto width = capture.get(CV_CAP_PROP_FRAME_WIDTH); 31 | info.size = cv::Size(width, height); 32 | info.frame_count = capture.get(CV_CAP_PROP_FRAME_COUNT); 33 | return info; 34 | } 35 | 36 | VideoInfo al::get_info(boost::filesystem::path filename) { 37 | VideoCapture capture; 38 | capture.open(filename.string()); 39 | return get_info(capture); 40 | } 41 | 42 | void al::resize_video(path input_filepath, path output_filepath, Size size) { 43 | path temp_output = path(output_filepath).parent_path().append("temp"); 44 | path temp_filename = path(temp_output).append(output_filepath.filename().string()); 45 | if (!exists(temp_output)) { 46 | create_directories(temp_output); 47 | } else { 48 | if (exists(temp_filename)) { 49 | remove(temp_filename); 50 | } 51 | } 52 | 53 | auto if_exists = exists(output_filepath); 54 | if (!if_exists) { 55 | if (detect_ffmpeg()) { 56 | cout << ":: resizing video..." << endl; 57 | const string i = input_filepath.string(); 58 | const string s = (to_string(size.width) + "x" + to_string(size.height)); 59 | const string o = temp_filename.string(); 60 | 61 | const string cli = "ffmpeg -loglevel panic -stats -i \"" + i + "\" -s " + s + " -an \"" + o + "\""; 62 | child_process(cli); 63 | cout << "done." << endl; 64 | } else { 65 | // Calculate hash string per frame. 66 | cout << ":: resizing video..." << endl; 67 | VideoCapture capture; 68 | capture.open(input_filepath.string()); 69 | 70 | VideoInfo info = get_info(capture); 71 | auto fourcc = CV_FOURCC('a', 'v', 'c', '1'); 72 | VideoWriter writer(temp_filename.string(), fourcc, info.fps, size); 73 | 74 | ProgressBar progressBar(info.frame_count, 35); 75 | 76 | cv::Mat image; 77 | while (capture.read(image)) { 78 | cv::resize(image, image, size); 79 | writer.write(image); 80 | image.release(); 81 | 82 | ++progressBar; 83 | progressBar.display(); 84 | } 85 | 86 | writer.release(); 87 | capture.release(); 88 | 89 | progressBar.done(); 90 | cout << "[o] done." << endl; 91 | } 92 | 93 | rename(temp_filename, output_filepath); 94 | } 95 | } 96 | 97 | bool al::get_frames(boost::filesystem::path file, FrameVector &frames) { 98 | cout << ":: get frames from resized video file." << endl; 99 | FrameVector _frames; 100 | VideoCapture capture; 101 | capture.open(file.string()); 102 | 103 | VideoInfo info = get_info(capture); 104 | ProgressBar progressBar(info.frame_count, 35); 105 | 106 | cv::Mat image; 107 | while (capture.read(image)) { 108 | _frames.push_back(image); 109 | ++progressBar; 110 | progressBar.display(); 111 | } 112 | capture.release(); 113 | frames = _frames; 114 | 115 | progressBar.done(); 116 | cout << "[o] done." << endl; 117 | return true; 118 | } 119 | 120 | void al::get_hash_strings(boost::filesystem::path filename, string type, int length, int dct_length, HashVector &hash_strings, path hash_file) { 121 | path temp_path = hash_file.parent_path().append("temp"); 122 | path temp_filename = path(temp_path).append(hash_file.filename().string()); 123 | 124 | if (!exists(temp_path)) { 125 | create_directories(temp_path); 126 | } else { 127 | if (exists(temp_filename)) { 128 | remove(temp_filename); 129 | } 130 | } 131 | 132 | auto if_exists = exists(hash_file); 133 | VideoCapture capture; 134 | if (if_exists) { 135 | // Read video frames hash string from file. 136 | std::cout << ":: restore " << type << " value from file..." << std::endl; 137 | 138 | std::ifstream input_file(hash_file.string()); 139 | HashVector hashs; 140 | 141 | while (input_file) { 142 | string line; 143 | getline(input_file, line); 144 | hashs.push_back(line); 145 | } 146 | input_file.close(); 147 | 148 | hash_strings = hashs; 149 | cout << "[o] done." << endl; 150 | } else { 151 | cout << ":: calculating " << type << " value..." << endl; 152 | 153 | VideoCapture capture; 154 | capture.open(filename.string()); 155 | VideoInfo info = get_info(capture); 156 | HashVector hashs; 157 | 158 | ProgressBar progressBar(info.frame_count, 35); 159 | 160 | cv::Mat image; 161 | while (capture.read(image)) { 162 | string hash = al::pHash(image, length, dct_length); 163 | hashs.push_back(hash); 164 | image.release(); 165 | 166 | ++progressBar; 167 | progressBar.display(); 168 | } 169 | hash_strings = hashs; 170 | 171 | progressBar.done(); 172 | cout << "[o] done." << endl; 173 | 174 | cout << ":: save " << type << " value into file..." << endl; 175 | std::ofstream output_file(temp_filename.string()); 176 | ostream_iterator output_iterator(output_file, "\n"); 177 | std::copy(hashs.begin(), hashs.end(), output_iterator); 178 | output_file.close(); 179 | 180 | rename(temp_filename, hash_file); 181 | cout << "[o] done." << endl; 182 | } 183 | } 184 | 185 | void al::get_cuts(boost::filesystem::path resized_video_filepath, CutVector &cuts, boost::filesystem::path cuts_filepath) { 186 | path temp_path = cuts_filepath.parent_path().append("temp"); 187 | path temp_filename = path(temp_path).append(cuts_filepath.filename().string()); 188 | CutVector _cuts; 189 | 190 | if (!exists(temp_path)) { 191 | create_directories(temp_path); 192 | } else { 193 | if (exists(temp_filename)) { 194 | remove(temp_filename); 195 | } 196 | } 197 | 198 | auto if_exists = exists(cuts_filepath); 199 | if (if_exists) { 200 | cout << ":: restore cuts data from file..." << endl; 201 | 202 | std::ifstream input_file(cuts_filepath.string()); 203 | 204 | int cut; 205 | while(input_file >> cut) { 206 | _cuts.push_back(cut); 207 | } 208 | 209 | input_file.close(); 210 | cout << "[o] done." << endl; 211 | } else { 212 | cout << ":: calculate cuts data..." << endl; 213 | 214 | Mat prevframe, nextframe, differframe; 215 | int i = 0; 216 | 217 | VideoCapture capture(resized_video_filepath.string()); 218 | VideoInfo info = get_info(capture); 219 | 220 | ProgressBar progressBar(info.frame_count, 35); 221 | 222 | capture.read(prevframe); 223 | cvtColor(prevframe, prevframe, CV_RGB2GRAY); 224 | ++progressBar; 225 | 226 | while (capture.read(nextframe)) { 227 | cvtColor(nextframe, nextframe, CV_RGB2GRAY); 228 | 229 | absdiff(nextframe, prevframe, differframe); 230 | 231 | int count = 0; 232 | int total = differframe.rows * differframe.cols; 233 | 234 | for (int row = 0; row < differframe.rows; ++row) { 235 | for (int col = 0; col < differframe.cols; ++col) { 236 | if (differframe.at(row, col) > 10) { 237 | count++; 238 | } 239 | } 240 | } 241 | 242 | double rate = (total != 0) ? double(count) / total : 0; 243 | 244 | if (rate > 0.8) { 245 | _cuts.push_back(i); 246 | } 247 | 248 | prevframe = nextframe; 249 | 250 | ++progressBar; 251 | progressBar.display(); 252 | } 253 | capture.release(); 254 | 255 | 256 | std::ofstream output_file(temp_filename.string()); 257 | 258 | for (auto cut : _cuts) { 259 | output_file << cut << " "; 260 | } 261 | output_file.close(); 262 | 263 | rename(temp_filename, cuts_filepath); 264 | 265 | progressBar.done(); 266 | cout << "[o] done." << endl; 267 | } 268 | cuts = _cuts; 269 | } 270 | 271 | 272 | double al::get_variance(std::vector distances) { 273 | double sum = std::accumulate(distances.begin(), distances.end(), 0.0); 274 | double mean = sum / distances.size(); 275 | double accum = 0.0; 276 | 277 | std::for_each(distances.begin(), distances.end(), [&](const double distance) { 278 | accum += (distance-mean) * (distance-mean); 279 | }); 280 | double variance = sqrt(accum / (distances.size() - 1)); 281 | return variance; 282 | } 283 | 284 | double al::get_mean(Mat image) { 285 | int64 sum = std::accumulate(image.begin(), image.end(), 0); 286 | double mean = sum / double(image.cols * image.rows); 287 | return mean; 288 | } 289 | 290 | Vec3b al::get_mean_rgb(Mat image) { 291 | int total_b = 0, total_g = 0, total_r = 0; 292 | for (int row = 0; row < image.rows; ++row) { 293 | for (int col = 0; col < image.cols; ++col) { 294 | total_b += image.at(row, col)[0]; 295 | total_g += image.at(row, col)[1]; 296 | total_r += image.at(row, col)[2]; 297 | } 298 | } 299 | int mean_b = total_b / (image.cols*image.cols); 300 | int mean_g = total_g / (image.cols*image.cols); 301 | int mean_r = total_r / (image.cols*image.cols); 302 | 303 | Vec3b rgb(mean_b, mean_g, mean_r); 304 | return rgb; 305 | } 306 | 307 | std::string al::time_string(double seconds) { 308 | auto ms = boost::posix_time::milliseconds(seconds * 1000); 309 | auto time = boost::posix_time::time_duration(ms); 310 | return boost::posix_time::to_simple_string(time); 311 | } 312 | 313 | bool detect_ffmpeg() { 314 | #ifdef _WIN32 315 | string find_ffmpeg_command = "where /q ffmpeg"; 316 | #else 317 | string find_ffmpeg_command = "which ffmpeg &> /dev/null"; 318 | #endif 319 | 320 | return system(find_ffmpeg_command.c_str()) == 0; 321 | } -------------------------------------------------------------------------------- /animeloop-cli/utils.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // utils.hpp 3 | // animeloop-cli 4 | // 5 | // Created by ShinCurry on 2017/4/3. 6 | // Copyright © 2017年 ShinCurry. All rights reserved. 7 | // 8 | 9 | #ifndef utils_hpp 10 | #define utils_hpp 11 | 12 | #include "models.hpp" 13 | 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | namespace al { 22 | VideoInfo get_info(boost::filesystem::path filename); 23 | 24 | VideoInfo get_info(cv::VideoCapture &capture); 25 | 26 | /** 27 | Resize a video and write into a new file. 28 | 29 | @param file intput path of origin video file 30 | @param output output path of resized video file 31 | @param size resizing size 32 | */ 33 | void resize_video(boost::filesystem::path input, boost::filesystem::path output, cv::Size size); 34 | 35 | bool get_frames(boost::filesystem::path file, FrameVector &frames); 36 | 37 | void get_hash_strings(boost::filesystem::path filename, std::string type, int length, int dct_length, al::HashVector &hash_strings, 38 | boost::filesystem::path hash_file); 39 | 40 | void get_cuts(boost::filesystem::path resized_filepath, CutVector &cuts, boost::filesystem::path cuts_filepath); 41 | 42 | 43 | double get_variance(std::vector distances); 44 | 45 | double get_mean(cv::Mat image); 46 | 47 | cv::Vec3b get_mean_rgb(cv::Mat image); 48 | 49 | 50 | 51 | /** 52 | Convert seconds value to a time string. 53 | 54 | @param seconds seconds value 55 | @return Time string 56 | */ 57 | std::string time_string(double seconds); 58 | } 59 | 60 | bool detect_ffmpeg(); 61 | 62 | #endif /* utils_hpp */ 63 | -------------------------------------------------------------------------------- /research/color.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | using namespace cv; 6 | 7 | vector imagepaths = { "/home/shincurry/Dropbox/Paper/animeloop/Resources/color/p1.jpg", 8 | "/home/shincurry/Dropbox/Paper/animeloop/Resources/color/p2.jpg", 9 | "/home/shincurry/Dropbox/Paper/animeloop/Resources/color/p3.jpg", 10 | "/home/shincurry/Dropbox/Paper/animeloop/Resources/color/p11.jpg", 11 | "/home/shincurry/Dropbox/Paper/animeloop/Resources/color/p21.jpg", 12 | "/home/shincurry/Dropbox/Paper/animeloop/Resources/color/p31.jpg"}; 13 | 14 | int main(int argc, char** argv) 15 | { 16 | for (auto it = imagepaths.begin(); it != imagepaths.end(); ++it) 17 | { 18 | // Mat image = imread(*it, CV_LOAD_IMAGE_COLOR); 19 | Mat image = imread(*it, CV_LOAD_IMAGE_COLOR); 20 | 21 | long total_b = 0, total_g = 0, total_r = 0; 22 | for (int row = 0; row < image.rows; ++row) { 23 | for (int col = 0; col < image.cols; ++col) { 24 | total_b += image.at(row, col)[0]; 25 | total_g += image.at(row, col)[1]; 26 | total_r += image.at(row, col)[2]; 27 | } 28 | } 29 | int mean_b = total_b / (image.cols*image.cols); 30 | int mean_g = total_g / (image.cols*image.cols); 31 | int mean_r = total_r / (image.cols*image.cols); 32 | printf("(%d, %d, %d)\n", mean_b, mean_g, mean_r); 33 | } 34 | 35 | 36 | return 0; 37 | } -------------------------------------------------------------------------------- /research/cuts_switch.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | using namespace cv; 6 | 7 | string basepath = "/home/shincurry/Dropbox/Paper/animeloop/Resources/lens_switch/"; 8 | string videopath = basepath + "lens_switch_example_.mkv"; 9 | //string videopath = "/home/shincurry/Downloads/[LoliHouse] Kimi no Na wa [BDRip 1920x1080 AVC-yuv420p8 AAC PGS(chs,eng,jpn)].mkv"; 10 | 11 | int main(int argc, char** argv) 12 | { 13 | VideoCapture capture(videopath); 14 | if (!capture.isOpened()) 15 | { 16 | cout << "No camera or video input!\n" << endl; 17 | return -1; 18 | } 19 | 20 | int total_frames = capture.get(CV_CAP_PROP_FRAME_COUNT); 21 | 22 | Mat prevframe, nextframe, differframe; 23 | int i = 0; 24 | 25 | 26 | ofstream file(basepath + "framediff.txt"); 27 | 28 | capture.read(prevframe); 29 | cvtColor(prevframe, prevframe, CV_RGB2GRAY); 30 | 31 | while (capture.read(nextframe)) { 32 | cvtColor(nextframe, nextframe, CV_RGB2GRAY); 33 | 34 | //帧差法 35 | absdiff(nextframe, prevframe, differframe); 36 | // imshow("Prev Frame", prevframe); 37 | // imshow("Next Frame", nextframe); 38 | // imshow("Differ Frame", differframe); 39 | 40 | int count = 0; 41 | int total = differframe.rows * differframe.cols; 42 | 43 | for (int row = 0; row < differframe.rows; ++row) { 44 | for (int col = 0; col < differframe.cols; ++col) { 45 | if (differframe.at(row, col) > 10) { 46 | count++; 47 | } 48 | } 49 | } 50 | 51 | double rate = (total != 0) ? double(count) / total : 0; 52 | 53 | if (rate > 0.85) { 54 | // cvWaitKey(); 55 | cout << "len switched. " << i << endl; 56 | } 57 | 58 | if (file.is_open()) 59 | { 60 | 61 | file << rate << endl; 62 | } 63 | 64 | char c = waitKey(33); 65 | if (c == 27) break; 66 | 67 | prevframe = nextframe; 68 | i++; 69 | } 70 | 71 | // file.close(); 72 | return 0; 73 | } -------------------------------------------------------------------------------- /research/figure_display.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import cv2 4 | 5 | basepath = "/home/shincurry/Dropbox/Paper/animeloop/Resources/" 6 | 7 | framediff = [] 8 | framediff64 = [] 9 | similarities_ahash = [] 10 | similarities_dhash = [] 11 | similarities_phash = [] 12 | 13 | with open(basepath+"lens_switch/framediff.txt") as f: 14 | framediff = map(float, f) 15 | with open(basepath+"lens_switch/framediff64.txt") as f: 16 | framediff64 = map(float, f) 17 | 18 | with open(basepath+"similarities_ahash64.txt") as f: 19 | similarities_ahash = map(float, f)[0:480] 20 | with open(basepath+"similarities_dhash64.txt") as f: 21 | similarities_dhash = map(float, f)[0:480] 22 | with open(basepath+"similarities_phash64.txt") as f: 23 | similarities_phash = map(float, f)[0:480] 24 | 25 | 26 | # frames = range(1, len(similarities_dhash)+1) 27 | # plt.plot(frames, similarities_dhash, 'b', frames, similarities_phash, 'r', frames, similarities_ahash, 'y') 28 | # plt.show() 29 | 30 | frames = range(1, len(framediff)+1) 31 | plt.plot(range(1, len(framediff64)+1), framediff64, 'r') 32 | # plt.plot(range(1, len(framediff)+1), framediff, 'b') 33 | plt.show() -------------------------------------------------------------------------------- /research/similar_and_same.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../animeloop-cli/algorithm.hpp" 7 | 8 | using namespace std; 9 | using namespace cv; 10 | 11 | vector imagepaths = { "/home/shincurry/Dropbox/Paper/animeloop/Resources/p1.jpg", 12 | "/home/shincurry/Dropbox/Paper/animeloop/Resources/p2.jpg", 13 | "/home/shincurry/Dropbox/Paper/animeloop/Resources/p3.jpg", 14 | "/home/shincurry/Dropbox/Paper/animeloop/Resources/p4.jpg" }; 15 | int main(int argc, char * argv[]) 16 | { 17 | vector images; 18 | for (auto it = imagepaths.begin(); it != imagepaths.end(); ++it) 19 | { 20 | Mat image = imread(*it, CV_LOAD_IMAGE_COLOR); 21 | images.push_back(image); 22 | } 23 | 24 | vector>> hashss; 25 | hashss.push_back(make_pair(8, vector())); 26 | hashss.push_back(make_pair(32, vector())); 27 | hashss.push_back(make_pair(64, vector())); 28 | hashss.push_back(make_pair(128, vector())); 29 | 30 | for (auto itt = hashss.begin(); itt != hashss.end(); ++itt) 31 | { 32 | auto len = itt->first; 33 | auto hashs = itt->second; 34 | 35 | for (auto image : images) 36 | { 37 | string hash = al::pHash(image, len, len); 38 | hashs.push_back(hash); 39 | } 40 | 41 | for (auto it = hashs.begin()+1; it != hashs.end(); ++it) 42 | { 43 | auto index = distance(hashs.begin(), it); 44 | 45 | int xs = al::hamming_distance(*hashs.begin(), *it); 46 | cout << "p1 and " << "p" << index+1 << ": " << 1 - double(xs) / (len * len) << endl; 47 | } 48 | cout << endl; 49 | } 50 | 51 | return 0; 52 | } 53 | 54 | 55 | --------------------------------------------------------------------------------