HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
COP2_SampleFilter.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2024
3  * Side Effects Software Inc. All rights reserved.
4  *
5  * Redistribution and use of Houdini Development Kit samples in source and
6  * binary forms, with or without modification, are permitted provided that the
7  * following conditions are met:
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. The name of Side Effects Software may not be used to endorse or
11  * promote products derived from this software without specific prior
12  * written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17  * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  *----------------------------------------------------------------------------
26  */
27 #include <UT/UT_DSOVersion.h>
28 #include <OP/OP_OperatorTable.h>
29 
30 #include <PRM/PRM_Include.h>
31 #include <PRM/PRM_Parm.h>
32 
33 #include <SYS/SYS_Math.h>
34 
35 #include <TIL/TIL_Region.h>
36 #include <TIL/TIL_Tile.h>
37 #include <TIL/TIL_TileList.h>
38 
39 #include <PXL/PXL_Pixel.h>
40 #include <RU/RU_Algorithm.h>
41 #include <COP2/COP2_CookAreaInfo.h>
42 
43 #include "COP2_SampleFilter.h"
44 
45 using namespace HDK_Sample;
46 
47 COP2_MASK_SWITCHER(4, "HDK Sample Filter");
48 
49 static PRM_Name names[] =
50 {
51  PRM_Name("left", "Left Enhance"),
52  PRM_Name("right", "Right Enhance"),
53  PRM_Name("top", "Top Enhance"),
54  PRM_Name("bottom", "Bottom Enhance"),
55 };
56 
57 
60 {
62 
67 
68  PRM_Template(),
69 };
70 
73 
75 
76 const char * COP2_SampleFilter::myInputLabels[] =
77 {
78  "Image to Enhance",
79  "Mask Input",
80  0
81 };
82 
83 
84 OP_Node *
86  const char *name,
87  OP_Operator *op)
88 {
89  return new COP2_SampleFilter(net, name, op);
90 }
91 
92 COP2_SampleFilter::COP2_SampleFilter(OP_Network *parent,
93  const char *name,
94  OP_Operator *entry)
95  : COP2_MaskOp(parent, name, entry)
96 {}
97 
99 {}
100 
101 // -----------------------------------------------------------------------
102 
103 // No cookSequenceInfo override - we'll let COP2_MaskOp do our work for us.
104 // This node doesn't alter any of the sequence parms - res, range or planes.
105 
106 
107 
108 // Stash some of our data in our shiny new context data.
111  float t, int xres, int yres,
112  int , int)
113 {
114  // Necessary since parameters cannot be evaluated in doCookMyTile
115 
117  float scx, scy;
118 
119  // The frame/scope effect allows the user to dial down the entire operation.
120  int index = mySequence.getImageIndex(t);
121  float effect = getFrameScopeEffect(index);
122 
123  // If cooking at a reduced res, scale down the effect for a closer
124  // approximation.
125  getScaleFactors(xres,yres, scx, scy);
126  effect *= SYSmin(scx,scy);
127 
128  data->myLeft = LEFT(t) * effect;
129  data->myRight = RIGHT(t) * effect;
130  data->myTop = TOP(t) * effect;
131  data->myBottom = BOTTOM(t) * effect;
132 
133  data->myKernel = new float[9];
134  // Kernel positions:
135  // 0 1 2
136  // 3 4 5
137  // 6 7 8
138 
139  data->myKernel[0] = -data->myLeft -data->myTop;
140  data->myKernel[1] = -data->myTop;
141  data->myKernel[2] = -data->myRight -data->myTop;
142 
143  data->myKernel[3] = -data->myLeft;
144  data->myKernel[5] = -data->myRight;
145 
146  data->myKernel[6] = -data->myLeft -data->myBottom;
147  data->myKernel[7] = -data->myBottom;
148  data->myKernel[8] = -data->myRight -data->myBottom;
149 
150  // center
151  data->myKernel[4] = 1.0f + 3.0f * (data->myLeft + data->myRight +
152  data->myTop + data->myBottom);
153  return data;
154 };
155 
156 // Our filter expands the image bounds by 1 in each direction.
157 void
159 {
160  int x1,y1,x2,y2;
161 
162  // Grab the bounds from the mask op, which combines the mask with the input
163  // bounds.
165 
166  // Now enlarge the bounds by 1 in each direction to account for the 3x3
167  // kernel.
168  context.getImageBounds(x1,y1,x2,y2);
169  context.setImageBounds(x1-1, y1-1, x2+1, y2+1);
170 }
171 
172 // Tell the scheduler which part of the inputs' image data we require.
173 void
175  COP2_CookAreaInfo &output_area,
176  const COP2_CookAreaList &input_areas,
177  COP2_CookAreaList &needed_areas)
178 {
179  // Add dependencies on the first input and the mask plane.
180  COP2_MaskOp::getInputDependenciesForOutputArea(output_area, input_areas,
181  needed_areas);
182 
183  // If bypassed, don't do anything else.
184  if (getBypass())
185  return;
186 
187  // Enlarge the needed area of the first input by 1 pixel in all directions.
188  COP2_CookAreaInfo *inarea =
189  makeOutputAreaDependOnMyPlane(0, output_area,input_areas,needed_areas);
190 
191  // It may not exist if the input node has an error.
192  if(inarea)
193  inarea->expandNeededArea(1, 1, 1, 1);
194 }
195 
196 
197 
198 // Some friendly node-info help. You can evaluate parms here and present even
199 // more specific help, like "Apply [Box/Gaussian]" blur (see Blur COP info)
200 const char *
202 {
203  return "This operation enhances individual edges.";
204 }
205 
206 namespace HDK_Sample {
207 
208 // This class allows the various data types to be abstracted out.
210 {
211 public:
212  cop2_EdgeEnhance(const float *kernel) : myKernel(kernel) {}
213 
215 
216  const float *myKernel;
217 };
218 
219 // This is the template class which defines the image operation
220 template<class Type,int fast> class cop2_EdgeEnhanceOp
221  : public RU_FilterOp<Type,fast>
222 {
223 public:
225  : RU_FilterOp<Type,fast>(alg)
226  { ; }
227 
228  int filter(TIL_TileList *output,
229  const TIL_Region *input, float t,
230  int thread=-1, void *data=0) override;
231 
232  int filter(TIL_Region *output,
233  const TIL_Region *input, float t,
234  int thread, void *data) override
236  output, input, t, thread, data); }
237 };
238 
240 
241 
242 template<class Type,int fast> int
244  const TIL_Region *input, float t,
245  int thread, void *data)
246 {
247  PXL_Pixel<Type,fast> pixel(output->myBlack, output->myWhite);
248  cop2_EdgeEnhance *parm =
250  const float *kernel = parm->myKernel;
251  TIL_Tile *itr=0;
252  const Type *source_data, *iscan1, *iscan2, *iscan3;
253  Type *dest_data, *scan;
254  int ti;
255  int stride, istride;
256  float sum;
257  int x,y;
258  int w,h;
259 
260  w = output->myX2 - output->myX1 + 1;
261  h = output->myY2 - output->myY1 + 1;
262  stride = w;
263  istride = w + 2;
264 
265  FOR_EACH_UNCOOKED_TILE(output, itr, ti)
266  {
267  dest_data = (Type *) itr->getImageData();
268  source_data = (Type *) input->getImageData(ti);
269 
270  // 3 scanlines for a 3x3 kernel
271  iscan1 = source_data + 1;
272  iscan2 = iscan1 + istride;
273  iscan3 = iscan2 + istride;
274 
275  scan = dest_data;
276 
277  for(y=0; y<h; y++)
278  {
279  for(x=0; x<w; x++)
280  {
281  pixel.set(iscan1[x-1]);
282  sum = (float)pixel * kernel[0];
283 
284  pixel.set(iscan1[x]);
285  sum += (float)pixel * kernel[1];
286 
287  pixel.set(iscan1[x+1]);
288  sum += (float)pixel * kernel[2];
289 
290  pixel.set(iscan2[x-1]);
291  sum += (float)pixel * kernel[3];
292 
293  pixel.set(iscan2[x]);
294  sum += (float)pixel * kernel[4];
295 
296  pixel.set(iscan2[x+1]);
297  sum += (float)pixel * kernel[5];
298 
299  pixel.set(iscan3[x-1]);
300  sum += (float)pixel * kernel[6];
301 
302  pixel.set(iscan3[x]);
303  sum += (float)pixel * kernel[7];
304 
305  pixel.set(iscan3[x+1]);
306  sum += (float)pixel * kernel[8];
307 
308  // Assign to the output array
309  pixel = sum;
310  scan[x] = pixel.getValue();
311  }
312 
313  scan += stride;
314  iscan1 += istride;
315  iscan2 += istride;
316  iscan3 += istride;
317  }
318  }
319  return 1;
320 }
321 
322 } // namespace HDK_Sample
323 
324 
325 
326 OP_ERROR
328 {
329  // Grab our context data.
331  static_cast<cop2_SampleFilterContext *>(context.data());
332 
333  // Grab the input image data that we need for our tile area.
334  TIL_Region *in = inputRegion(0, context,
335  tiles->myX1 -1,
336  tiles->myY1 -1,
337  tiles->myX2 +1,
338  tiles->myY2 +1,
339  TIL_HOLD); // streak edges when outside canvas
340  if(!in)
341  {
342  tiles->clearToBlack();
343  return error();
344  }
345 
346  // call the templated operation
347  cop2_EdgeEnhance op(data->myKernel);
348  op.filter(tiles, in, context.getTime(), NULL, context.myThreadIndex);
349 
350  releaseRegion(in);
351 
352  // done - return any errors.
353  return error();
354 }
355 
356 
357 void
359 {
360  table->addOperator(new OP_Operator("hdksamplefilt",
361  "HDK Sample Filter",
364  1,
365  2, // optional mask input.
367  0, // not generator
369 }
370 
371 
Simple example of a kernel filter.
void getInputDependenciesForOutputArea(COP2_CookAreaInfo &output_area, const COP2_CookAreaList &input_areas, COP2_CookAreaList &needed_areas) override
Storage class for our parameters and the kernel.
float * myKernel
Kernel filter derived from parameters.
static OP_Node * myConstructor(OP_Network *, const char *, OP_Operator *)
All nodes are instantiated via a myConstructor method.
unsigned int myWhite
Definition: TIL_TileList.h:80
void computeImageBounds(COP2_Context &context) override
OP_ERROR doCookMyTile(COP2_Context &context, TIL_TileList *tilelist) override
TIL_Sequence mySequence
Definition: COP2_Node.h:1345
GLboolean * data
Definition: glcorearb.h:131
OP_ERROR error() override
Definition: COP2_Node.h:483
void computeImageBounds(COP2_Context &context) override
int filter(TIL_TileList *output, const TIL_Region *input, float t, void *ndata=0, int thread=-1, void *data=0)
#define FOR_EACH_UNCOOKED_TILE(list, tile, i)
Definition: TIL_Defines.h:192
fpreal getTime() const
Definition: COP2_Context.h:77
int filter(TIL_Region *output, const TIL_Region *input, float t, int thread, void *data) override
static OP_TemplatePair myTemplatePair
Definition: COP2_MaskOp.h:29
UT_ErrorSeverity
Definition: UT_Error.h:25
void setImageBounds(int x1, int y1, int x2, int y2)
GLint y
Definition: glcorearb.h:103
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
static OP_VariablePair myVariablePair
Definition: COP2_Node.h:663
GLdouble GLdouble x2
Definition: glad.h:2349
void * getImageData(int index)
bool expandNeededArea(int pixels_left, int pixels_down, int pixels_right, int pixels_up)
int filter(TIL_TileList *output, const TIL_Region *input, float t, int thread=-1, void *data=0) override
GLint GLenum GLboolean GLsizei stride
Definition: glcorearb.h:872
GLdouble y1
Definition: glad.h:2349
COP2_ContextData * data()
void getScaleFactors(int xres, int yres, float &sx, float &sy) const
GLuint const GLchar * name
Definition: glcorearb.h:786
void getInputDependenciesForOutputArea(COP2_CookAreaInfo &output_area, const COP2_CookAreaList &input_areas, COP2_CookAreaList &needed_areas) override
GLint GLenum GLint x
Definition: glcorearb.h:409
PRM_API const PRM_Type PRM_FLT_J
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
PRM_API const PRM_Type PRM_SWITCHER
COP2_CookAreaInfo * makeOutputAreaDependOnMyPlane(int input, COP2_CookAreaInfo &output_area, const COP2_CookAreaList &input_areas, COP2_CookAreaList &needed_areas)
GLdouble t
Definition: glad.h:2397
void releaseRegion(TIL_Region *, int output=0)
cop2_EdgeEnhanceOp(RU_Algorithm *alg)
#define TOOL_PARM
Definition: COP2_Common.h:26
cop2_EdgeEnhance(const float *kernel)
Type getValue() const
Definition: PXL_Pixel.h:80
PRM_API PRM_Name PRMswitcherName
GLfloat GLfloat GLfloat GLfloat h
Definition: glcorearb.h:2002
**Note that the tasks the is the thread number *for the or if it s being executed by a non pool thread(this *can happen in cases where the whole pool is occupied and the calling *thread contributes to running the work load).**Thread pool.Have fun
TIL_Region * inputRegion(int input_index, COP2_Context &context, const TIL_Plane *plane, int array_index, float t, int xstart, int ystart, int xend, int yend, TIL_RegionExtend hold=TIL_BLACK, int share=1, void *regionmem[PLANE_MAX_VECTOR_SIZE]=0, bool correct_aspect=true, bool correct_bounds=true, int scan_alignment=0)
float getFrameScopeEffect(int image_index) override
unsigned int myBlack
Definition: TIL_TileList.h:80
void newCop2Operator(OP_OperatorTable *table)
static const char * myInputLabels[]
static OP_TemplatePair myTemplatePair
static OP_VariablePair myVariablePair
GLuint index
Definition: glcorearb.h:786
COP2_ContextData * newContextData(const TIL_Plane *p, int array_index, float t, int xres, int yres, int thread, int max_threads) override
COP2_MASK_SWITCHER(4,"HDK Sample Filter")
bool getImageBounds(int &x1, int &y1, int &x2, int &y2)
void getImageData(const unsigned char *&data) const
Type set(Type)
void clearToBlack(bool markconstant=true)
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:857
static PRM_Template myTemplateList[]
bool getBypass() const
Definition: OP_Node.h:1331
const char * getOperationInfo() override
Returns a description of the operation for the node info popup.
GLdouble GLdouble GLdouble y2
Definition: glad.h:2349
exint getImageIndex(double t, int clamp_range=1, int round_off=SEQUENCE_NEAREST) const
PRM_API PRM_Default PRMzeroDefaults[]
#define SYSmin(a, b)
Definition: SYS_Math.h:1539
DECLARE_FILTER_OP(cop2_EdgeEnhanceOp, override)
virtual int filter(TIL_TileList *, const TIL_Region *, float, int, void *)
Definition: format.h:895
IMPLEMENT_FILTER_OP(cop2_EdgeEnhance, cop2_EdgeEnhanceOp)