diff --git a/.gitignore b/.gitignore index e2d5d25..f810ede 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /__build -/proprietary +*.aps diff --git a/.gitmodules b/.gitmodules index 5dcc879..37f8e75 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "thirdparty/JM"] path = thirdparty/JM url = https://github.com/slavanap/JM.git -[submodule "Sub3D"] - path = proprietary - url = p:/sub3d [submodule "thirdparty/libudfread"] path = thirdparty/libudfread url = git://git.videolan.org/libudfread.git diff --git a/AvsTools/CMakeLists.txt b/AvsTools/CMakeLists.txt index 4761616..6b837d1 100644 --- a/AvsTools/CMakeLists.txt +++ b/AvsTools/CMakeLists.txt @@ -10,6 +10,7 @@ set(HEADERS Filter.RestoreAlpha.hpp Filter.SequentialToSeekable.hpp Filter.SetLogger.hpp + Filter.VideoCorrelation.hpp Tools.AviSynth.hpp Tools.AviSynth.Frame.hpp Tools.Motion.hpp @@ -25,6 +26,7 @@ set(SOURCES Filter.RestoreAlpha.cpp Filter.SequentialToSeekable.cpp Filter.SetLogger.cpp + Filter.VideoCorrelation.cpp Tools.AviSynth.cpp Tools.AviSynth.Frame.cpp Tools.Motion.cpp @@ -32,12 +34,6 @@ set(SOURCES Tools.WinApi.cpp ) -if (EXISTS "${CMAKE_SOURCE_DIR}/proprietary/Tools.Motion.Proprietary.hpp") - add_definitions(-D MOTION_PROPRIETARY) - include_directories(${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_SOURCE_DIR}/proprietary") - list(APPEND HEADERS "${CMAKE_SOURCE_DIR}/proprietary/Tools.Motion.Proprietary.hpp") -endif() - set_precompiled_header(stdafx.h stdafx.cpp) add_library(${PROJECT_NAME} STATIC ${HEADERS} ${SOURCES}) diff --git a/AvsTools/Filter.SequentialToSeekable.hpp b/AvsTools/Filter.SequentialToSeekable.hpp index 00a8587..7cfaac4 100644 --- a/AvsTools/Filter.SequentialToSeekable.hpp +++ b/AvsTools/Filter.SequentialToSeekable.hpp @@ -30,7 +30,25 @@ namespace Filter { public: SequentialToSeekable(IScriptEnvironment* env, const char* command); PVideoFrame WINAPI GetFrame(int n, IScriptEnvironment* env); - + + bool WINAPI GetParity(int n) override { + return clip->GetParity(n); + } + + void WINAPI GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) override { + return clip->GetAudio(buf, start, count, env); + } + +#if defined __AVISYNTH_H__ + void WINAPI SetCacheHints(int cachehints, int frame_range) override { + clip->SetCacheHints(cachehints, frame_range); + } +#elif defined __AVISYNTH_6_H__ + int WINAPI SetCacheHints(int cachehints, int frame_range) override { + return clip->SetCacheHints(cachehints, frame_range); + } +#endif + static AvsParams CreateParams; static AVSValue __cdecl Create(AVSValue args, void* user_data, IScriptEnvironment* env); private: diff --git a/AvsTools/Filter.VideoCorrelation.cpp b/AvsTools/Filter.VideoCorrelation.cpp new file mode 100644 index 0000000..4b2ddbb --- /dev/null +++ b/AvsTools/Filter.VideoCorrelation.cpp @@ -0,0 +1,215 @@ +#include "stdafx.h" +#include +#include + +#include "Tools.AviSynth.Frame.hpp" +#include "Filter.VideoCorrelation.hpp" + +namespace Filter { + + // API & Tools + + std::ostream& operator<<(std::ostream& out, const VideoCorrelation::Signature& vs) { + out << vs.size() << std::endl; + for (auto it = vs.begin(); it != vs.end(); ++it) + out << *it << " "; + return out; + } + + std::istream& operator>>(std::istream& in, VideoCorrelation::Signature& vs) { + size_t temp; + if (!(in >> temp)) + goto error; + vs.resize(temp); + for (size_t i = 0; i < temp; ++i) { + if (!(in >> vs[i])) + goto error; + } + return in; + error: + throw std::runtime_error("Invalid array in file"); + } + + AvsParams VideoCorrelation::CreateParams = "[input]c[overlay]c[shift]i[threshold]i[preprocess_input]s"; + AVSValue VideoCorrelation::Create(AVSValue args, void* user_data, IScriptEnvironment* env) { + PClip input = args[0].AsClip(); + PClip overlay = args[1].AsClip(); + int shift = args[2].AsInt(-1); + int threshold = args[3].AsInt(0); + const char* preprocess = args[4].AsString(nullptr); + try { + Signature sig; + if (preprocess != nullptr) { + std::ifstream f(preprocess); + if (!f.good()) + throw std::runtime_error("Can't open file"); + f >> sig; + } + return new VideoCorrelation(env, input, overlay, shift, sig, threshold); + } + catch (std::exception& e) { + env->ThrowError(e.what()); + return AVSValue(); + } + } + + AvsParams VideoCorrelation::PreprocessParams = "c[output]s"; + AVSValue VideoCorrelation::Preprocess(AVSValue args, void* user_data, IScriptEnvironment* env) { + try { + std::ofstream f(args[1].AsString()); + if (!f.good()) + throw std::runtime_error("Can't create file"); + auto sig = Preprocess(env, args[0].AsClip()); + f << sig; + f.close(); + return AVSValue(); + } + catch (std::exception& e) { + env->ThrowError("%s", e.what()); + return AVSValue(); + } + } + + AvsParams VideoCorrelation::GetShiftParams = "[overlay]c[input_sig]s"; + AVSValue VideoCorrelation::GetShift(AVSValue args, void* user_data, IScriptEnvironment* env) { + try { + std::ifstream f(args[1].AsString()); + if (!f.good()) + throw std::runtime_error("Can't open file"); + Signature input_sig; + f >> input_sig; + return GetShift(env, args[0].AsClip(), input_sig); + } + catch (std::exception& e) { + env->ThrowError("%s", e.what()); + return AVSValue(); + } + } + + + + // Algorithms + + using Tools::AviSynth::Frame; + + constexpr size_t MaxSigValue = (size_t)((Frame::ColorCount - 1) * (Frame::ColorCount - 1) * 3) * 100; + + VideoCorrelation::Signature::value_type VideoCorrelation::ComputeForFrame(const Frame& frame) { + uint64_t result = 0; + for (int j = 0; j < frame.height(); ++j) { + const Frame::Pixel *row = frame.read_row(j); + for (int k = 0; k < frame.width(); ++k) { + const Frame::Pixel &px = row[k]; + result += (uint32_t)px.r*px.r + (uint32_t)px.g*px.g + (uint32_t)px.b*px.b; + } + } + result = result * 100 / (frame.width() * frame.height()); + return static_cast(result); + } + + VideoCorrelation::Signature VideoCorrelation::Preprocess(IScriptEnvironment* env, PClip clip) { + clip = Tools::AviSynth::ConvertToRGB32(env, clip); + int len = clip->GetVideoInfo().num_frames; + Signature result; + result.reserve(len); + for (int i = 0; i < len; ++i) + result.push_back(ComputeForFrame(Frame(env, clip, i))); + return result; + } + + int VideoCorrelation::GetShift(IScriptEnvironment* env, PClip overlay, const Signature& input_sig) { + if ((int)input_sig.size() <= overlay->GetVideoInfo().num_frames) + throw std::exception("Overlay must me shorter than input"); + Signature overlay_sig = Preprocess(env, overlay); + Correlation c(overlay_sig, MaxSigValue); + for (SigValue v : input_sig) + if (c.Next(v)) + break; + return c.GetMatch(); + } + + + + // Overlay implementation + + constexpr int OverlaySigShiftFrames = 1200; + constexpr int OverlaySigLength = 1000; + + VideoCorrelation::VideoCorrelation(IScriptEnvironment* env, PClip input, PClip overlay, + int shift, const Signature& input_sig, Signature::value_type threshold) + : + GenericVideoFilter(Tools::AviSynth::ConvertToRGB32(env, input)), + m_overlay(Tools::AviSynth::ConvertToRGB32(env, overlay)), + m_shift(shift) + { + m_overlay_vi = m_overlay->GetVideoInfo(); + + if (vi.width != m_overlay_vi.width || vi.height != m_overlay_vi.height || vi.pixel_type != m_overlay_vi.pixel_type) + env->ThrowError("Overlay and input properties must be equal"); + + if (vi.num_frames <= m_overlay_vi.num_frames) + env->ThrowError("Overlay must be shorter than input"); + + int overlay_sig_len = std::min(m_overlay_vi.num_frames, OverlaySigLength); + m_overlay_sig_shift = std::min(m_overlay_vi.num_frames - overlay_sig_len, OverlaySigShiftFrames); + if (m_overlay_sig_shift < OverlaySigShiftFrames) + fprintf(stderr, "WARNING: overlay is too short. Consider increasing its length to improve matching quality\n"); + + if (m_shift < 0) { + for (int i = 0; i < m_overlay_sig_shift; ++i) + m_overlay->GetFrame(i, env); + Signature overlay_sig; + overlay_sig.reserve(overlay_sig_len); + for (int i = m_overlay_sig_shift; i < m_overlay_sig_shift + overlay_sig_len; ++i) + overlay_sig.push_back(ComputeForFrame(Frame(env, m_overlay, i))); + m_correlation = std::make_unique>(overlay_sig, MaxSigValue, threshold); + if (!input_sig.empty()) { + for (SigValue v : input_sig) { + m_correlation->Next(v); + } + if (m_correlation->IsMatchFound()) { + m_shift = m_correlation->GetMatch() - m_overlay_sig_shift; + } + else { + m_shift = vi.num_frames; + fprintf(stderr, "WARNING: no match found with specified threshold\n"); + } + } + for (int i = 0; i < m_overlay_sig_shift; ++i) + m_overlay->GetFrame(i, env); + } + } + + PVideoFrame VideoCorrelation::GetFrame(int n, IScriptEnvironment* env) { + if (m_shift < 0) { + if (m_correlation->Next(ComputeForFrame(Frame(env, child, n)))) + m_shift = m_correlation->GetMatch() - m_overlay_sig_shift; + } + if (0 <= m_shift && m_shift < vi.num_frames) { + if (m_shift <= n && n < m_shift + m_overlay_vi.num_frames) + return m_overlay->GetFrame(n - m_shift, env); + } + return child->GetFrame(n, env); + } + + void VideoCorrelation::GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) { + if (0 <= m_shift && m_shift < vi.num_frames) { + auto audio_start = vi.AudioSamplesFromFrames(m_shift); + auto audio_end = vi.AudioSamplesFromFrames(m_shift + m_overlay_vi.num_frames); + if (audio_start <= start && start < audio_end - count) { + m_overlay->GetAudio(buf, start - audio_start, count, env); + return; + } + } + child->GetAudio(buf, start, count, env); + } + + bool VideoCorrelation::GetParity(int n) { + if (0 <= m_shift && m_shift < vi.num_frames) { + if (m_shift <= n && n < m_shift + m_overlay_vi.num_frames) + return m_overlay->GetParity(n - m_shift); + } + return child->GetParity(n); + } + +} diff --git a/AvsTools/Filter.VideoCorrelation.hpp b/AvsTools/Filter.VideoCorrelation.hpp new file mode 100644 index 0000000..9fe723e --- /dev/null +++ b/AvsTools/Filter.VideoCorrelation.hpp @@ -0,0 +1,141 @@ +// +// Match 2 videos by frames +// Copyright 2016 Vyacheslav Napadovsky. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +#pragma once +#include +#include +#include + +#include "Tools.AviSynth.Frame.hpp" + +namespace Filter { + + template + class Correlation { + public: + Correlation(const std::vector& pattern, T maxvalue, T threshold = 0) : + m_threshold(threshold), + m_maxvalue(maxvalue), + m_inited(false) + { + if (pattern.empty()) + throw std::runtime_error("Pattern could not be empty"); + m_pattern_size = pattern.size(); + InitHistogram(m_pattern_histogram, pattern); + m_init.reserve(m_pattern_size); + } + + bool Next(T nextval) { + if (!m_inited) { + m_init.push_back(nextval); + if (m_init.size() == m_pattern_size) { + InitHistogram(m_init_histogram, m_init); + int64_t error = 0; + for (size_t i = 0; i <= m_maxvalue; ++i) + error += sqr((int64_t)m_init_histogram[i] - m_pattern_histogram[i]); + m_best_error = m_last_error = error; + m_best_pos = m_last_pos = 0; + m_inited = true; + return m_best_error <= m_threshold; + } + return false; + } + //if (m_best_error <= 0) + // return true; + size_t realpos = m_last_pos % m_pattern_size; + T prev_val = m_init[realpos]; + m_init[realpos] = nextval; + m_last_error -= sqr((int64_t)m_init_histogram[prev_val] - m_pattern_histogram[prev_val]) + + sqr((int64_t)m_init_histogram[nextval] - m_pattern_histogram[nextval]); + --m_init_histogram[prev_val], ++m_init_histogram[nextval]; + m_last_error += sqr((int64_t)m_init_histogram[prev_val] - m_pattern_histogram[prev_val]) + + sqr((int64_t)m_init_histogram[nextval] - m_pattern_histogram[nextval]); + if (m_last_error < m_best_error) { + m_best_error = m_last_error; + m_best_pos = m_last_pos; + } + ++m_last_pos; + return m_best_error <= m_threshold; + } + + unsigned int GetMatch() const { + return m_best_pos; + } + + T GetThreshold() const { + return m_threshold; + } + + bool IsMatchFound() const { + return m_best_error <= m_threshold; + } + + private: + void InitHistogram(std::vector& hist, const std::vector& values) { + hist.clear(); + hist.resize(m_maxvalue+1, 0); + for (T v : values) + ++hist[v]; + } + + T m_threshold, m_maxvalue; + int64_t m_best_error, m_last_error; + size_t m_pattern_size, m_best_pos, m_last_pos; + bool m_inited; + std::vector m_init; + std::vector m_pattern_histogram, m_init_histogram; + }; + + class VideoCorrelation : public GenericVideoFilter { + public: + using SigValue = uint32_t; + using Signature = std::vector; + static Signature::value_type ComputeForFrame(const Tools::AviSynth::Frame& frame); + + static AvsParams CreateParams; + static AVSValue __cdecl Create(AVSValue args, void* user_data, IScriptEnvironment* env); + + static AvsParams PreprocessParams; + static AVSValue __cdecl Preprocess(AVSValue args, void* user_data, IScriptEnvironment* env); + static Signature Preprocess(IScriptEnvironment* env, PClip clip); + + static AvsParams GetShiftParams; + static AVSValue __cdecl GetShift(AVSValue args, void* user_data, IScriptEnvironment* env); + static int GetShift(IScriptEnvironment* env, PClip overlay, const Signature& input_sig); + + VideoCorrelation(IScriptEnvironment* env, PClip input, PClip overlay, + int shift = -1, const Signature& input_sig = Signature(), SigValue threshold = 0); + + PVideoFrame WINAPI GetFrame(int n, IScriptEnvironment* env) override; + void WINAPI GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) override; + bool WINAPI GetParity(int n) override; + + private: + PClip m_overlay; + int m_shift; + VideoInfo m_overlay_vi; + int m_overlay_sig_shift; + std::unique_ptr> m_correlation; + }; + +} diff --git a/AvsTools/Tools.AviSynth.Frame.hpp b/AvsTools/Tools.AviSynth.Frame.hpp index 2589151..139d054 100644 --- a/AvsTools/Tools.AviSynth.Frame.hpp +++ b/AvsTools/Tools.AviSynth.Frame.hpp @@ -42,8 +42,8 @@ namespace Tools { BYTE g; BYTE r; BYTE a; - Pixel() { } - Pixel(BYTE r, BYTE g, BYTE b, BYTE a = 0xFF) : r(r), g(g), b(b), a(a) { } + Pixel() = default; + Pixel(BYTE r, BYTE g, BYTE b, BYTE a = 0xFF) noexcept: r(r), g(g), b(b), a(a) { } Pixel(int r, int g, int b, int a = 0xFF) : r(ColorLimits(r)), @@ -129,13 +129,14 @@ namespace Tools { friend class Iterator; friend class ConstIterator; - Frame() : + Frame() noexcept: m_env(nullptr), m_width(0), m_height(0), m_pitch(0), m_read_ptr(nullptr), - m_write_ptr(nullptr) + m_write_ptr(nullptr), + m_is_bff(true) { // empty } @@ -143,7 +144,8 @@ namespace Tools { Frame(IScriptEnvironment* env, const VideoInfo& vi) : m_env(env), m_vi(vi), - m_frame(env->NewVideoFrame(vi)) + m_frame(env->NewVideoFrame(vi)), + m_is_bff(vi.IsBFF()) // create frame with default BFF parity { CheckRGB32(); m_width = m_frame->GetRowSize() / sizeof(Pixel); @@ -151,8 +153,12 @@ namespace Tools { m_pitch = m_frame->GetPitch(); m_read_ptr = m_frame->GetReadPtr(); m_write_ptr = m_frame->GetWritePtr(); + + if (vi.IsBFF() && vi.IsTFF()) + fprintf(stderr, "WARNING: VideoInfo supports both field parities!\n"); } + __declspec(deprecated("This constructor may create frame instance with wrong IsBFF field")) Frame(IScriptEnvironment* env, const VideoInfo& vi, const PVideoFrame frame) : m_env(env), m_vi(vi), @@ -164,12 +170,21 @@ namespace Tools { m_pitch = frame->GetPitch(); m_read_ptr = frame->GetReadPtr(); m_write_ptr = nullptr; + + m_is_bff = vi.IsBFF(); } +#pragma warning(push) +#pragma warning(disable : 4996) Frame(IScriptEnvironment* env, const PClip& clip, int framenum) : Frame(env, clip->GetVideoInfo(), clip->GetFrame(framenum, env)) { + m_is_bff = !clip->GetParity(framenum); } +#pragma warning(pop) + + bool IsBFF() const { return m_is_bff; } + void SetBFF(bool value) { m_is_bff = value; } operator PVideoFrame() const { return m_frame; } int width() const { return m_width; } @@ -219,6 +234,11 @@ namespace Tools { bool operator!=(const Frame& other) const; void EnableWriting(); + static void CheckRGB32(const VideoInfo& vi, IScriptEnvironment* env) { + if (vi.IsFieldBased() || !vi.IsRGB32()) + env->ThrowError("Only RGB32 input supported"); + } + private: VideoInfo m_vi; IScriptEnvironment *m_env; @@ -228,13 +248,13 @@ namespace Tools { int m_pitch; const BYTE *m_read_ptr; BYTE *m_write_ptr; + bool m_is_bff; int get_real_y(int offset_y) const { - return (!m_vi.IsBFF()) ? offset_y : (m_vi.height - offset_y - 1); + return (!m_is_bff) ? offset_y : (m_vi.height - offset_y - 1); } void CheckRGB32() const { - if (!m_vi.IsRGB32()) - m_env->ThrowError("Only RGB32 input supported"); + CheckRGB32(m_vi, m_env); } }; diff --git a/AvsTools/Tools.AviSynth.hpp b/AvsTools/Tools.AviSynth.hpp index 56236c8..f8cbaa8 100644 --- a/AvsTools/Tools.AviSynth.hpp +++ b/AvsTools/Tools.AviSynth.hpp @@ -59,7 +59,7 @@ namespace Tools { // stub for AviSynth source video filters that doesn't support audio playback class SourceFilterStub : public IClip { public: - SourceFilterStub() { + SourceFilterStub() noexcept { memset(&vi, 0, sizeof(vi)); } bool WINAPI GetParity(int n) override { diff --git a/AvsTools/Tools.Motion.cpp b/AvsTools/Tools.Motion.cpp index 2d81139..4a4e6d0 100644 --- a/AvsTools/Tools.Motion.cpp +++ b/AvsTools/Tools.Motion.cpp @@ -81,7 +81,7 @@ namespace Tools { || (pixel_y + dy < 0) || (pixel_y + dy + BLOCK_COMPARE_SIZE >= from.height()) || (pixel_x < 0) || (pixel_x + BLOCK_COMPARE_SIZE >= from.width()) || (pixel_y < 0) || (pixel_y + BLOCK_COMPARE_SIZE >= from.height()) - ) + ) { return; } @@ -137,17 +137,18 @@ namespace Tools { } static void init_patterns() { + // diamond pattern patterns.push_back({ Point(0, 1), Point(1, 0), Point(0, -1), Point(-1, 0) - }); + }); patterns.push_back({ Point(0, 5), Point(5, 0), Point(0, -5), Point(-5, 0), Point(2, 2), Point(2, -2), Point(-2, -2), Point(-2, 2) - }); + }); patterns.push_back({ Point(0, 2), Point(2, 0), Point(0, -2), Point(-2, 0), Point(1, 1), Point(1, -1), Point(-1, -1), Point(-1, 1) - }); + }); } public: @@ -180,12 +181,21 @@ namespace Tools { }; std::vector Estimation::patterns; + + inline void PrepareFrames(const Frame& left, Frame& right); + + void Estimate(const Frame& from, const Frame& to, Map& result, const Array2D& mask) { + Frame new_to(to); + PrepareFrames(from, new_to); + Map m(result); + Estimation(from, new_to, m, result, mask); + } + } } -#ifdef MOTION_PROPRIETARY - #include -#else + +#ifdef MOTION_SIMPLE namespace Tools { namespace Motion { @@ -214,17 +224,76 @@ namespace Tools { } } -#endif +#else + +#include "Filter.HistogramMatching.hpp" namespace Tools { namespace Motion { - void Estimate(const Frame& from, const Frame& to, Map& result, const Array2D& mask) { - Frame new_to(to); - PrepareFrames(from, new_to); - Map m(result); - Estimation(from, new_to, m, result, mask); + /* In case of publishing papers based on this method, please refer to: + + A. Voronov, D. Vatolin, D. Sumin, V. Napadovsky, A. Borisov + "Towards Automatic Stereo-video Quality Assessment and Detection of Color and Sharpness Mismatch" // + International Conference on 3D Imaging (IC3D) — 2012. — pp. 1-6. DOI:10.1109/IC3D.2012.6615121 + (section 4.2. Color-Independent Stereo Matching) + + or + + Napadovsky, V. 2014. Locally adaptive algorithm for detection + and elimination of color inconsistencies between views of stereoscopic video, + M.S. Thesis, Moscow State University + + */ + + inline void PrepareFrames(const Frame& left, Frame& right) { + HistogramMatching(right, left); } + inline int dot(const IntPixel& x, const IntPixel& y) { + return x.r * y.r + x.g * y.g + x.b * y.b; + } + + inline void Estimation::NewCandidate(int pixel_x, int pixel_y, Vector& v) { + IntPixel sum; + for (int y = 0; y < BLOCK_COMPARE_SIZE; ++y) { + for (int x = 0; x < BLOCK_COMPARE_SIZE; ++x) { + // to - from + sum += to.read(pixel_x + x, pixel_y + y); + sum -= from.read(pixel_x + v.dx + x, pixel_y + v.dy + y); + } + } + sum /= BLOCK_COMPARE_SIZE * BLOCK_COMPARE_SIZE; + int diffsum = 0; + int sad = 0; + for (int y = 0; y < BLOCK_COMPARE_SIZE; ++y) { + for (int x = 0; x < BLOCK_COMPARE_SIZE; ++x) { + // from - to + IntPixel pixel = from.read(pixel_x + v.dx + x, pixel_y + v.dy + y); + pixel -= to.read(pixel_x + x, pixel_y + y); + sad += dot(pixel, pixel); + + pixel += sum; + diffsum += dot(pixel, pixel); + } + } + v.confidence = diffsum; + v.confidence += sad; + //v->confidence += sqr(abs(sum.r) + abs(sum.g) + abs(sum.b)); + //v->confidence += sqr(dy); + } + + double GetRealConfidence(const Vector& mv) { + double c = (double)mv.confidence / ( + Frame::ColorCount // color - color + * 3 // 3 component in dot + * BLOCK_COMPARE_SIZE // loop over block sz * sz + * BLOCK_COMPARE_SIZE + * 2 // 2 components + ); + return std::max(0.0, std::min(1.0 - c, 1.0)); + } } } + +#endif diff --git a/AvsTools/Tools.Motion.hpp b/AvsTools/Tools.Motion.hpp index a3bab45..4dc3237 100644 --- a/AvsTools/Tools.Motion.hpp +++ b/AvsTools/Tools.Motion.hpp @@ -61,7 +61,7 @@ namespace Tools { template class Array2D : public std::vector { public: - Array2D() { } + Array2D() = default; Array2D(size_t width, size_t height) : m_width(width), m_height(height) { resize(width, height); } @@ -97,7 +97,7 @@ namespace Tools { struct Vector { int confidence; int dx, dy; - Vector() : confidence(INT_MAX), dx(0), dy(0) {} + Vector() noexcept: confidence(INT_MAX), dx(0), dy(0) {} void AssignIfBetter(const Vector& other) { if (other.confidence < confidence) diff --git a/AvsTools/stdafx.h b/AvsTools/stdafx.h index 856f867..657a58d 100644 --- a/AvsTools/stdafx.h +++ b/AvsTools/stdafx.h @@ -14,3 +14,8 @@ #include #include #include + +template +inline T sqr(T v) { + return v * v; +} diff --git a/CMakeLists.txt b/CMakeLists.txt index ad85582..4002e3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.0) -project(ssifSource VERSION "5.0.4") +project(ssifSource VERSION "5.0.5") include(utils.cmake) set(PRODUCT_COMPANY "Vyacheslav Napadovsky") set(CPACK_PACKAGE_NAME ${PROJECT_NAME}) diff --git a/README.md b/README.md index a183168..1366327 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,27 @@ -This project contains a collection of AviSynth plugins. +[![If you want to ease my life, consider a donation](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/slavanap) + +Collection of AviSynth plugins +------------------------------ * ssifSource - for reading BD3D, -* Sub3D - for rendering 3D subtitles and estimating they depth (partially closed-source), +* Sub3D - for rendering 3D subtitles and estimating their depth, * LoadHelper - for easy loading AviSynth plugins that requires additional .dll files, -* FilmTester - for reading through .avs file frame by frame (without a need of encoding with x264, for example) +* FilmTester - for reading through .avs file frame by frame * and others. -**ssifSource** supports can open .ssif files or .mpls files for rendering. It uses other thirdparty software to make a pipeline for BD3D decoding without a need to write intermediate files on disk. The plugin uses Windows pipes to connect all executables in pipeline. This adds a little overhead, but allows neither change original apps sources nor use additional disk space. For more info, see [its own help file](https://github.com/slavanap/ssifSource/blob/master/ssifSource/ssifSource.avs) +**ssifSource** is able to render .ssif and .mpls files from BD3D. Rendering directly from ISO files is also supported. The plugin uses thirdparty software to build a pipeline for BD3D decoding without a need for intermediate files. The plugin uses Windows pipes to connect all executables. This adds a small overhead, but allows neither change original apps nor use additional storage. See [sample avs file](https://github.com/slavanap/ssifSource/blob/master/ssifSource/ssifSource.avs) for more info. -**Sub3D** supports .srt subtitles, .xml/png subtitles and other AviSynth plugins that can render subtitles over casual 2D video. It is able to extract subtitles from 2D video (if original is available), compute their depth (according 3D video) and render them over 3D video. Plugin works in RGB32 colorspace. For more info, see [sample .avs file](https://github.com/slavanap/ssifSource/blob/master/Sub3D/sub3d.avs) +**Sub3D** can execute any plugin that renders subtitles over 2D video and then convert them to 3D subtitles according depth analysis results. Estimated depth can be adjusted with Lua scripts. The plugin also supports .srt and .xml subtitles as an input. For other subtitle renderers there's a function for alpha channel extraction. The plugin works only in RGB32 colorspace. See [sample avs file](https://github.com/slavanap/ssifSource/blob/master/Sub3D/sub3d.avs) for more info. -**LoadHelper** simple plugin that adds its own directory to process PATH variable when the plugin is loaded. Thus every .dll within LoadHelper.dll directory will be loaded properly if it is required by some other plugins. Currently the plugin is used in [BD3D2MK3D](http://forum.doom9.org/showthread.php?t=170828) +**LoadHelper** when loaded, adds its own path to process PATH environment variable. This allows other .dll files that are in the same folder as LoadHelper.dll to be loaded properly (and without full path specification) if they are required by some other plugins. Currently the plugin is used in [BD3D2MK3D](http://forum.doom9.org/showthread.php?t=170828) **FilmTester** simple tool that tests .avs file for readability. -**Additinally** Sub3D.dll plugin contains some other interesing functions: +**Additinally** `Sub3D.dll` contains functions for: * *CropDetect* - detects a clipbox of video and generates .avsi file for easy cropping. -* *SequentialToSeekable* - converts any AviSynth plugin that outputs its result sequentially to a seekable plugin. Beware, if you seek backwards, the dependent plugin will be reloaded because seeking is emulated and slow. -* *HistogramMatching* - tries to make color histogram of input clip similar to reference clip frame by frame. +* *SequentialToSeekable* - converts any AviSynth plugin that outputs its result sequentially to a seekable plugin. Beware, if you seek backwards, the dependent plugin will be reloaded because seeking is emulated. +* *HistogramMatching* - tries to make color histogram of input clip similar to reference clip, frame by frame. +* *VideoCorrelation{,Preprocess,GetShift}* - merge video sequence with its subsequence by video frame data when sequences alignment isn't known. + +**AvsTools** static library contains more video processing alrogithms. diff --git a/Sub3D/CMakeLists.txt b/Sub3D/CMakeLists.txt index 8885a08..5129d8c 100644 --- a/Sub3D/CMakeLists.txt +++ b/Sub3D/CMakeLists.txt @@ -1,4 +1,4 @@ -project(Sub3D VERSION "1.0.2") +project(Sub3D VERSION "1.0.3") make_version_file(${PROJECT_VERSION} "3D subtitles renderer") include_directories( @@ -21,7 +21,6 @@ set(SOURCES Tools.Lua.cpp dllmain.cpp - sub3d.rc ) if (MINGW) @@ -29,6 +28,7 @@ if (MINGW) endif() set_precompiled_header(stdafx.h stdafx.cpp) +list(APPEND SOURCES sub3d.rc) add_library(${PROJECT_NAME} SHARED ${HEADERS} ${SOURCES}) target_link_libraries(${PROJECT_NAME} AvsTools Lua TinyXML2) diff --git a/Sub3D/Filter.SRTRenderer.hpp b/Sub3D/Filter.SRTRenderer.hpp index 7285064..34cfb64 100644 --- a/Sub3D/Filter.SRTRenderer.hpp +++ b/Sub3D/Filter.SRTRenderer.hpp @@ -32,8 +32,8 @@ namespace Filter { struct subtitle_desc_t { int length; int depth; - subtitle_desc_t() {} - subtitle_desc_t(int length, int depth) : length(length), depth(depth) { } + subtitle_desc_t() = default; + subtitle_desc_t(int length, int depth) noexcept: length(length), depth(depth) { } }; typedef std::map subtitles_map_t; diff --git a/Sub3D/Tools.Lua.cpp b/Sub3D/Tools.Lua.cpp index 91598e0..a865460 100644 --- a/Sub3D/Tools.Lua.cpp +++ b/Sub3D/Tools.Lua.cpp @@ -146,8 +146,10 @@ namespace Tools { int Script::luaCalcForFrameRaw(lua_State* L) { Script *self = m_luaStates[L]; - if (!self->m_depth_list) + if (!self->m_depth_list) { luaL_error(L, "Unexpected. You should call CalcForFrameRaw only in borders of CalcForFrame function!"); + return 0; // make VS static analyzer happy + } const depth_list_t &list = *self->m_depth_list; auto conf_threshold = luaL_optnumber(L, 1, 0); @@ -184,8 +186,10 @@ namespace Tools { int Script::luaGetFrameRawData(lua_State* L) { Script *self = m_luaStates[L]; - if (!self->m_depth_list) + if (!self->m_depth_list) { luaL_error(L, "Unexpected situation. You should call CalcForFrameRaw only in borders of CalcForFrame function!"); + return 0; // make VS static analyzer happy + } const depth_list_t &list = *self->m_depth_list; std::map counting; for (auto it = list.begin(); it != list.end(); ++it) { diff --git a/Sub3D/dllmain.cpp b/Sub3D/dllmain.cpp index ede5efa..2204bfd 100644 --- a/Sub3D/dllmain.cpp +++ b/Sub3D/dllmain.cpp @@ -45,6 +45,7 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv #include #include #include +#include #include "Filter.XMLRenderer.hpp" #include "Filter.SRTRenderer.hpp" #include "Filter.RestoreAlpha.hpp" @@ -73,39 +74,8 @@ const char* WINAPI AvisynthPluginInit2(IScriptEnvironment* env) { env->AddFunction("CropDetect", Filter::CropDetect::CreateParams, Filter::CropDetect::Create, nullptr); env->AddFunction("HistogramMatching", Filter::HistogramMatching::CreateParams, Filter::HistogramMatching::Create, nullptr); env->AddFunction("SequentialToSeekable", Filter::SequentialToSeekable::CreateParams, Filter::SequentialToSeekable::Create, nullptr); + env->AddFunction("VideoCorrelation", Filter::VideoCorrelation::CreateParams, Filter::VideoCorrelation::Create, nullptr); + env->AddFunction("VideoCorrelationPreprocess", Filter::VideoCorrelation::PreprocessParams, Filter::VideoCorrelation::Preprocess, nullptr); + env->AddFunction("VideoCorrelationGetShift", Filter::VideoCorrelation::GetShiftParams, Filter::VideoCorrelation::GetShift, nullptr); return nullptr; } - - - -#include - -enum { - IDD_DONATION = 1, - ID_SENDEMAIL = 1, - ID_CLOSE = 2, -}; - -INT_PTR CALLBACK DlgMsgHandler(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { - switch (message) { - case WM_CLOSE: - close: - EndDialog(hwnd, 0); - break; - case WM_COMMAND: - switch (wParam) { - case ID_SENDEMAIL: - ShellExecute(hwnd, TEXT("open"), TEXT("mailto:napadovskiy@gmail.com?subject=sub3d%20plugin"), TEXT(""), TEXT(""), SW_SHOWNORMAL); - break; - case ID_CLOSE: - goto close; - } - break; - } - return 0; -} - -extern "C" __declspec(dllexport) -void CALLBACK Donate(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) { - DialogBoxParam(Tools::WinApi::hInstance, (LPCTSTR)IDD_DONATION, hwnd, DlgMsgHandler, 0); -} diff --git a/Sub3D/sub3d.rc b/Sub3D/sub3d.rc index dbc608c..6ebb2a1 100644 --- a/Sub3D/sub3d.rc +++ b/Sub3D/sub3d.rc @@ -58,18 +58,6 @@ BEGIN END END -1 DIALOGEX FIXED IMPURE LOADONCALL DISCARDABLE 0, 0, 208, 99, 0 -EXSTYLE 0 -STYLE DS_FIXEDSYS | DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_SYSMENU | WS_CAPTION -CAPTION "Sub3d donation info" -FONT 8, "MS Shell Dlg", 0, 0, 0 -LANGUAGE LANG_NEUTRAL, 0 -BEGIN - CONTROL "Glad to see you interested in donation!\x0A\x0AUnfortunately, donation way is still under development, but if you want to contribute to the project or have other ideas about contribution or collaboration, please contact the developer via e-mail.\x0A\x0AVyacheslav", -1, "STATIC", SS_LEFT|WS_CHILD|WS_GROUP|WS_VISIBLE, 6, 5, 197, 90, 0x0, 0 - CONTROL "Send mail...",1,"BUTTON",BS_PUSHBUTTON|WS_CHILD|WS_TABSTOP|WS_VISIBLE, 86, 79, 55, 15, 0x0, 0 - CONTROL "Close",2,"BUTTON",BS_DEFPUSHBUTTON|WS_CHILD|WS_TABSTOP|WS_VISIBLE, 146, 79, 55, 15, 0x0, 0 -END - #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/_vs/AvsTools/AvsTools.vcxproj b/_vs/AvsTools/AvsTools.vcxproj index 0090acc..0f95821 100644 --- a/_vs/AvsTools/AvsTools.vcxproj +++ b/_vs/AvsTools/AvsTools.vcxproj @@ -27,6 +27,7 @@ + @@ -42,11 +43,9 @@ + - Create - Create - Create - Create + Create @@ -58,26 +57,26 @@ {D2EE1A9A-D7BC-4BEE-99F7-41313913860E} Win32Proj AvsTools - 8.1 + 10.0.17134.0 StaticLibrary true - v140 + v141 Unicode StaticLibrary false - v140 + v141 true Unicode StaticLibrary true - v140 + v141 Unicode diff --git a/_vs/AvsTools/AvsTools.vcxproj.filters b/_vs/AvsTools/AvsTools.vcxproj.filters index 033b51b..c6094e2 100644 --- a/_vs/AvsTools/AvsTools.vcxproj.filters +++ b/_vs/AvsTools/AvsTools.vcxproj.filters @@ -39,6 +39,9 @@ Header Files + + Header Files + Header Files @@ -80,6 +83,9 @@ Source Files + + Source Files + Source Files diff --git a/_vs/BaseClasses/BaseClasses.vcxproj b/_vs/BaseClasses/BaseClasses.vcxproj index 3006c86..39f5982 100644 --- a/_vs/BaseClasses/BaseClasses.vcxproj +++ b/_vs/BaseClasses/BaseClasses.vcxproj @@ -94,26 +94,26 @@ {E7205816-9D04-4A04-A8E8-53F0D19EA25C} Win32Proj BaseClasses - 8.1 + 10.0.17134.0 StaticLibrary true - v140 + v141 Unicode StaticLibrary false - v140 + v141 true Unicode StaticLibrary true - v140 + v141 Unicode diff --git a/_vs/CoreAVCWrapper/CoreAVCWrapper.vcxproj b/_vs/CoreAVCWrapper/CoreAVCWrapper.vcxproj index b1fef6f..3ec2511 100644 --- a/_vs/CoreAVCWrapper/CoreAVCWrapper.vcxproj +++ b/_vs/CoreAVCWrapper/CoreAVCWrapper.vcxproj @@ -14,19 +14,19 @@ {6E323EDA-E1F1-4C21-9FBE-38401AE66D0D} Win32Proj CoreAVCWrapper - 8.1 + 10.0.17134.0 DynamicLibrary true - v140 + v141 Unicode DynamicLibrary false - v140 + v141 true Unicode diff --git a/_vs/FilmTester/FilmTester.vcxproj b/_vs/FilmTester/FilmTester.vcxproj index fed0227..5a956b1 100644 --- a/_vs/FilmTester/FilmTester.vcxproj +++ b/_vs/FilmTester/FilmTester.vcxproj @@ -14,19 +14,19 @@ {B245A205-A91B-45D3-89F3-030F74E39689} Win32Proj FilmTester - 8.1 + 10.0.17134.0 Application true - v140 + v141 Unicode Application false - v140 + v141 true Unicode diff --git a/_vs/LoadHelper/LoadHelper.vcxproj b/_vs/LoadHelper/LoadHelper.vcxproj index 8182d53..c25d64b 100644 --- a/_vs/LoadHelper/LoadHelper.vcxproj +++ b/_vs/LoadHelper/LoadHelper.vcxproj @@ -14,19 +14,19 @@ {C251383F-3629-40A3-9511-462577C0E56E} Win32Proj LoadHelper - 8.1 + 10.0.17134.0 DynamicLibrary true - v140 + v141 Unicode DynamicLibrary false - v140 + v141 true Unicode diff --git a/_vs/Lua/Lua.vcxproj b/_vs/Lua/Lua.vcxproj index 76efd65..e3d6832 100644 --- a/_vs/Lua/Lua.vcxproj +++ b/_vs/Lua/Lua.vcxproj @@ -97,26 +97,26 @@ {13478569-05CC-4AB3-BC90-C20C210CD3F3} Win32Proj Lua - 8.1 + 10.0.17134.0 StaticLibrary true - v140 + v141 Unicode StaticLibrary false - v140 + v141 true Unicode StaticLibrary true - v140 + v141 Unicode diff --git a/_vs/MpegSplitter/MpegSplitter.vcxproj b/_vs/MpegSplitter/MpegSplitter.vcxproj index 8da4cb7..b86ac84 100644 --- a/_vs/MpegSplitter/MpegSplitter.vcxproj +++ b/_vs/MpegSplitter/MpegSplitter.vcxproj @@ -22,26 +22,26 @@ {7531C193-4FC8-4D55-A41B-97853693999F} Win32Proj MpegSplitter - 8.1 + 10.0.17134.0 DynamicLibrary true - v140 + v141 Unicode DynamicLibrary false - v140 + v141 true Unicode DynamicLibrary true - v140 + v141 Unicode diff --git a/_vs/Sub3D/Sub3D.vcxproj b/_vs/Sub3D/Sub3D.vcxproj index 7104a30..cb76381 100644 --- a/_vs/Sub3D/Sub3D.vcxproj +++ b/_vs/Sub3D/Sub3D.vcxproj @@ -59,19 +59,19 @@ {42B83F03-D4C4-4286-8CE1-5A55ABCF5788} Win32Proj Sub3D - 8.1 + 10.0.17134.0 DynamicLibrary true - v140 + v141 Unicode DynamicLibrary false - v140 + v141 true Unicode diff --git a/_vs/ldecod/ldecod.vcxproj b/_vs/ldecod/ldecod.vcxproj index 42b341e..582ba3b 100644 --- a/_vs/ldecod/ldecod.vcxproj +++ b/_vs/ldecod/ldecod.vcxproj @@ -22,26 +22,26 @@ {DD4CF16A-7420-49D4-BFEF-E63F3DC165FF} Win32Proj ldecod - 8.1 + 10.0.17134.0 Application true - v140 + v141 Unicode Application false - v140 + v141 true Unicode Application true - v140 + v141 Unicode diff --git a/_vs/libudfread/libudfread.vcxproj b/_vs/libudfread/libudfread.vcxproj index 54a7c9f..35f3e9f 100644 --- a/_vs/libudfread/libudfread.vcxproj +++ b/_vs/libudfread/libudfread.vcxproj @@ -38,26 +38,26 @@ {E7E63CD5-9EF3-48F0-9708-2EA926A0DEC3} Win32Proj libudfread - 8.1 + 10.0.17134.0 DynamicLibrary true - v140 + v141 Unicode DynamicLibrary false - v140 + v141 true Unicode DynamicLibrary true - v140 + v141 Unicode diff --git a/_vs/libufileread/libufileread.vcxproj b/_vs/libufileread/libufileread.vcxproj index 3c360fa..3259170 100644 --- a/_vs/libufileread/libufileread.vcxproj +++ b/_vs/libufileread/libufileread.vcxproj @@ -28,26 +28,26 @@ {E2AD9B82-518D-4C3A-853D-6AE58E816F7D} Win32Proj libufileread - 8.1 + 10.0.17134.0 StaticLibrary true - v140 + v141 Unicode StaticLibrary false - v140 + v141 true Unicode StaticLibrary true - v140 + v141 Unicode diff --git a/_vs/ssifSource/ssifSource.vcxproj b/_vs/ssifSource/ssifSource.vcxproj index ca2b53d..a1f0209 100644 --- a/_vs/ssifSource/ssifSource.vcxproj +++ b/_vs/ssifSource/ssifSource.vcxproj @@ -14,19 +14,19 @@ {82F991A6-5C7D-4146-BF57-1C2DC1A9897C} Win32Proj ssifSource - 8.1 + 10.0.17134.0 DynamicLibrary true - v140 + v141 Unicode DynamicLibrary false - v140 + v141 true Unicode diff --git a/_vs/tinyxml2/tinyxml2.vcxproj b/_vs/tinyxml2/tinyxml2.vcxproj index 1aab706..b639a73 100644 --- a/_vs/tinyxml2/tinyxml2.vcxproj +++ b/_vs/tinyxml2/tinyxml2.vcxproj @@ -22,26 +22,26 @@ {E8B806E6-4E67-4EA7-90E8-312A85D891C0} Win32Proj tinyxml2 - 8.1 + 10.0.17134.0 StaticLibrary true - v140 + v141 Unicode StaticLibrary false - v140 + v141 true Unicode StaticLibrary true - v140 + v141 Unicode diff --git a/configure.bat b/configure.bat index bb40c05..72e597f 100644 --- a/configure.bat +++ b/configure.bat @@ -1,5 +1,5 @@ @mkdir __build @cd __build -cmake -G "Visual Studio 14" ../ +cmake -G "Visual Studio 15" ../ @cd .. @pause diff --git a/ssifSource/CMakeLists.txt b/ssifSource/CMakeLists.txt index 8bf14c7..f2ec58b 100644 --- a/ssifSource/CMakeLists.txt +++ b/ssifSource/CMakeLists.txt @@ -23,12 +23,12 @@ set(SOURCES Filter.mplsSource2.cpp Filter.ssifSource.cpp Tools.DirectShow.cpp - ssifSource.rc ) set_precompiled_header(stdafx.h stdafx.cpp) list(APPEND SOURCES + ssifSource.rc mplsReader.c ) diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index ee658f3..2885d11 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -38,3 +38,9 @@ target_link_libraries(${PROJECT_NAME} Shlwapi.lib) set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER "Third Party") add_subdirectory(MPC) + +if (MSVC) + set_property( TARGET BaseClasses APPEND_STRING PROPERTY COMPILE_FLAGS " -wd4996 " ) + set_property( TARGET MpegSplitter APPEND_STRING PROPERTY COMPILE_FLAGS " -wd4244 " ) + set_property( TARGET libudfread APPEND_STRING PROPERTY COMPILE_FLAGS " -wd4996 -wd4244 -wd4267 " ) +endif() diff --git a/utils.cmake b/utils.cmake index 93dc301..3f312dd 100644 --- a/utils.cmake +++ b/utils.cmake @@ -6,7 +6,7 @@ macro(set_precompiled_header PrecompiledHeader PrecompiledSource) PROPERTIES COMPILE_FLAGS "/Yc\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\"" OBJECT_OUTPUTS "${PrecompiledBinary}") set_source_files_properties(${SOURCES} - PROPERTIES COMPILE_FLAGS "/Yu\"${PrecompiledHeader}\" /FI\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\"" + PROPERTIES COMPILE_FLAGS "/Yu\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\"" OBJECT_DEPENDS "${PrecompiledBinary}") endif() list(APPEND HEADERS ${PrecompiledHeader})