├── .gitignore
├── meson.build
├── msvc_project
├── AdaptiveBinarize.vcxproj.filters
├── AdaptiveBinarize.sln
└── AdaptiveBinarize.vcxproj
├── LICENSE
├── README.md
├── .github
└── workflows
│ └── build.yaml
└── src
└── adaptivebinarize.cpp
/.gitignore:
--------------------------------------------------------------------------------
1 | msvc_project/.vs
2 | msvc_project/x64
3 | *.user
--------------------------------------------------------------------------------
/meson.build:
--------------------------------------------------------------------------------
1 | project('adaptivebinarize', 'cpp',
2 | default_options: ['buildtype=release', 'warning_level=2', 'b_lto=true', 'b_ndebug=if-release', 'cpp_std=c++17'],
3 | license: 'MIT',
4 | meson_version: '>=0.51.0',
5 | version: '14'
6 | )
7 |
8 | cxx = meson.get_compiler('cpp')
9 |
10 | gcc_syntax = cxx.get_argument_syntax() == 'gcc'
11 |
12 | if gcc_syntax
13 | vapoursynth_dep = dependency('vapoursynth', version: '>=55').partial_dependency(compile_args: true, includes: true)
14 | install_dir = vapoursynth_dep.get_variable(pkgconfig: 'libdir') / 'vapoursynth'
15 | else
16 | vapoursynth_dep = []
17 | install_dir = get_option('libdir') / 'vapoursynth'
18 | endif
19 |
20 | sources = [
21 | 'src/adaptivebinarize.cpp'
22 | ]
23 |
24 | libs = []
25 |
26 | shared_module('adaptivebinarize', sources,
27 | dependencies: vapoursynth_dep,
28 | link_with: libs,
29 | install: true,
30 | install_dir: install_dir,
31 | gnu_symbol_visibility: 'hidden'
32 | )
33 |
--------------------------------------------------------------------------------
/msvc_project/AdaptiveBinarize.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Julek
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## AdaptiveBinarize
2 |
3 | Adaptive Binarize for Vapoursynth, based on [OpenCV's Adaptive Thresholding](https://docs.opencv.org/5.x/d7/d4d/tutorial_py_thresholding.html).
4 |
5 | ### Usage
6 | ```python
7 | abrz.AdaptiveBinarize(vnode clip, vnode clip2[, int c=3])
8 | ```
9 | ### Parameters:
10 |
11 | - clip\
12 | A clip to process. It must be in YUV/GRAY 8-bit.
13 |
14 | - clip2\
15 | Blurred clip to calculate the thresholding.\
16 | Gauss has a cleaner result and Mean/BoxBlur retains more detail.
17 |
18 | - c\
19 | Controls the threshold, read the OpenCV doc for more details.\
20 | Default: 3.
21 |
22 | ### Example
23 | This code in Vapoursynth:
24 | ```python
25 | import vsutil
26 | import vapoursynth as vs
27 | core = vs.core
28 |
29 | src8 = ...
30 | luma = vsutil.get_y(src8)
31 | luma16 = vsutil.depth(luma, 16)
32 | blur16 = luma16.std.Convolution([1]*11, mode='hv')
33 | blur8 = vsutil.depth(blur16, 8)
34 | binarize = core.abrz.AdaptiveBinarize(luma, blur8, c=3)
35 | ```
36 |
37 | Has the same result as this in OpenCV:
38 | ```python
39 | binarize = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY_INV, 11, 3)
40 | ```
41 |
42 | More example: [flat_mask from jvsfunc](https://github.com/dnjulek/jvsfunc/blob/ee14f8e908781ff19c891a0fc2bd4b43ba94852a/jvsfunc/mask.py#L15-L41)
--------------------------------------------------------------------------------
/msvc_project/AdaptiveBinarize.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32526.322
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AdaptiveBinarize", "AdaptiveBinarize.vcxproj", "{68926A12-6394-4B75-80E4-F3D138161C7E}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Debug|x86 = Debug|x86
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {68926A12-6394-4B75-80E4-F3D138161C7E}.Debug|x64.ActiveCfg = Debug|x64
17 | {68926A12-6394-4B75-80E4-F3D138161C7E}.Debug|x64.Build.0 = Debug|x64
18 | {68926A12-6394-4B75-80E4-F3D138161C7E}.Debug|x86.ActiveCfg = Debug|Win32
19 | {68926A12-6394-4B75-80E4-F3D138161C7E}.Debug|x86.Build.0 = Debug|Win32
20 | {68926A12-6394-4B75-80E4-F3D138161C7E}.Release|x64.ActiveCfg = Release|x64
21 | {68926A12-6394-4B75-80E4-F3D138161C7E}.Release|x64.Build.0 = Release|x64
22 | {68926A12-6394-4B75-80E4-F3D138161C7E}.Release|x86.ActiveCfg = Release|Win32
23 | {68926A12-6394-4B75-80E4-F3D138161C7E}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {019222CE-6D80-45CD-8063-36619199A5C3}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/.github/workflows/build.yaml:
--------------------------------------------------------------------------------
1 | name: Build
2 | on:
3 | - push
4 | - release
5 | - pull_request
6 | - workflow_dispatch
7 |
8 | jobs:
9 | build:
10 | runs-on: windows-latest
11 | strategy:
12 | matrix:
13 | arch:
14 | - amd64
15 | - x86
16 | steps:
17 | - uses: actions/checkout@v2
18 | - name: setup MS dev commands
19 | uses: ilammy/msvc-dev-cmd@v1
20 | with:
21 | arch: ${{ matrix.arch }}
22 | - name: Setup Python
23 | uses: actions/setup-python@v1
24 | with:
25 | python-version: '3.x'
26 | - name: install meson and ninja
27 | run: pip install meson ninja
28 |
29 | - name: Plug in vs-api3
30 | shell: bash
31 | run: |
32 | git clone https://github.com/AmusementClub/vs-api3 --depth=1 --branch master api3
33 | cp api3/api3.cc src/
34 | sed -i -e "/adaptivebinarize.cpp'/s//&, 'src\\/api3.cc'/" meson.build
35 | cat meson.build
36 |
37 | - name: download VS headers and patch header location
38 | shell: bash
39 | run: |
40 | git clone https://github.com/AmusementClub/vapoursynth-classic --depth=1 --branch doodle2 vapoursynth
41 | cp vapoursynth/include/*.h src/
42 | #sed -i -e '/#include |""|' src/adaptivebinarize.cpp
43 | - name: Meson setup
44 | run: meson setup builddir/ -Db_vscrt=mt
45 | - name: Meson compile
46 | run: meson compile -C builddir/ -v
47 | - name: Upload artifact
48 | uses: actions/upload-artifact@v2
49 | with:
50 | name: release-${{matrix.arch}}
51 | path: |
52 | builddir/*.dll
53 |
--------------------------------------------------------------------------------
/src/adaptivebinarize.cpp:
--------------------------------------------------------------------------------
1 | #include "VapourSynth4.h"
2 | #include "VSHelper4.h"
3 |
4 | typedef struct AdaptiveBinarizeData {
5 | VSNode* node;
6 | VSNode* node2;
7 | const VSVideoInfo* vi;
8 | int c_param;
9 | int tab[768]{};
10 | } AdaptiveBinarizeData;
11 |
12 | static const VSFrame* VS_CC adaptiveBinarizeGetFrame(int n, int activationReason, void* instanceData, void** frameData, VSFrameContext* frameCtx, VSCore* core, const VSAPI* vsapi) {
13 | auto* d = reinterpret_cast(instanceData);
14 |
15 | if (activationReason == arInitial) {
16 | vsapi->requestFrameFilter(n, d->node, frameCtx);
17 | vsapi->requestFrameFilter(n, d->node2, frameCtx);
18 | }
19 | else if (activationReason == arAllFramesReady) {
20 | const VSFrame* src = vsapi->getFrameFilter(n, d->node, frameCtx);
21 | const VSFrame* src2 = vsapi->getFrameFilter(n, d->node2, frameCtx);
22 |
23 | const VSVideoFormat* fi = vsapi->getVideoFrameFormat(src);
24 | int height = vsapi->getFrameHeight(src, 0);
25 | int width = vsapi->getFrameWidth(src, 0);
26 |
27 | VSFrame* dst = vsapi->newVideoFrame(fi, width, height, src, core);
28 |
29 | for (int plane = 0; plane < fi->numPlanes; plane++) {
30 |
31 | const uint8_t* srcp = vsapi->getReadPtr(src, plane);
32 | const uint8_t* src2p = vsapi->getReadPtr(src2, plane);
33 | uint8_t* dstp = vsapi->getWritePtr(dst, plane);
34 |
35 | ptrdiff_t stride = vsapi->getStride(src, plane);
36 |
37 | int h = vsapi->getFrameHeight(src, plane);
38 | int w = vsapi->getFrameWidth(src, plane);
39 |
40 | for (int y = 0; y < h; y++) {
41 | for (int x = 0; x < w; x++) {
42 | int z = (srcp[x] - src2p[x] + 255);
43 | dstp[x] = d->tab[z];
44 | }
45 |
46 | dstp += stride;
47 | srcp += stride;
48 | src2p += stride;
49 | }
50 | }
51 |
52 | vsapi->freeFrame(src);
53 | vsapi->freeFrame(src2);
54 |
55 | return dst;
56 | }
57 |
58 | return NULL;
59 | }
60 |
61 | static void VS_CC adaptiveBinarizeFree(void* instanceData, VSCore* core, const VSAPI* vsapi) {
62 | AdaptiveBinarizeData* d = (AdaptiveBinarizeData*)instanceData;
63 | vsapi->freeNode(d->node);
64 | vsapi->freeNode(d->node2);
65 | free(d);
66 | }
67 |
68 | static void VS_CC adaptiveBinarizeCreate(const VSMap* in, VSMap* out, void* userData, VSCore* core, const VSAPI* vsapi) {
69 | AdaptiveBinarizeData d;
70 | AdaptiveBinarizeData* data;
71 | int err;
72 |
73 | d.node = vsapi->mapGetNode(in, "clip", 0, 0);
74 | d.node2 = vsapi->mapGetNode(in, "clip2", 0, 0);
75 | d.vi = vsapi->getVideoInfo(d.node);
76 |
77 | if (!vsh::isConstantVideoFormat(d.vi) || d.vi->format.sampleType != stInteger || d.vi->format.bitsPerSample != 8) {
78 | vsapi->mapSetError(out, "AdaptiveBinarize: only constant format 8bit integer input supported");
79 | vsapi->freeNode(d.node);
80 | return;
81 | }
82 |
83 | if (!vsh::isSameVideoInfo(vsapi->getVideoInfo(d.node2), d.vi)) {
84 | vsapi->mapSetError(out, "AdaptiveBinarize: both clips must have the same format and dimensions");
85 | vsapi->freeNode(d.node);
86 | vsapi->freeNode(d.node2);
87 | return;
88 | }
89 |
90 | if (vsapi->getVideoInfo(d.node2)->numFrames != d.vi->numFrames) {
91 | vsapi->mapSetError(out, "AdaptiveBinarize: both clips' number of frames do not match");
92 | vsapi->freeNode(d.node);
93 | vsapi->freeNode(d.node2);
94 | return;
95 | }
96 |
97 | d.c_param = vsapi->mapGetIntSaturated(in, "c", 0, &err);
98 | if (err)
99 | d.c_param = 3;
100 |
101 | for (int i = 0; i < 768; i++) {
102 | d.tab[i] = i - 255 <= -d.c_param ? 255 : 0;
103 | }
104 |
105 | data = (AdaptiveBinarizeData*)malloc(sizeof(d));
106 | *data = d;
107 |
108 | VSFilterDependency deps[]{ {d.node, rpGeneral}, {d.node2, rpGeneral} };
109 | vsapi->createVideoFilter(out, "AdaptiveBinarize", data->vi, adaptiveBinarizeGetFrame, adaptiveBinarizeFree, fmParallel, deps, 2, data, core);
110 | }
111 |
112 | VS_EXTERNAL_API(void) VapourSynthPluginInit2(VSPlugin* plugin, const VSPLUGINAPI* vspapi) {
113 | vspapi->configPlugin("com.julek.abrz", "abrz", "Adaptive Binarize", VS_MAKE_VERSION(1, 0), VAPOURSYNTH_API_VERSION, 0, plugin);
114 | vspapi->registerFunction("AdaptiveBinarize",
115 | "clip:vnode;"
116 | "clip2:vnode;"
117 | "c:int:opt;",
118 | "clip:vnode;",
119 | adaptiveBinarizeCreate, NULL, plugin);
120 | }
--------------------------------------------------------------------------------
/msvc_project/AdaptiveBinarize.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 |
23 |
24 |
25 | 16.0
26 | Win32Proj
27 | {68926a12-6394-4b75-80e4-f3d138161c7e}
28 | AdaptiveBinarize
29 | 10.0
30 |
31 |
32 |
33 | DynamicLibrary
34 | true
35 | v143
36 | Unicode
37 |
38 |
39 | DynamicLibrary
40 | false
41 | v143
42 | true
43 | Unicode
44 |
45 |
46 | DynamicLibrary
47 | true
48 | v143
49 | Unicode
50 |
51 |
52 | DynamicLibrary
53 | false
54 | v143
55 | true
56 | Unicode
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | C:\Program Files\VapourSynth\sdk\include\vapoursynth;$(IncludePath)
78 |
79 |
80 | C:\Program Files\VapourSynth\sdk\include\vapoursynth;$(IncludePath)
81 |
82 |
83 | C:\Program Files\VapourSynth\sdk\include\vapoursynth;$(IncludePath)
84 |
85 |
86 | C:\Program Files\VapourSynth\sdk\include\vapoursynth;$(IncludePath)
87 |
88 |
89 |
90 | Level3
91 | true
92 | WIN32;_DEBUG;ADAPTIVEBINARIZE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
93 | true
94 | NotUsing
95 | stdafx.h
96 | stdcpp20
97 |
98 |
99 | Windows
100 | true
101 | false
102 |
103 |
104 |
105 |
106 | Level3
107 | true
108 | true
109 | true
110 | WIN32;NDEBUG;ADAPTIVEBINARIZE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
111 | true
112 | NotUsing
113 | stdafx.h
114 | stdcpp20
115 |
116 |
117 | Windows
118 | true
119 | true
120 | true
121 | false
122 |
123 |
124 |
125 |
126 | Level3
127 | true
128 | _DEBUG;ADAPTIVEBINARIZE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
129 | true
130 | NotUsing
131 | stdafx.h
132 | stdcpp20
133 |
134 |
135 | Windows
136 | true
137 | false
138 |
139 |
140 |
141 |
142 | Level3
143 | true
144 | true
145 | true
146 | NDEBUG;ADAPTIVEBINARIZE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
147 | true
148 | NotUsing
149 | stdafx.h
150 | stdcpp20
151 |
152 |
153 | Windows
154 | true
155 | true
156 | true
157 | false
158 |
159 |
160 |
161 |
162 |
163 |
--------------------------------------------------------------------------------