NoPaste

Proof of Concept

von schorsch_76

SNIPPET_TEXT:
  1. #include <iostream>
  2. #include <future>
  3. #include <vector>
  4. #include <bitset>
  5. #include <optional>
  6. #include <sstream>
  7. #include <iomanip>
  8.  
  9. #include "Vision/FrameGrabber.h"
  10.  
  11. #include <opencv2/img_hash/phash.hpp>
  12.  
  13. using HashValue_t = std::bitset<64>;
  14.  
  15. struct SceneInfo {
  16.     // this is a End Open Sequence [start,end[
  17.     // excluding the end
  18.     int64_t start_frame{};
  19.     int64_t end_frame{};
  20.  
  21.     std::vector<HashValue_t> hashes{};
  22. };
  23.  
  24. struct MovieInfo {
  25.     fs::path file_path{};
  26.     int64_t width{};
  27.     int64_t height{};
  28.     float fps{};
  29.     int64_t frame_count{};
  30.  
  31.     std::vector<SceneInfo> scenes{};
  32. };
  33.  
  34. size_t HammingDistance(const HashValue_t &v1, const HashValue_t &v2) {
  35.     auto x = v1 ^ v2;
  36.     return x.count();
  37. }
  38.  
  39. MovieInfo ProcessFile(const fs::path &input_file) {
  40.     const size_t hamming_distance_threshold = 10;
  41.     const size_t min_scene_length = 25;
  42.  
  43.     Vision::FrameGrabber frame_grabber(input_file);
  44.  
  45.     auto hasher = cv::img_hash::PHash::create();
  46.  
  47.     MovieInfo ret{};
  48.     ret.file_path = input_file;
  49.     ret.width = frame_grabber.GetWidth();
  50.     ret.height = frame_grabber.GetHeight();
  51.     ret.fps = frame_grabber.GetFPS();
  52.     ret.frame_count = frame_grabber.GetFrameCount();
  53.  
  54.     std::optional<HashValue_t> last_hash{};
  55.  
  56.     SceneInfo current_scene_info{};
  57.     current_scene_info.start_frame = 0;
  58.  
  59.     while (frame_grabber.IsOk()) {
  60.         auto frame = frame_grabber.Generate();
  61.  
  62.         cv::Mat out{};
  63.         hasher->compute(frame, out);
  64.         HashValue_t hash_value = out.at<uint64_t>(0, 0);
  65.  
  66.         // add the phash to the scene
  67.         current_scene_info.end_frame = frame_grabber.GetPosition();
  68.         current_scene_info.hashes.push_back(hash_value);
  69.  
  70.         if (!last_hash.has_value()) {
  71.             last_hash = hash_value;
  72.  
  73.             assert(current_scene_info.hashes.size() ==
  74.                 static_cast<size_t>(current_scene_info.end_frame-current_scene_info.start_frame));
  75.         } else {
  76.             // check the hamming distance
  77.             auto hamming_distance = HammingDistance(*last_hash, hash_value);
  78.             last_hash = hash_value;
  79.  
  80.             if (hamming_distance > hamming_distance_threshold) {
  81.                 // we got a new scene
  82.  
  83.                 // if this scene is too short, add it to the previous scene
  84.                 if (current_scene_info.hashes.size() < min_scene_length &&
  85.                     ret.scenes.size() > 0) {
  86.                     auto &last_scene_info = ret.scenes.back();
  87.                     last_scene_info.end_frame = current_scene_info.end_frame;
  88.  
  89.                     for (auto &c: current_scene_info.hashes) {
  90.                         last_scene_info.hashes.push_back(c);
  91.                     }
  92.  
  93.                     current_scene_info = SceneInfo{};
  94.                     current_scene_info.start_frame = frame_grabber.GetPosition();
  95.  
  96.                     assert(last_scene_info.hashes.size() ==
  97.                         static_cast<size_t>(last_scene_info.end_frame-last_scene_info.start_frame));
  98.                 } else {
  99.                     assert(current_scene_info.end_frame > current_scene_info.start_frame);
  100.  
  101.                     assert(current_scene_info.hashes.size() ==
  102.                         static_cast<size_t>(current_scene_info.end_frame-current_scene_info.start_frame));
  103.  
  104.                     ret.scenes.push_back(current_scene_info);
  105.  
  106.                     current_scene_info = SceneInfo{};
  107.                     current_scene_info.start_frame = frame_grabber.GetPosition();
  108.                 }
  109.             }
  110.         }
  111.     }
  112.  
  113.     // add the last scene to the result
  114.     current_scene_info.end_frame = frame_grabber.GetPosition();
  115.     ret.scenes.push_back(current_scene_info);
  116.  
  117.     int64_t last_end = 0;
  118.     for (auto &scene: ret.scenes) {
  119.         assert(scene.start_frame == last_end);
  120.         last_end = scene.end_frame;
  121.     }
  122.  
  123.     return ret;
  124. }
  125.  
  126. void DebugWrite(const MovieInfo &info, fs::path output_folder) {
  127.     int s = 0;
  128.  
  129.     fs::create_directory(output_folder);
  130.  
  131.     Vision::FrameGrabber frame_grabber(info.file_path);
  132.  
  133.     for (auto &scene: info.scenes) {
  134.         // get target filename
  135.         std::ostringstream oss;
  136.         oss << std::setw(5) << std::setfill('0') << std::setprecision(5) << std::fixed << s;;
  137.         ++s;
  138.         fs::path file = output_folder / (oss.str() + ".mp4");
  139.  
  140.         std::cout << "Writing: " << file.string() << std::endl;
  141.  
  142.         // open target file writer
  143.         cv::VideoWriter writer(file.string(), cv::VideoWriter::fourcc('a','v','c','1'),
  144.                                info.fps, cv::Size(info.width, info.height), true);
  145.  
  146.         frame_grabber.SetPosition(scene.start_frame);
  147.         for (int64_t pos = scene.start_frame; pos < scene.end_frame; pos++) {
  148.             auto frame = frame_grabber.Generate();
  149.             writer << frame;
  150.         }
  151.     }
  152. }
  153.  
  154. int main(int argc, const char *argv[]) {
  155.     if (argc < 3) {
  156.         std::cerr << "Usage: " << argv[0] << " input_file1 input_file2" << std::endl;
  157.         return EXIT_FAILURE;
  158.     }
  159.  
  160.     try {
  161.         fs::path input_file1 = argv[1];
  162.         fs::path input_file2 = argv[2];
  163.  
  164.         if (!fs::exists(input_file1)) {
  165.             std::cerr << "Input file 1 not found" << std::endl;
  166.             return EXIT_FAILURE;
  167.         }
  168.         if (!fs::exists(input_file2)) {
  169.             std::cerr << "Input file 2 not found" << std::endl;
  170.             return EXIT_FAILURE;
  171.         }
  172.  
  173.         auto a1 = std::async(std::launch::async, ProcessFile, input_file1);
  174.         auto a2 = std::async(std::launch::async, ProcessFile, input_file2);
  175.  
  176.         auto info1 = a1.get();
  177.         auto info2 = a2.get();
  178.  
  179.         DebugWrite(info1, "/tmp/input1");
  180.         DebugWrite(info2, "/tmp/input2");
  181.  
  182.         return EXIT_SUCCESS;
  183.     } catch (const std::exception &e) {
  184.         std::cerr << e.what() << std::endl;
  185.     }
  186.     return EXIT_FAILURE;
  187. }

Quellcode

Hier kannst du den Code kopieren und ihn in deinen bevorzugten Editor einfügen. PASTEBIN_DOWNLOAD_SNIPPET_EXPLAIN