1 /* ***** BEGIN LICENSE BLOCK *****
2 * This file is part of openfx-misc <https://github.com/devernay/openfx-misc>,
3 * Copyright (C) 2013-2018 INRIA
4 *
5 * openfx-misc is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * openfx-misc is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with openfx-misc. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
17 * ***** END LICENSE BLOCK ***** */
18
19 /*
20 * OFX CImgEqualize plugin.
21 */
22
23 #include <memory>
24 #include <cmath>
25 #include <cfloat> // DBL_MAX
26 #include <cstring>
27 #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
28 #include <windows.h>
29 #endif
30
31 #include "ofxsProcessing.H"
32 #include "ofxsMaskMix.h"
33 #include "ofxsMacros.h"
34 #include "ofxsCoords.h"
35 #include "ofxsCopier.h"
36
37 #include "CImgFilter.h"
38
39 using namespace OFX;
40
41 OFXS_NAMESPACE_ANONYMOUS_ENTER
42
43 #define kPluginName "EqualizeCImg"
44 #define kPluginGrouping "Color"
45 #define kPluginDescription \
46 "Equalize histogram of pixel values.\n" \
47 "To equalize image brightness only, use the HistEQCImg plugin.\n" \
48 "Uses the 'equalize' function from the CImg library.\n" \
49 "CImg is a free, open-source library distributed under the CeCILL-C " \
50 "(close to the GNU LGPL) or CeCILL (compatible with the GNU GPL) licenses. " \
51 "It can be used in commercial applications (see http://cimg.eu)."
52
53 #define kPluginIdentifier "net.sf.cimg.CImgEqualize"
54 // History:
55 // version 1.0: initial version
56 // version 2.0: use kNatronOfxParamProcess* parameters
57 #define kPluginVersionMajor 2 // Incrementing this number means that you have broken backwards compatibility of the plug-in.
58 #define kPluginVersionMinor 0 // Increment this when you have fixed a bug or made it faster.
59
60 #define kSupportsComponentRemapping 1
61 #define kSupportsTiles 0 // Histogram must be computed on the whole image
62 #define kSupportsMultiResolution 1
63 #define kSupportsRenderScale 1
64 #define kSupportsMultipleClipPARs false
65 #define kSupportsMultipleClipDepths false
66 #define kRenderThreadSafety eRenderFullySafe
67 #ifdef cimg_use_openmp
68 #define kHostFrameThreading false
69 #else
70 #define kHostFrameThreading true
71 #endif
72 #define kSupportsRGBA true
73 #define kSupportsRGB true
74 #define kSupportsXY true
75 #define kSupportsAlpha true
76
77 #define kParamNbLevels "nb_levels"
78 #define kParamNbLevelsLabel "NbLevels"
79 #define kParamNbLevelsHint "Number of histogram levels used for the equalization."
80 #define kParamNbLevelsDefault 4096
81
82 #define kParamMin "min_value"
83 #define kParamMinLabel "Min Value"
84 #define kParamMinHint "Minimum pixel value considered for the histogram computation. All pixel values lower than min_value will not be counted."
85 #define kParamMinDefault 0.0
86
87 #define kParamMax "max_value"
88 #define kParamMaxLabel "Max Value"
89 #define kParamMaxHint "Maximum pixel value considered for the histogram computation. All pixel values higher than max_value will not be counted."
90 #define kParamMaxDefault 1.0
91
92
93 /// Equalize plugin
94 struct CImgEqualizeParams
95 {
96 int nb_levels;
97 double min_value;
98 double max_value;
99 };
100
101 class CImgEqualizePlugin
102 : public CImgFilterPluginHelper<CImgEqualizeParams, false>
103 {
104 public:
105
CImgEqualizePlugin(OfxImageEffectHandle handle)106 CImgEqualizePlugin(OfxImageEffectHandle handle)
107 : CImgFilterPluginHelper<CImgEqualizeParams, false>(handle, /*usesMask=*/false, kSupportsComponentRemapping, kSupportsTiles, kSupportsMultiResolution, kSupportsRenderScale, /*defaultUnpremult=*/ true)
108 {
109 _nb_levels = fetchIntParam(kParamNbLevels);
110 _min_value = fetchDoubleParam(kParamMin);
111 _max_value = fetchDoubleParam(kParamMax);
112 assert(_nb_levels && _min_value && _max_value);
113 }
114
getValuesAtTime(double time,CImgEqualizeParams & params)115 virtual void getValuesAtTime(double time,
116 CImgEqualizeParams& params) OVERRIDE FINAL
117 {
118 _nb_levels->getValueAtTime(time, params.nb_levels);
119 _min_value->getValueAtTime(time, params.min_value);
120 _max_value->getValueAtTime(time, params.max_value);
121 }
122
123 // compute the roi required to compute rect, given params. This roi is then intersected with the image rod.
124 // only called if mix != 0.
getRoI(const OfxRectI & rect,const OfxPointD &,const CImgEqualizeParams &,OfxRectI * roi)125 virtual void getRoI(const OfxRectI& rect,
126 const OfxPointD& /*renderScale*/,
127 const CImgEqualizeParams& /*params*/,
128 OfxRectI* roi) OVERRIDE FINAL
129 {
130 int delta_pix = 0;
131
132 roi->x1 = rect.x1 - delta_pix;
133 roi->x2 = rect.x2 + delta_pix;
134 roi->y1 = rect.y1 - delta_pix;
135 roi->y2 = rect.y2 + delta_pix;
136 }
137
render(const RenderArguments &,const CImgEqualizeParams & params,int,int,cimg_library::CImg<cimgpix_t> &,cimg_library::CImg<cimgpix_t> & cimg,int)138 virtual void render(const RenderArguments & /*args*/,
139 const CImgEqualizeParams& params,
140 int /*x1*/,
141 int /*y1*/,
142 cimg_library::CImg<cimgpix_t>& /*mask*/,
143 cimg_library::CImg<cimgpix_t>& cimg,
144 int /*alphaChannel*/) OVERRIDE FINAL
145 {
146 // PROCESSING.
147 // This is the only place where the actual processing takes place
148 cimg.equalize(params.nb_levels, (float)params.min_value, (float)params.max_value);
149 }
150
151 //virtual bool isIdentity(const IsIdentityArguments &/*args*/, const CImgEqualizeParams& /*params*/) OVERRIDE FINAL
152 //{
153 // return false;
154 //};
155
156 private:
157
158 // params
159 IntParam *_nb_levels;
160 DoubleParam *_min_value;
161 DoubleParam *_max_value;
162 };
163
164
165 mDeclarePluginFactory(CImgEqualizePluginFactory, {ofxsThreadSuiteCheck();}, {});
166
167 void
describe(ImageEffectDescriptor & desc)168 CImgEqualizePluginFactory::describe(ImageEffectDescriptor& desc)
169 {
170 // basic labels
171 desc.setLabel(kPluginName);
172 desc.setPluginGrouping(kPluginGrouping);
173 desc.setPluginDescription(kPluginDescription);
174
175 // add supported context
176 desc.addSupportedContext(eContextFilter);
177 desc.addSupportedContext(eContextGeneral);
178
179 // add supported pixel depths
180 //desc.addSupportedBitDepth(eBitDepthUByte);
181 //desc.addSupportedBitDepth(eBitDepthUShort);
182 desc.addSupportedBitDepth(eBitDepthFloat);
183
184 // set a few flags
185 desc.setSingleInstance(false);
186 desc.setHostFrameThreading(kHostFrameThreading);
187 desc.setSupportsMultiResolution(kSupportsMultiResolution);
188 desc.setSupportsTiles(kSupportsTiles);
189 desc.setTemporalClipAccess(false);
190 desc.setRenderTwiceAlways(true);
191 desc.setSupportsMultipleClipPARs(kSupportsMultipleClipPARs);
192 desc.setSupportsMultipleClipDepths(kSupportsMultipleClipDepths);
193 desc.setRenderThreadSafety(kRenderThreadSafety);
194 }
195
196 void
describeInContext(ImageEffectDescriptor & desc,ContextEnum context)197 CImgEqualizePluginFactory::describeInContext(ImageEffectDescriptor& desc,
198 ContextEnum context)
199 {
200 // create the clips and params
201 PageParamDescriptor *page = CImgEqualizePlugin::describeInContextBegin(desc, context,
202 kSupportsRGBA,
203 kSupportsRGB,
204 kSupportsXY,
205 kSupportsAlpha,
206 kSupportsTiles,
207 /*processRGB=*/ true,
208 /*processAlpha*/ false,
209 /*processIsSecret=*/ false);
210
211 {
212 IntParamDescriptor *param = desc.defineIntParam(kParamNbLevels);
213 param->setLabel(kParamNbLevelsLabel);
214 param->setHint(kParamNbLevelsHint);
215 param->setDefault(kParamNbLevelsDefault);
216 if (page) {
217 page->addChild(*param);
218 }
219 }
220 {
221 DoubleParamDescriptor *param = desc.defineDoubleParam(kParamMin);
222 param->setLabel(kParamMinLabel);
223 param->setHint(kParamMinHint);
224 param->setDefault(kParamMinDefault);
225 param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
226 param->setDisplayRange(0., 1.);
227 param->setIncrement(0.001);
228 if (page) {
229 page->addChild(*param);
230 }
231 }
232 {
233 DoubleParamDescriptor *param = desc.defineDoubleParam(kParamMax);
234 param->setLabel(kParamMaxLabel);
235 param->setHint(kParamMaxHint);
236 param->setDefault(kParamMaxDefault);
237 param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
238 param->setDisplayRange(0., 1.);
239 param->setIncrement(0.001);
240 if (page) {
241 page->addChild(*param);
242 }
243 }
244
245 CImgEqualizePlugin::describeInContextEnd(desc, context, page);
246 }
247
248 ImageEffect*
createInstance(OfxImageEffectHandle handle,ContextEnum)249 CImgEqualizePluginFactory::createInstance(OfxImageEffectHandle handle,
250 ContextEnum /*context*/)
251 {
252 return new CImgEqualizePlugin(handle);
253 }
254
255 static CImgEqualizePluginFactory p(kPluginIdentifier, kPluginVersionMajor, kPluginVersionMinor);
256 mRegisterPluginFactoryInstance(p)
257
258 OFXS_NAMESPACE_ANONYMOUS_EXIT
259