diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..db2f574 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.16) + +project(libhqdn3d LANGUAGES CXX) + +find_package (Git) + +if (GIT_FOUND) + execute_process (COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0 + OUTPUT_VARIABLE ver + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +else () + message (STATUS "GIT not found") +endif () + +add_library(hqdn3d SHARED src/hqdn3d.cpp) + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE) +endif() + +message(STATUS "Build type - ${CMAKE_BUILD_TYPE}") + +target_include_directories(hqdn3d PRIVATE /usr/local/include/avisynth) + +set_target_properties(hqdn3d PROPERTIES OUTPUT_NAME "hqdn3d.${ver}") + +target_compile_features(hqdn3d PRIVATE cxx_std_11) + +include(GNUInstallDirs) + +INSTALL(TARGETS hqdn3d LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/avisynth") + +# uninstall target +if(NOT TARGET uninstall) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) +endif() diff --git a/Changelog b/Changelog index 6dac068..5df37e6 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,11 @@ +hqdn3d 1.0.0 (2021-4-12): +Add parameters y, u, v. +Add support for all YUV formats. +Add support for 10..16-bit formats. +Reduce intermediate precision - faster code, negligible change to output. +Add Linux building option. +Add version. + hqdn3d 0.11 (2005-1-25): Fix a floating-point exception. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f9fb264 --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# Description + +High Quality DeNoise 3D is an AviSynth port of the MPlayer filter of the same name. It performs a 3-way low-pass filter, which can completely remove high-frequency noise while minimizing blending artifacts. + +High bit depth support ported from the ffmpeg plugin. + +# Usage + +``` +hqdn3d (clip input, float "ls", float "cs", float "lt", float "ct", int "restart", int "y", int "u", int "v") +``` + +## Parameters: + +- input\ + A clip to process.\ + It must be in YUV 8..16-bit planar format. + +- ls\ + Spatial luma strength.\ + Increasing the value will improve the smoothing but may overblur.\ + Anything above about 10 is probably not a good idea.\ + Must be between 0.0..255.0.\ + Default: 4.0. + +- cs\ + Spatial chroma strength.\ + Increasing the value will improve the smoothing but may overblur.\ + Anything above about 10 is probably not a good idea.\ + Must be between 0.0..255.0.\ + Default: 3.0 \* ls / 4.0. + +- lt\ + Luma temporal strength.\ + Increasing the values will improve the smoothing but may cause ghosting.\ + Anything above about 13 is probably not a good idea.\ + Must be between 0.0..255.0.\ + Default: 6.0 \* ls / 4.0. + +- ct\ + Chroma temporal strength.\ + Increasing the values will improve the smoothing but may cause ghosting.\ + Anything above about 13 is probably not a good idea.\ + Must be between 0.0..255.0.\ + Default: lt \* cs / ls. + +- restart\ + Whenever a frame is requested out of order, restart filtering this many frames before.\ + While seeking still slightly affects the content of the frames returned, this should reduce the disturbance to an unnoticeable level.\ + Must be non-negative value. + Default: max(2, 1 + max(lt, ct)). + +- y, u, v\ + Planes to process.\ + 1: Return garbage.\ + 2: Copy plane.\ + 3: Process plane.\ + Default: y = u = v = 3. + +# Building + +## Windows + +Use solution files. + +## Linux + +### Requirements + +- Git +- C++11 compiler +- CMake >= 3.16 + +``` +git clone https://github.com/Asd-g/hqdn3d && \ +cd hqdn3d && \ +mkdir build && \ +cd build && \ +cmake .. && \ +make -j$(nproc) && \ +sudo make install +``` diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in new file mode 100644 index 0000000..c2d34d4 --- /dev/null +++ b/cmake_uninstall.cmake.in @@ -0,0 +1,21 @@ +if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") +endif() + +file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + exec_program( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif() + else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif() +endforeach() diff --git a/hqdn3d.txt b/hqdn3d_old.txt similarity index 100% rename from hqdn3d.txt rename to hqdn3d_old.txt diff --git a/msvc/hqdn3d.sln b/msvc/hqdn3d.sln new file mode 100644 index 0000000..071eb93 --- /dev/null +++ b/msvc/hqdn3d.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30503.244 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hqdn3d", "hqdn3d.vcxproj", "{9A722FBD-65EF-4C8B-BDED-4F632253AE46}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9A722FBD-65EF-4C8B-BDED-4F632253AE46}.Debug|x64.ActiveCfg = Debug|x64 + {9A722FBD-65EF-4C8B-BDED-4F632253AE46}.Debug|x64.Build.0 = Debug|x64 + {9A722FBD-65EF-4C8B-BDED-4F632253AE46}.Debug|x86.ActiveCfg = Debug|Win32 + {9A722FBD-65EF-4C8B-BDED-4F632253AE46}.Debug|x86.Build.0 = Debug|Win32 + {9A722FBD-65EF-4C8B-BDED-4F632253AE46}.Release|x64.ActiveCfg = Release|x64 + {9A722FBD-65EF-4C8B-BDED-4F632253AE46}.Release|x64.Build.0 = Release|x64 + {9A722FBD-65EF-4C8B-BDED-4F632253AE46}.Release|x86.ActiveCfg = Release|Win32 + {9A722FBD-65EF-4C8B-BDED-4F632253AE46}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {37DAF99A-8D07-4FB9-9A83-6EA2D4EA1423} + EndGlobalSection +EndGlobal diff --git a/msvc/hqdn3d.vcxproj b/msvc/hqdn3d.vcxproj new file mode 100644 index 0000000..ceb0611 --- /dev/null +++ b/msvc/hqdn3d.vcxproj @@ -0,0 +1,147 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {9A722FBD-65EF-4C8B-BDED-4F632253AE46} + Win32Proj + 10.0 + + + + Application + true + v142 + + + DynamicLibrary + false + v142 + + + DynamicLibrary + true + v142 + + + DynamicLibrary + false + v142 + + + + + + + + + + + + + + + + + + + + + true + + + false + ..\..\AviSynthPlus\avs_core\include;$(IncludePath) + + + ..\..\AviSynthPlus\avs_core\include;$(IncludePath) + + + ..\..\AviSynthPlus\avs_core\include;$(IncludePath) + false + + + + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level3 + ProgramDatabase + Disabled + + + MachineX86 + true + Windows + + + + + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + true + AnySuitable + true + Speed + true + true + Precise + + + MachineX86 + true + Windows + true + true + + + + + stdcpp17 + + + + + true + AnySuitable + true + Speed + true + Precise + true + + + true + + + true + + + + + + + + + + + + \ No newline at end of file diff --git a/msvc/hqdn3d.vcxproj.filters b/msvc/hqdn3d.vcxproj.filters new file mode 100644 index 0000000..b5ce961 --- /dev/null +++ b/msvc/hqdn3d.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/avisynth.h b/src/avisynth.h deleted file mode 100644 index 073fc01..0000000 --- a/src/avisynth.h +++ /dev/null @@ -1,676 +0,0 @@ -// Avisynth v2.5 alpha. Copyright 2002 Ben Rudiak-Gould et al. -// http://www.avisynth.org - -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit -// http://www.gnu.org/copyleft/gpl.html . -// -// Linking Avisynth statically or dynamically with other modules is making a -// combined work based on Avisynth. Thus, the terms and conditions of the GNU -// General Public License cover the whole combination. -// -// As a special exception, the copyright holders of Avisynth give you -// permission to link Avisynth with independent modules that communicate with -// Avisynth solely through the interfaces defined in avisynth.h, regardless of the license -// terms of these independent modules, and to copy and distribute the -// resulting combined work under terms of your choice, provided that -// every copy of the combined work is accompanied by a complete copy of -// the source code of Avisynth (the version of Avisynth used to produce the -// combined work), being distributed under the terms of the GNU General -// Public License plus this exception. An independent module is a module -// which is not derived from or based on Avisynth, such as 3rd-party filters, -// import and export plugins, or graphical user interfaces. - - - - -#ifndef __AVISYNTH_H__ -#define __AVISYNTH_H__ - -enum { AVISYNTH_INTERFACE_VERSION = 2 }; - - -/* Define all types necessary for interfacing with avisynth.dll - Moved from internal.h */ - -// Win32 API macros, notably the types BYTE, DWORD, ULONG, etc. -#include - -// COM interface macros -#include - -// Raster types used by VirtualDub & Avisynth -#define in64 (__int64)(unsigned short) -typedef unsigned long Pixel; // this will break on 64-bit machines! -typedef unsigned long Pixel32; -typedef unsigned char Pixel8; -typedef long PixCoord; -typedef long PixDim; -typedef long PixOffset; - - -/* Compiler-specific crap */ - -// Tell MSVC to stop precompiling here -#ifdef _MSC_VER - #pragma hdrstop -#endif - -// Set up debugging macros for MS compilers; for others, step down to the -// standard interface -#ifdef _MSC_VER - #include -#else - #define _RPT0(a,b) ((void)0) - #define _RPT1(a,b,c) ((void)0) - #define _RPT2(a,b,c,d) ((void)0) - #define _RPT3(a,b,c,d,e) ((void)0) - #define _RPT4(a,b,c,d,e,f) ((void)0) - - #define _ASSERTE(x) assert(x) - #include -#endif - - - -// I had problems with Premiere wanting 1-byte alignment for its structures, -// so I now set the Avisynth struct alignment explicitly here. -#pragma pack(push,8) - -#define FRAME_ALIGN 16 -// Default frame alignment is 16 bytes, to help P4, when using SSE2 - -// The VideoInfo struct holds global information about a clip (i.e. -// information that does not depend on the frame number). The GetVideoInfo -// method in IClip returns this struct. - -// Audio Sample information -typedef float SFLOAT; - -enum {SAMPLE_INT8 = 1<<0, - SAMPLE_INT16 = 1<<1, - SAMPLE_INT24 = 1<<2, // Int24 is a very stupid thing to code, but it's supported by some hardware. - SAMPLE_INT32 = 1<<3, - SAMPLE_FLOAT = 1<<4}; - -enum { - PLANAR_Y=1<<0, - PLANAR_U=1<<1, - PLANAR_V=1<<2, - PLANAR_ALIGNED=1<<3, - PLANAR_Y_ALIGNED=PLANAR_Y|PLANAR_ALIGNED, - PLANAR_U_ALIGNED=PLANAR_U|PLANAR_ALIGNED, - PLANAR_V_ALIGNED=PLANAR_V|PLANAR_ALIGNED, - }; - -struct VideoInfo { - int width, height; // width=0 means no video - unsigned fps_numerator, fps_denominator; - int num_frames; - // This is more extensible than previous versions. More properties can be added seemlessly. - - // Colorspace properties. - enum { - CS_BGR = 1<<28, - CS_YUV = 1<<29, - CS_INTERLEAVED = 1<<30, - CS_PLANAR = 1<<31 - }; - - // Specific colorformats - enum { CS_UNKNOWN = 0, - CS_BGR24 = 1<<0 | CS_BGR | CS_INTERLEAVED, - CS_BGR32 = 1<<1 | CS_BGR | CS_INTERLEAVED, - CS_YUY2 = 1<<2 | CS_YUV | CS_INTERLEAVED, - CS_YV12 = 1<<3 | CS_YUV | CS_PLANAR, // y-v-u, planar - CS_I420 = 1<<4 | CS_YUV | CS_PLANAR, // y-u-v, planar - CS_IYUV = 1<<4 | CS_YUV | CS_PLANAR // same as above - }; - int pixel_type; // changed to int as of 2.5 - - - int audio_samples_per_second; // 0 means no audio - int sample_type; // as of 2.5 - __int64 num_audio_samples; // changed as of 2.5 - int nchannels; // as of 2.5 - - // Imagetype properties - - int image_type; - - enum { - IT_BFF = 1<<0, - IT_TFF = 1<<1, - IT_FIELDBASED = 1<<2 - }; - - // useful functions of the above - bool HasVideo() const { return (width!=0); } - bool HasAudio() const { return (audio_samples_per_second!=0); } - bool IsRGB() const { return !!(pixel_type&CS_BGR); } - bool IsRGB24() const { return (pixel_type&CS_BGR24)==CS_BGR24; } // Clear out additional properties - bool IsRGB32() const { return (pixel_type & CS_BGR32) == CS_BGR32 ; } - bool IsYUV() const { return !!(pixel_type&CS_YUV ); } - bool IsYUY2() const { return (pixel_type & CS_YUY2) == CS_YUY2; } - bool IsYV12() const { return ((pixel_type & CS_YV12) == CS_YV12)||((pixel_type & CS_I420) == CS_I420); } - bool IsColorSpace(int c_space) const { return ((pixel_type & c_space) == c_space); } - bool Is(int property) const { return ((pixel_type & property)==property ); } - bool IsPlanar() const { return !!(pixel_type & CS_PLANAR); } - bool IsFieldBased() const { return !!(image_type & IT_FIELDBASED); } - bool IsParityKnown() const { return ((image_type & IT_FIELDBASED)&&(image_type & (IT_BFF||IT_TFF))); } - bool IsBFF() const { return !!(pixel_type & IT_BFF); } - bool IsTFF() const { return !!(pixel_type & IT_TFF); } - - bool IsVPlaneFirst() const {return ((pixel_type & CS_YV12) == CS_YV12); } // Don't use this - int BytesFromPixels(int pixels) const { return pixels * (BitsPerPixel()>>3); } // Will not work on planar images, but will return only luma planes - int RowSize() const { return BytesFromPixels(width); } // Also only returns first plane on planar images - int BMPSize() const { if (IsPlanar()) {int p = height * ((RowSize()+3) & ~3); p+=p>>1; return p; } return height * ((RowSize()+3) & ~3); } - __int64 AudioSamplesFromFrames(__int64 frames) const { return (__int64(frames) * audio_samples_per_second * fps_denominator / fps_numerator); } - int FramesFromAudioSamples(__int64 samples) const { return (int)(samples * (__int64)fps_numerator / (__int64)fps_denominator / (__int64)audio_samples_per_second); } - __int64 AudioSamplesFromBytes(__int64 bytes) const { return bytes / BytesPerAudioSample(); } - __int64 BytesFromAudioSamples(__int64 samples) const { return samples * BytesPerAudioSample(); } - int AudioChannels() const { return nchannels; } - int SampleType() const{ return sample_type;} - int SamplesPerSecond() const { return audio_samples_per_second; } - int BytesPerAudioSample() const { return nchannels*BytesPerChannelSample();} - void SetFieldBased(bool isfieldbased) { if (isfieldbased) image_type|=IT_FIELDBASED; else image_type&=~IT_FIELDBASED; } - void Set(int property) { image_type|=property; } - void Clear(int property) { image_type&=~property; } - - int BitsPerPixel() const { - switch (pixel_type) { - case CS_BGR24: - return 24; - case CS_BGR32: - return 32; - case CS_YUY2: - return 16; - case CS_YV12: - case CS_I420: - return 12; - default: - return 0; - } - } - int BytesPerChannelSample() const { - switch (sample_type) { - case SAMPLE_INT8: - return sizeof(signed char); - case SAMPLE_INT16: - return sizeof(signed short); - case SAMPLE_INT24: - return 3; - case SAMPLE_INT32: - return sizeof(signed int); - case SAMPLE_FLOAT: - return sizeof(SFLOAT); - default: - _ASSERTE("Sample type not recognized!"); - return 0; - } - } - - // useful mutator - void SetFPS(unsigned numerator, unsigned denominator) { - unsigned x=numerator, y=denominator; - while (y) { // find gcd - unsigned t = x%y; x = y; y = t; - } - fps_numerator = numerator/x; - fps_denominator = denominator/x; - } -}; - -enum { - FILTER_TYPE=1, - FILTER_INPUT_COLORSPACE=2, - FILTER_OUTPUT_TYPE=9, - FILTER_NAME=4, - FILTER_AUTHOR=5, - FILTER_VERSION=6, - FILTER_ARGS=7, - FILTER_ARGS_INFO=8, - FILTER_ARGS_DESCRIPTION=10, - FILTER_DESCRIPTION=11, -}; -enum { //SUBTYPES - FILTER_TYPE_AUDIO=1, - FILTER_TYPE_VIDEO=2, - FILTER_OUTPUT_TYPE_SAME=3, - FILTER_OUTPUT_TYPE_DIFFERENT=4, -}; - - - -// VideoFrameBuffer holds information about a memory block which is used -// for video data. For efficiency, instances of this class are not deleted -// when the refcount reaches zero; instead they're stored in a linked list -// to be reused. The instances are deleted when the corresponding AVS -// file is closed. - -class VideoFrameBuffer { - BYTE* const data; - const int data_size; - // sequence_number is incremented every time the buffer is changed, so - // that stale views can tell they're no longer valid. - long sequence_number; - - friend class VideoFrame; - friend class Cache; - long refcount; - -public: - VideoFrameBuffer(int size); - VideoFrameBuffer(); - ~VideoFrameBuffer(); - - const BYTE* GetReadPtr() const { return data; } - BYTE* GetWritePtr() { ++sequence_number; return data; } - int GetDataSize() { return data_size; } - int GetSequenceNumber() { return sequence_number; } - int GetRefcount() { return refcount; } -}; - - -class IClip; -class PClip; -class PVideoFrame; -class IScriptEnvironment; -class AVSValue; - - -// VideoFrame holds a "window" into a VideoFrameBuffer. Operator new -// is overloaded to recycle class instances. - -class VideoFrame { - int refcount; - VideoFrameBuffer* const vfb; - const int offset, pitch, row_size, height, offsetU, offsetV, pitchUV; // U&V offsets are from top of picture. - - friend class PVideoFrame; - void AddRef() { ++refcount; } - void Release() { if (refcount==1) --vfb->refcount; --refcount; } - - friend class ScriptEnvironment; - friend class Cache; - - VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height); - VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height, int _offsetU, int _offsetV, int _pitchUV); - - void* operator new(unsigned size); -// TESTME: OFFSET U/V may be switched to what could be expected from AVI standard! -public: - int GetPitch() const { return pitch; } - int GetPitch(int plane) const { switch (plane) {case PLANAR_U: case PLANAR_V: return pitchUV;} return pitch; } - int GetRowSize() const { return row_size; } - int GetRowSize(int plane) const { - switch (plane) { - case PLANAR_U: case PLANAR_V: if (pitchUV) return row_size>>1; else return 0; - case PLANAR_U_ALIGNED: case PLANAR_V_ALIGNED: - if (pitchUV) { - int r = ((row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)) )>>1; // Aligned rowsize - if (r<=pitchUV) - return r; - return row_size>>1; - } else return 0; - case PLANAR_Y_ALIGNED: - int r = (row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)); // Aligned rowsize - if (r<=pitch) - return r; - return row_size; - } - return row_size; } - int GetHeight() const { return height; } - int GetHeight(int plane) const { switch (plane) {case PLANAR_U: case PLANAR_V: if (pitchUV) return height>>1; return 0;} return height; } - - // generally you shouldn't use these three - VideoFrameBuffer* GetFrameBuffer() const { return vfb; } - int GetOffset() const { return offset; } - int GetOffset(int plane) const { switch (plane) {case PLANAR_U: return offsetU;case PLANAR_V: return offsetV;default: return offset;}; } - - // in plugins use env->SubFrame() - VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height) const; - VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int pitchUV) const; - - - const BYTE* GetReadPtr() const { return vfb->GetReadPtr() + offset; } - const BYTE* GetReadPtr(int plane) const { return vfb->GetReadPtr() + GetOffset(plane); } - - bool IsWritable() const { return (refcount == 1 && vfb->refcount == 1); } - - BYTE* GetWritePtr() const { - _ASSERTE(IsWritable()); - return IsWritable() ? (vfb->GetWritePtr() + offset) : 0; - } - - BYTE* GetWritePtr(int plane) const { - if (plane==PLANAR_Y) - return IsWritable() ? vfb->GetWritePtr() + GetOffset(plane) : 0; - return vfb->data + GetOffset(plane); - } - - ~VideoFrame() { --vfb->refcount; } -}; - -enum { - CACHE_NOTHING=0, - CACHE_RANGE=1 }; - -// Base class for all filters. -class IClip { - friend class PClip; - friend class AVSValue; - int refcnt; - void AddRef() { ++refcnt; } - void Release() { if (!--refcnt) delete this; } -public: - IClip() : refcnt(0) {} - - virtual int __stdcall GetVersion() { return AVISYNTH_INTERFACE_VERSION; } - - virtual PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) = 0; - virtual bool __stdcall GetParity(int n) = 0; // return field parity if field_based, else parity of first field in frame - virtual void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) = 0; // start and count are in samples - virtual void __stdcall SetCacheHints(int cachehints,int frame_range) = 0 ; // We do not pass cache requests upwards, only to the next filter. - virtual const VideoInfo& __stdcall GetVideoInfo() = 0; - virtual __stdcall ~IClip() {} -}; - - -// smart pointer to IClip -class PClip { - - IClip* p; - - IClip* GetPointerWithAddRef() const { if (p) p->AddRef(); return p; } - friend class AVSValue; - friend class VideoFrame; - - void Init(IClip* x) { - if (x) x->AddRef(); - p=x; - } - void Set(IClip* x) { - if (x) x->AddRef(); - if (p) p->Release(); - p=x; - } - -public: - PClip() { p = 0; } - PClip(const PClip& x) { Init(x.p); } - PClip(IClip* x) { Init(x); } - void operator=(IClip* x) { Set(x); } - void operator=(const PClip& x) { Set(x.p); } - - IClip* operator->() const { return p; } - - // useful in conditional expressions - operator void*() const { return p; } - bool operator!() const { return !p; } - - ~PClip() { if (p) p->Release(); } -}; - - -// smart pointer to VideoFrame -class PVideoFrame { - - VideoFrame* p; - - void Init(VideoFrame* x) { - if (x) x->AddRef(); - p=x; - } - void Set(VideoFrame* x) { - if (x) x->AddRef(); - if (p) p->Release(); - p=x; - } - -public: - PVideoFrame() { p = 0; } - PVideoFrame(const PVideoFrame& x) { Init(x.p); } - PVideoFrame(VideoFrame* x) { Init(x); } - void operator=(VideoFrame* x) { Set(x); } - void operator=(const PVideoFrame& x) { Set(x.p); } - - VideoFrame* operator->() const { return p; } - - // for conditional expressions - operator void*() const { return p; } - bool operator!() const { return !p; } - - ~PVideoFrame() { if (p) p->Release(); } -}; - - -class AVSValue { -public: - - AVSValue() { type = 'v'; } - AVSValue(IClip* c) { type = 'c'; clip = c; if (c) c->AddRef(); } - AVSValue(const PClip& c) { type = 'c'; clip = c.GetPointerWithAddRef(); } - AVSValue(bool b) { type = 'b'; boolean = b; } - AVSValue(int i) { type = 'i'; integer = i; } -// AVSValue(__int64 l) { type = 'l'; longlong = l; } - AVSValue(float f) { type = 'f'; floating_pt = f; } - AVSValue(double f) { type = 'f'; floating_pt = float(f); } - AVSValue(const char* s) { type = 's'; string = s; } - AVSValue(const AVSValue* a, int size) { type = 'a'; array = a; array_size = size; } - AVSValue(const AVSValue& v) { Assign(&v, true); } - - ~AVSValue() { if (IsClip() && clip) clip->Release(); } - AVSValue& operator=(const AVSValue& v) { Assign(&v, false); return *this; } - - // Note that we transparently allow 'int' to be treated as 'float'. - // There are no int<->bool conversions, though. - - bool Defined() const { return type != 'v'; } - bool IsClip() const { return type == 'c'; } - bool IsBool() const { return type == 'b'; } - bool IsInt() const { return type == 'i'; } -// bool IsLong() const { return (type == 'l'|| type == 'i'); } - bool IsFloat() const { return type == 'f' || type == 'i'; } - bool IsString() const { return type == 's'; } - bool IsArray() const { return type == 'a'; } - - PClip AsClip() const { _ASSERTE(IsClip()); return IsClip()?clip:0; } - bool AsBool() const { _ASSERTE(IsBool()); return boolean; } - int AsInt() const { _ASSERTE(IsInt()); return integer; } -// int AsLong() const { _ASSERTE(IsLong()); return longlong; } - const char* AsString() const { _ASSERTE(IsString()); return IsString()?string:0; } - double AsFloat() const { _ASSERTE(IsFloat()); return IsInt()?integer:floating_pt; } - - bool AsBool(bool def) const { _ASSERTE(IsBool()||!Defined()); return IsBool() ? boolean : def; } - int AsInt(int def) const { _ASSERTE(IsInt()||!Defined()); return IsInt() ? integer : def; } - double AsFloat(double def) const { _ASSERTE(IsFloat()||!Defined()); return IsInt() ? integer : type=='f' ? floating_pt : def; } - const char* AsString(const char* def) const { _ASSERTE(IsString()||!Defined()); return IsString() ? string : def; } - - int ArraySize() const { _ASSERTE(IsArray()); return IsArray()?array_size:1; } - const AVSValue& operator[](int index) const { - _ASSERTE(IsArray() && index>=0 && index=0 && indexIsClip() && src->clip) - src->clip->AddRef(); - if (!init && IsClip() && clip) - clip->Release(); - // make sure this copies the whole struct! - ((__int32*)this)[0] = ((__int32*)src)[0]; - ((__int32*)this)[1] = ((__int32*)src)[1]; - } -}; - - -// instantiable null filter -class GenericVideoFilter : public IClip { -protected: - PClip child; - VideoInfo vi; -public: - GenericVideoFilter(PClip _child) : child(_child) { vi = child->GetVideoInfo(); } - PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) { return child->GetFrame(n, env); } - void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) { child->GetAudio(buf, start, count, env); } - const VideoInfo& __stdcall GetVideoInfo() { return vi; } - bool __stdcall GetParity(int n) { return child->GetParity(n); } - void __stdcall SetCacheHints(int cachehints,int frame_range) { } ; // We do not pass cache requests upwards, only to the next filter. -}; - - -class AvisynthError /* exception */ { -public: - const char* const msg; - AvisynthError(const char* _msg) : msg(_msg) {} -}; - - -// For GetCPUFlags. These are backwards-compatible with those in VirtualDub. -enum { - /* slowest CPU to support extension */ - CPUF_FORCE = 0x01, // N/A - CPUF_FPU = 0x02, // 386/486DX - CPUF_MMX = 0x04, // P55C, K6, PII - CPUF_INTEGER_SSE = 0x08, // PIII, Athlon - CPUF_SSE = 0x10, // PIII, Athlon XP/MP - CPUF_SSE2 = 0x20, // PIV, Hammer - CPUF_3DNOW = 0x40, // K6-2 - CPUF_3DNOW_EXT = 0x80, // Athlon - CPUF_X86_64 = 0xA0, // Hammer (note: equiv. to 3DNow + SSE2, which only Hammer - // will have anyway) -}; -#define MAX_INT 0x7fffffff -#define MIN_INT 0x80000000 - - -class ConvertAudio : public GenericVideoFilter -/** - * Helper class to convert audio to any format - **/ -{ -public: - ConvertAudio(PClip _clip, int prefered_format); - void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env); - - static PClip Create(PClip clip, int sample_type, int prefered_type); - static AVSValue __cdecl Create_float(AVSValue args, void*, IScriptEnvironment*); - static AVSValue __cdecl Create_32bit(AVSValue args, void*, IScriptEnvironment*); - static AVSValue __cdecl Create_16bit(AVSValue args, void*, IScriptEnvironment*); - static AVSValue __cdecl Create_8bit(AVSValue args, void*, IScriptEnvironment*); - virtual ~ConvertAudio() - {if (tempbuffer_size) {delete[] tempbuffer;tempbuffer_size=0;}} -private: -void ConvertAudio::convertToFloat(char* inbuf, float* outbuf, char sample_type, int count); -void ConvertAudio::convertFromFloat(float* inbuf, void* outbuf, char sample_type, int count); - - __inline int Saturate_int8(float n); - __inline short Saturate_int16(float n); - __inline int Saturate_int24(float n); - __inline int Saturate_int32(float n); - - char src_format; - char dst_format; - int src_bps; - char *tempbuffer; - SFLOAT *floatbuffer; - int tempbuffer_size; -}; - -class AlignPlanar : public GenericVideoFilter { -public: - AlignPlanar(PClip _clip); - static PClip Create(PClip clip); - PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); -}; - -class FillBorder : public GenericVideoFilter { -public: - FillBorder(PClip _clip); - static PClip Create(PClip clip); - PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); -}; - -class IScriptEnvironment { -public: - virtual __stdcall ~IScriptEnvironment() {} - - virtual /*static*/ long __stdcall GetCPUFlags() = 0; - - virtual char* __stdcall SaveString(const char* s, int length = -1) = 0; - virtual char* __stdcall Sprintf(const char* fmt, ...) = 0; - // note: val is really a va_list; I hope everyone typedefs va_list to a pointer - virtual char* __stdcall VSprintf(const char* fmt, void* val) = 0; - - __declspec(noreturn) virtual void __stdcall ThrowError(const char* fmt, ...) = 0; - - class NotFound /*exception*/ {}; // thrown by Invoke and GetVar - - typedef AVSValue (__cdecl *ApplyFunc)(AVSValue args, void* user_data, IScriptEnvironment* env); - - virtual void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data) = 0; - virtual bool __stdcall FunctionExists(const char* name) = 0; - virtual AVSValue __stdcall Invoke(const char* name, const AVSValue args, const char** arg_names=0) = 0; - - virtual AVSValue __stdcall GetVar(const char* name) = 0; - virtual bool __stdcall SetVar(const char* name, const AVSValue& val) = 0; - virtual bool __stdcall SetGlobalVar(const char* name, const AVSValue& val) = 0; - - virtual void __stdcall PushContext(int level=0) = 0; - virtual void __stdcall PopContext() = 0; - - // align should be 4 or 8 - virtual PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, int align=FRAME_ALIGN) = 0; - - virtual bool __stdcall MakeWritable(PVideoFrame* pvf) = 0; - - virtual /*static*/ void __stdcall BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) = 0; - - typedef void (__cdecl *ShutdownFunc)(void* user_data, IScriptEnvironment* env); - virtual void __stdcall AtExit(ShutdownFunc function, void* user_data) = 0; - - virtual void __stdcall CheckVersion(int version = AVISYNTH_INTERFACE_VERSION) = 0; - - virtual PVideoFrame __stdcall Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) = 0; - - virtual int __stdcall SetMemoryMax(int mem) = 0; - - virtual int __stdcall SetWorkingDir(const char * newdir) = 0; - -}; - - -// avisynth.dll exports this; it's a way to use it as a library, without -// writing an AVS script or without going through AVIFile. -IScriptEnvironment* __stdcall CreateScriptEnvironment(int version = AVISYNTH_INTERFACE_VERSION); - - -#pragma pack(pop) - -#endif //__AVISYNTH_H__ diff --git a/src/hqdn3d.cpp b/src/hqdn3d.cpp index d33d73e..0541faa 100644 --- a/src/hqdn3d.cpp +++ b/src/hqdn3d.cpp @@ -1,6 +1,4 @@ /* - HQDN3D 0.11 for Avisynth - Copyright (C) 2003 Daniel Moreno Avisynth port (C) 2005 Loren Merritt @@ -19,239 +17,316 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "internal.h" +#include +#include +#include "avisynth.h" -static inline unsigned int LowPassMul(unsigned int PrevMul, unsigned int CurrMul, int* Coef){ -// int dMul= (PrevMul&0xFFFFFF)-(CurrMul&0xFFFFFF); - int dMul= PrevMul-CurrMul; - int d=((dMul+0x10007FF)/(65536/16)); - return CurrMul + Coef[d]; -} +typedef union { + uint16_t u16; + uint8_t u8[2]; +} av_alias; + +#define AV_RN16A(p) (((const av_alias*)(p))->u16) +#define AV_WN16A(p, v) (((av_alias*)(p))->u16 = (v)) + +#define LUT_BITS (depth==16 ? 8 : 4) +#define LOAD(x) (((depth == 8 ? src[x] : AV_RN16A(src + (x) * 2)) << (16 - depth)) + (((1 << (16 - depth)) - 1) >> 1)) +#define STORE(x,val) (depth == 8 ? dst[x] = (val) >> (16 - depth) : AV_WN16A(dst + (x) * 2, (val) >> (16 - depth))) + +class hqdn3d : public GenericVideoFilter +{ + int16_t* coefs[4]; + uint16_t* line[3]; + uint16_t* frame_prev[3]; + int planecount; + int process[3]; + bool has_at_least_v8; + int _restartlap; + int prev_frame = -99999; + PVideoFrame cache; + + PVideoFrame filterFrame(PVideoFrame& src, IScriptEnvironment* env); -static void deNoise(const unsigned char *Frame, // mpi->planes[x] - unsigned char *FrameDest, // dmpi->planes[x] - unsigned int *LineAnt, // vf->priv->Line (width bytes) - unsigned short *FrameAnt, - int W, int H, int sStride, int dStride, - int *Horizontal, int *Vertical, int *Temporal) +public: + hqdn3d(PClip _child, double LumSpac, double ChromSpac, double LumTmp, double ChromTmp, int restart, int y, int u, int v, IScriptEnvironment* env); + ~hqdn3d(); + PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); +}; + +static inline uint32_t lowpass(int prev, int cur, int16_t* coef, int depth) { - int X, Y; - int sLineOffs = 0, dLineOffs = 0; - unsigned int PixelAnt; - int PixelDst; - - /* First pixel has no left nor top neightbour. Only previous frame */ - LineAnt[0] = PixelAnt = Frame[0]<<16; - PixelDst = LowPassMul(FrameAnt[0]<<8, PixelAnt, Temporal); - FrameAnt[0] = ((PixelDst+0x1000007F)/256); - FrameDest[0]= ((PixelDst+0x10007FFF)/65536); - - /* Fist line has no top neightbour. Only left one for each pixel and - * last frame */ - for (X = 1; X < W; X++){ - LineAnt[X] = PixelAnt = LowPassMul(PixelAnt, Frame[X]<<16, Horizontal); - PixelDst = LowPassMul(FrameAnt[X]<<8, PixelAnt, Temporal); - FrameAnt[X] = ((PixelDst+0x1000007F)/256); - FrameDest[X]= ((PixelDst+0x10007FFF)/65536); - } - - for (Y = 1; Y < H; Y++){ - unsigned int PixelAnt; - unsigned short* LinePrev=&FrameAnt[Y*W]; - sLineOffs += sStride, dLineOffs += dStride; - /* First pixel on each line doesn't have previous pixel */ - PixelAnt = Frame[sLineOffs]<<16; - LineAnt[0] = LowPassMul(LineAnt[0], PixelAnt, Vertical); - PixelDst = LowPassMul(LinePrev[0]<<8, LineAnt[0], Temporal); - LinePrev[0] = ((PixelDst+0x1000007F)/256); - FrameDest[dLineOffs]= ((PixelDst+0x10007FFF)/65536); - - for (X = 1; X < W; X++){ - int PixelDst; - /* The rest are normal */ - PixelAnt = LowPassMul(PixelAnt, Frame[sLineOffs+X]<<16, Horizontal); - LineAnt[X] = LowPassMul(LineAnt[X], PixelAnt, Vertical); - PixelDst = LowPassMul(LinePrev[X]<<8, LineAnt[X], Temporal); - LinePrev[X] = ((PixelDst+0x1000007F)/256); - FrameDest[dLineOffs+X]= ((PixelDst+0x10007FFF)/65536); - } - } + const int d = (prev - cur) >> (8 - LUT_BITS); + return cur + coef[d]; } -#define ABS(A) ( (A) > 0 ? (A) : -(A) ) +static inline void denoise_temporal(const uint8_t* src, uint8_t* dst, uint16_t* frame_ant, int w, int h, int sstride, int dstride, int16_t* temporal, int depth) +{ + long x, y; + uint32_t tmp; + + temporal += 256 << LUT_BITS; + + for (y = 0; y < h; ++y) + { + for (x = 0; x < w; ++x) + { + frame_ant[x] = tmp = lowpass(frame_ant[x], LOAD(x), temporal, depth); + STORE(x, tmp); + } + + src += sstride; + dst += dstride; + frame_ant += w; + } +} -static void PrecalcCoefs(int *Ct, double Dist25) +static inline void denoise_spatial(const uint8_t* src, uint8_t* dst, uint16_t* line_ant, uint16_t* frame_ant, int w, int h, int sstride, int dstride, int16_t* spatial, int16_t* temporal, int depth) { - int i; - double Gamma, Simil, C; + long x, y; + uint32_t pixel_ant; + uint32_t tmp; + + spatial += 256 << LUT_BITS; + temporal += 256 << LUT_BITS; + + /* First line has no top neighbor. Only left one for each tmp and + * last frame */ + pixel_ant = LOAD(0); + for (x = 0; x < w; ++x) + { + line_ant[x] = tmp = pixel_ant = lowpass(pixel_ant, LOAD(x), spatial, depth); + frame_ant[x] = tmp = lowpass(frame_ant[x], tmp, temporal, depth); + STORE(x, tmp); + } + + w /= (depth == 8) ? 1 : 2; + + for (y = 1; y < h; ++y) + { + src += sstride; + dst += dstride; + frame_ant += w; + + pixel_ant = LOAD(0); + for (x = 0; x < w - 1; ++x) + { + line_ant[x] = tmp = lowpass(line_ant[x], pixel_ant, spatial, depth); + pixel_ant = lowpass(pixel_ant, LOAD(x + 1), spatial, depth); + frame_ant[x] = tmp = lowpass(frame_ant[x], tmp, temporal, depth); + STORE(x, tmp); + } + line_ant[x] = tmp = lowpass(line_ant[x], pixel_ant, spatial, depth); + frame_ant[x] = tmp = lowpass(frame_ant[x], tmp, temporal, depth); + STORE(x, tmp); + } +} - Gamma = log(0.25) / log(1.0 - Dist25/255.0 - 0.00001); +static inline void denoise(const uint8_t* src, uint8_t* dst, uint16_t* line_ant, uint16_t** frame_ant_ptr, int w, int h, int sstride, int dstride, int16_t* spatial, int16_t* temporal, int depth) +{ + if (spatial[0]) + denoise_spatial(src, dst, line_ant, *frame_ant_ptr, w, h, sstride, dstride, spatial, temporal, depth); + else + denoise_temporal(src, dst, *frame_ant_ptr, w, h, sstride, dstride, temporal, depth); +} - for (i = -255*16; i < 256*16; i++) - { - Simil = 1.0 - ABS(i) / (16*255.0); - C = pow(Simil, Gamma) * 65536.0 * (double)i / 16.0; - Ct[16*256+i] = (int)((C<0) ? (C-0.5) : (C+0.5)); - } +PVideoFrame hqdn3d::filterFrame(PVideoFrame& src, IScriptEnvironment* env) +{ + PVideoFrame dst = (has_at_least_v8) ? env->NewVideoFrameP(vi, &src) : env->NewVideoFrame(vi); + const int planes[3] = { PLANAR_Y, PLANAR_U, PLANAR_V }; + + for (int i = 0; i < planecount; ++i) + { + const int pitch = src->GetPitch(planes[i]); + const int dst_pitch = dst->GetPitch(planes[i]); + const int width = src->GetRowSize(planes[i]); + const int height = src->GetHeight(planes[i]); + const uint8_t* srcp = src->GetReadPtr(planes[i]); + uint8_t* dstp = dst->GetWritePtr(planes[i]); + + if (process[i] == 3) + denoise(srcp, dstp, line[i], &frame_prev[i], width, height, pitch, dst_pitch, coefs[(i) ? 2 : 0], coefs[(i) ? 3 : 1], vi.BitsPerComponent()); + else if (process[i] == 2) + env->BitBlt(dstp, dst_pitch, srcp, pitch, width, height); + } + + return dst; } +static void precalc_coefs(double dist25, int depth, int16_t* ct) +{ + int i; + double gamma, simil, C; -class hqdn3d : public GenericVideoFilter + gamma = log(0.25) / log(1.0 - std::min(dist25, 252.0) / 255.0 - 0.00001); + + for (i = -256 << LUT_BITS; i < 256 << LUT_BITS; ++i) + { + double f = ((i << (9 - LUT_BITS)) + (1 << (8 - LUT_BITS)) - 1) / 512.0; // midpoint of the bin + simil = std::max(0.0, 1.0 - fabs(f) / 255.0); + C = pow(simil, gamma) * 256.0 * f; + ct[(256 << LUT_BITS) + i] = lrint(C); + } + + ct[0] = !!dist25; +} + +hqdn3d::hqdn3d(PClip _child, double LumSpac, double ChromSpac, double LumTmp, double ChromTmp, int restart, int y, int u, int v, IScriptEnvironment* env) + : GenericVideoFilter(_child), _restartlap(restart) { -public: - hqdn3d(PClip _child, double LumSpac, double ChromSpac, double LumTmp, double ChromTmp, - int RestartLap, IScriptEnvironment* _env) : - GenericVideoFilter(_child), restart_lap(RestartLap), env(_env) - { - if(!vi.IsYV12()) - env->ThrowError("hqdn3d: requires YV12 source"); - - if(LumSpac < 0) - LumSpac = 4.0; - if(ChromSpac < 0) - ChromSpac = .75 * LumSpac; - if(LumTmp < 0) - LumTmp = 1.5 * LumSpac; - if(ChromTmp < 0) { - if(LumSpac == 0) - ChromTmp = ChromSpac * 1.5; - else - ChromTmp = LumTmp * ChromSpac / LumSpac; - } - LumSpac = __min(254.9, LumSpac); - ChromSpac = __min(254.9, ChromSpac); - LumTmp = __min(254.9, LumTmp); - ChromTmp = __min(254.9, ChromTmp); - - if(restart_lap < 0) - restart_lap = __max(2, (int)(1 + __max(LumTmp, ChromTmp))); - - PrecalcCoefs(Coefs[0], LumSpac); - PrecalcCoefs(Coefs[1], LumTmp); - PrecalcCoefs(Coefs[2], ChromSpac); - PrecalcCoefs(Coefs[3], ChromTmp); - - prev_frame = -99999; - w= vi.width; - h= vi.height; - cw = w>>1; - ch = h>>1; - - Line = new unsigned int[w]; - Frame[0] = new unsigned short[w*h]; - Frame[1] = new unsigned short[cw*ch]; - Frame[2] = new unsigned short[cw*ch]; - } - ~hqdn3d() - { - if(Line) delete [] Line; - if(Frame[0]) delete [] Frame[0]; - if(Frame[1]) delete [] Frame[1]; - if(Frame[2]) delete [] Frame[2]; - } - - PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* _env); - -private: - //// Options - int restart_lap; // how many frames back we look when seeking - - //// State - IScriptEnvironment* env; - // TODO: cache more than one frame, to speed up playing/seeking backwards - PVideoFrame cache; - - int Coefs[4][512*16]; - unsigned int *Line; - unsigned short *Frame[3]; - - int prev_frame; - int w, cw, h, ch; - - PVideoFrame filterFrame(PVideoFrame cur); -}; + const int depth = vi.BitsPerComponent(); + + if (vi.IsRGB() || depth == 32 || !vi.IsPlanar()) + env->ThrowError("hqdn3d: clip must be in YUV 8..16-bit planar format."); + if (LumSpac < 0.0 || LumSpac > 255.0) + env->ThrowError("hqdn3d: ls must be between 0.0..255.0."); + if (ChromSpac < 0.0 || ChromSpac > 255.0) + env->ThrowError("hqdn3d: cs must be between 0.0..255.0."); + if (LumTmp < 0.0 || LumTmp > 255.0) + env->ThrowError("hqdn3d: lt must be between 0.0..255.0."); + if (ChromTmp < 0.0 || ChromTmp > 255.0) + env->ThrowError("hqdn3d: ct must be between 0.0..255.0."); + if (_restartlap < 0) + env->ThrowError("hqdn3d: restart must be non-negative value."); + + const int process_planes[3] = { y, u, v }; + for (int i = 0; i < 3; ++i) + { + switch (process_planes[i]) + { + case 3: process[i] = 3; break; + case 2: process[i] = 2; break; + default: process[i] = 1; break; + } + } + + planecount = std::min(vi.NumComponents(), 3); + const int planes[3] = { PLANAR_Y, PLANAR_U, PLANAR_V }; + + for (int i = 0; i < planecount; ++i) + { + if (process[i] == 3) + { + line[i] = new uint16_t[((i) ? vi.width >> vi.GetPlaneWidthSubsampling(planes[i]) : vi.width) * vi.ComponentSize()]; + frame_prev[i] = new uint16_t[((i) ? (vi.width >> vi.GetPlaneWidthSubsampling(planes[i])) * (vi.height >> vi.GetPlaneHeightSubsampling(planes[i])) : vi.width * vi.height) * vi.ComponentSize()]; + } + else + { + line[i] = nullptr; + frame_prev[i] = nullptr; + } + } + + double strength[4] = { LumSpac = std::min(254.9, LumSpac), + LumTmp = std::min(254.9, LumTmp), + ChromSpac = std::min(254.9, ChromSpac), + ChromTmp = std::min(254.9, ChromTmp) }; + + for (int i = 0; i < 4; ++i) + { + coefs[i] = new int16_t[512 << LUT_BITS]; + precalc_coefs(strength[i], depth, coefs[i]); + } + + has_at_least_v8 = true; + try { env->CheckVersion(8); } + catch (const AvisynthError&) { has_at_least_v8 = false; }; +} -PVideoFrame hqdn3d::filterFrame(PVideoFrame cur) +hqdn3d::~hqdn3d() { - PVideoFrame newframe = env->NewVideoFrame(vi); - - deNoise(cur->GetReadPtr(PLANAR_Y), - newframe->GetWritePtr(PLANAR_Y), - Line, Frame[0], w, h, - cur->GetPitch(PLANAR_Y), - newframe->GetPitch(PLANAR_Y), - Coefs[0], Coefs[0], Coefs[1]); - deNoise(cur->GetReadPtr(PLANAR_U), - newframe->GetWritePtr(PLANAR_U), - Line, Frame[1], cw, ch, - cur->GetPitch(PLANAR_U), - newframe->GetPitch(PLANAR_U), - Coefs[2], Coefs[2], Coefs[3]); - deNoise(cur->GetReadPtr(PLANAR_V), - newframe->GetWritePtr(PLANAR_V), - Line, Frame[2], cw, ch, - cur->GetPitch(PLANAR_V), - newframe->GetPitch(PLANAR_V), - Coefs[2], Coefs[2], Coefs[3]); - - return newframe; + for (int i = 0; i < 4; ++i) + delete[] coefs[i]; + + for (int i = 0; i < planecount; ++i) + { + if (process[i] == 3) + { + delete[] frame_prev[i]; + delete[] line[i]; + } + } } -PVideoFrame __stdcall hqdn3d::GetFrame(int n, IScriptEnvironment* _env) +PVideoFrame __stdcall hqdn3d::GetFrame(int n, IScriptEnvironment* env) { - env = _env; - - if(n == prev_frame) - return cache; - // if we skip some frames, filter the gap anyway - else if(n > prev_frame+1 && n - prev_frame <= restart_lap+1 && prev_frame >= 0) { - for(int i=prev_frame+1; iGetFrame(i, env)); - } - // if processing out of sequence, filter several previous frames to minimize seeking problems - else if(n == 0 || n != prev_frame+1) { - int sn = __max(0, n - restart_lap); - PVideoFrame sf = child->GetFrame(sn, env); - int x, y, c; - int sStride = sf->GetPitch(PLANAR_Y); - const BYTE* srcp = sf->GetReadPtr(PLANAR_Y); - for(y = 0; y < h; y++) { - unsigned short* dst=&Frame[0][y*w]; - const BYTE* src=srcp+y*sStride; - for(x = 0; x < w; x++) dst[x]=src[x]<<8; - } - for(c=1; c<=2; c++) { - sStride = sf->GetPitch(PLANAR_U); - srcp = sf->GetReadPtr((c==1) ? PLANAR_U : PLANAR_V); - for(y = 0; y < ch; y++) { - unsigned short* dst=&Frame[c][y*cw]; - const BYTE* src=srcp+y*sStride; - for(x = 0; x < cw; x++) dst[x]=src[x]<<8; - } - } - for(int i=sn+1; iGetFrame(i, env)); - } - - prev_frame = n; - cache = filterFrame(child->GetFrame(n, env)); - return cache; + if (n == prev_frame) + return cache; + // if we skip some frames, filter the gap anyway + if (n > prev_frame + 1 && n - prev_frame <= _restartlap + 1 && prev_frame >= 0) + { + for (int i = prev_frame + 1; i < n; ++i) + { + PVideoFrame src = child->GetFrame(i, env); + filterFrame(src, env); + } + } + // if processing out of sequence, filter several previous frames to minimize seeking problems + else if (n == 0 || n != prev_frame + 1) + { + const int sn = std::max(0, n - _restartlap); + PVideoFrame sf = child->GetFrame(sn, env); + const int planes[3] = { PLANAR_Y, PLANAR_U, PLANAR_V }; + + for (int i = 0; i < planecount; ++i) + { + + const int sStride = sf->GetPitch(planes[i]); + const int w = sf->GetRowSize(planes[i]); + const int h = sf->GetHeight(planes[i]); + const uint8_t* srcp = sf->GetReadPtr(planes[i]); + + for (int y = 0; y < h; ++y) + { + const uint8_t* src = srcp + y * sStride; + uint16_t* dst = &frame_prev[0][y * w]; + + for (int x = 0; x < w; ++x) + dst[x] = src[x] << 8; + } + } + + for (int i = sn + 1; i < n; ++i) + { + PVideoFrame src = child->GetFrame(i, env); + filterFrame(src, env); + } + } + + prev_frame = n; + PVideoFrame src = child->GetFrame(n, env); + cache = filterFrame(src, env); + + return cache; } AVSValue __cdecl Create_hqdn3d(AVSValue args, void* user_data, IScriptEnvironment* env) { - return new hqdn3d(args[0].AsClip(), - args[1].AsFloat(-1), - args[2].AsFloat(-1), - args[3].AsFloat(-1), - args[4].AsFloat(-1), - args[5].AsInt(-1), - env); + const float ls = args[1].AsFloatf(4.0f); + const float cs = args[2].AsFloatf(3.0f * ls / 4.0f); + const float lt = args[3].AsFloatf(6.0f * ls / 4.0f); + const float ct = args[4].AsFloatf(lt * cs / ls); + + return new hqdn3d( + args[0].AsClip(), + ls, + cs, + lt, + ct, + args[5].AsInt(std::max(2LL, llrint(1.0 + std::max(lt, ct)))), + args[6].AsInt(3), + args[7].AsInt(3), + args[8].AsInt(3), + env); } -extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env) +const AVS_Linkage* AVS_linkage; + +extern "C" __declspec(dllexport) +const char* __stdcall AvisynthPluginInit3(IScriptEnvironment * env, const AVS_Linkage* const vectors) { - env->AddFunction("hqdn3d", "c[ls]f[cs]f[lt]f[ct]f[restart]i", Create_hqdn3d, 0); + AVS_linkage = vectors; + + env->AddFunction("hqdn3d", "c[ls]f[cs]f[lt]f[ct]f[restart]i[y]i[u]i[v]i", Create_hqdn3d, 0); return 0; } diff --git a/src/hqdn3d.dsp b/src/hqdn3d.dsp deleted file mode 100644 index 7d809d5..0000000 --- a/src/hqdn3d.dsp +++ /dev/null @@ -1,114 +0,0 @@ -# Microsoft Developer Studio Project File - Name="hqdn3d" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=hqdn3d - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "hqdn3d.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "hqdn3d.mak" CFG="hqdn3d - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "hqdn3d - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "hqdn3d - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "hqdn3d - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DUP_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DUP_EXPORTS" /YX /FD /c -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 - -!ELSEIF "$(CFG)" == "hqdn3d - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DUP_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DUP_EXPORTS" /YX /FD /GZ /c -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "hqdn3d - Win32 Release" -# Name "hqdn3d - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\hqdn3d.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\avisynth.h -# End Source File -# Begin Source File - -SOURCE=.\internal.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/src/hqdn3d.dsw b/src/hqdn3d.dsw deleted file mode 100644 index bf4583f..0000000 --- a/src/hqdn3d.dsw +++ /dev/null @@ -1,29 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "hqdn3d"=".\hqdn3d.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/src/hqdn3d.rc b/src/hqdn3d.rc new file mode 100644 index 0000000..ac428f8 --- /dev/null +++ b/src/hqdn3d.rc @@ -0,0 +1,30 @@ +#include +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 1,0,0,0 +PRODUCTVERSION 1,0,0,0 +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS 0x0L +FILEOS VOS__WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "Comments", "High Quality DeNoise 3D filter." + VALUE "FileDescription", "hqdn3d for AviSynth 2.6 / AviSynth+" + VALUE "FileVersion", "1.0.0" + VALUE "InternalName", "hqdn3d" + VALUE "OriginalFilename", "hqdn3d.dll" + VALUE "ProductName", "hqdn3d" + VALUE "ProductVersion", "1.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/src/internal.h b/src/internal.h deleted file mode 100644 index 995a311..0000000 --- a/src/internal.h +++ /dev/null @@ -1,131 +0,0 @@ -// Avisynth v1.0 beta. Copyright 2000 Ben Rudiak-Gould. -// http://www.math.berkeley.edu/~benrg/avisynth.html - -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit -// http://www.gnu.org/copyleft/gpl.html . - -#ifndef __Internal_H__ -#define __Internal_H__ - - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#include -#include -#include -#include - -#define in64 (__int64)(unsigned short) - -#define ATHLON // comment this out if using the Intel compiler, or you need Pentium/K6 support - -typedef unsigned long Pixel; -typedef unsigned long Pixel32; -typedef unsigned char Pixel8; -typedef long PixCoord; -typedef long PixDim; -typedef long PixOffset; - -#pragma hdrstop - -#ifndef _MSC_VER - #define _RPT0(a,b) ((void)0) - #define _RPT1(a,b,c) ((void)0) - #define _RPT2(a,b,c,d) ((void)0) - #define _RPT3(a,b,c,d,e) ((void)0) - #define _RPT4(a,b,c,d,e,f) ((void)0) - - #define _ASSERTE(x) assert(x) - #include -#else - #include -#endif - -#ifndef __max -#define __max(a,b) (((a) > (b)) ? (a) : (b)) -#define __min(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -#include "avisynth.h" - - -struct AVSFunction { - const char* name; - const char* param_types; - AVSValue (__cdecl *apply)(AVSValue args, void* user_data, IScriptEnvironment* env); - void* user_data; -}; - - -int RGB2YUV(int rgb); - -PClip Create_MessageClip(const char* message, int width, int height, - int pixel_type, bool shrink, int textcolor, int halocolor, int bgcolor, - IScriptEnvironment* env); - -PClip new_Splice(PClip _child1, PClip _child2, bool realign_sound, IScriptEnvironment* env); -PClip new_SeparateFields(PClip _child, IScriptEnvironment* env); -PClip new_AssumeFrameBased(PClip _child); - -void BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, - int src_pitch, int row_size, int height); - -long GetCPUFlags(); - - -class _PixelClip { - enum { buffer=320 }; - BYTE clip[256+buffer*2]; -public: - _PixelClip() { - memset(clip, 0, buffer); - for (int i=0; i<256; ++i) clip[i+buffer] = (unsigned char) i; - memset(clip+buffer+256, 255, buffer); - } - BYTE operator()(int i) { return clip[i+buffer]; } -}; - -extern _PixelClip PixelClip; - - -template -static __inline void Relink(ListNode* newprev, ListNode* me, ListNode* newnext) { - if (me == newprev || me == newnext) return; - me->next->prev = me->prev; - me->prev->next = me->next; - me->prev = newprev; - me->next = newnext; - me->prev->next = me->next->prev = me; -} - - - -/*** Inline helper methods ***/ - - -static __inline BYTE ScaledPixelClip(int i) { - return PixelClip((i+32768) >> 16); -} - - -static __inline bool IsClose(int a, int b, unsigned threshold) - { return (unsigned(a-b+threshold) <= threshold*2); } - - - - -#endif // __Internal_H__