HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
COP2/COP2_SampleFilter.C
/*
* Copyright (c) 2024
* Side Effects Software Inc. All rights reserved.
*
* Redistribution and use of Houdini Development Kit samples in source and
* binary forms, with or without modification, are permitted provided that the
* following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. The name of Side Effects Software may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*----------------------------------------------------------------------------
*/
#include <PRM/PRM_Parm.h>
#include <SYS/SYS_Math.h>
#include <TIL/TIL_Region.h>
#include <TIL/TIL_Tile.h>
#include <PXL/PXL_Pixel.h>
using namespace HDK_Sample;
COP2_MASK_SWITCHER(4, "HDK Sample Filter");
static PRM_Name names[] =
{
PRM_Name("left", "Left Enhance"),
PRM_Name("right", "Right Enhance"),
PRM_Name("top", "Top Enhance"),
PRM_Name("bottom", "Bottom Enhance"),
};
{
};
{
"Image to Enhance",
"Mask Input",
0
};
const char *name,
{
return new COP2_SampleFilter(net, name, op);
}
COP2_SampleFilter::COP2_SampleFilter(OP_Network *parent,
const char *name,
OP_Operator *entry)
: COP2_MaskOp(parent, name, entry)
{}
COP2_SampleFilter::~COP2_SampleFilter()
{}
// -----------------------------------------------------------------------
// No cookSequenceInfo override - we'll let COP2_MaskOp do our work for us.
// This node doesn't alter any of the sequence parms - res, range or planes.
// Stash some of our data in our shiny new context data.
float t, int xres, int yres,
int , int)
{
// Necessary since parameters cannot be evaluated in doCookMyTile
float scx, scy;
// The frame/scope effect allows the user to dial down the entire operation.
float effect = getFrameScopeEffect(index);
// If cooking at a reduced res, scale down the effect for a closer
// approximation.
getScaleFactors(xres,yres, scx, scy);
effect *= SYSmin(scx,scy);
data->myLeft = LEFT(t) * effect;
data->myRight = RIGHT(t) * effect;
data->myTop = TOP(t) * effect;
data->myBottom = BOTTOM(t) * effect;
data->myKernel = new float[9];
// Kernel positions:
// 0 1 2
// 3 4 5
// 6 7 8
data->myKernel[0] = -data->myLeft -data->myTop;
data->myKernel[1] = -data->myTop;
data->myKernel[2] = -data->myRight -data->myTop;
data->myKernel[3] = -data->myLeft;
data->myKernel[5] = -data->myRight;
data->myKernel[6] = -data->myLeft -data->myBottom;
data->myKernel[7] = -data->myBottom;
data->myKernel[8] = -data->myRight -data->myBottom;
// center
data->myKernel[4] = 1.0f + 3.0f * (data->myLeft + data->myRight +
data->myTop + data->myBottom);
return data;
};
// Our filter expands the image bounds by 1 in each direction.
void
{
int x1,y1,x2,y2;
// Grab the bounds from the mask op, which combines the mask with the input
// bounds.
// Now enlarge the bounds by 1 in each direction to account for the 3x3
// kernel.
context.getImageBounds(x1,y1,x2,y2);
context.setImageBounds(x1-1, y1-1, x2+1, y2+1);
}
// Tell the scheduler which part of the inputs' image data we require.
void
COP2_CookAreaInfo &output_area,
const COP2_CookAreaList &input_areas,
COP2_CookAreaList &needed_areas)
{
// Add dependencies on the first input and the mask plane.
needed_areas);
// If bypassed, don't do anything else.
if (getBypass())
return;
// Enlarge the needed area of the first input by 1 pixel in all directions.
makeOutputAreaDependOnMyPlane(0, output_area,input_areas,needed_areas);
// It may not exist if the input node has an error.
if(inarea)
inarea->expandNeededArea(1, 1, 1, 1);
}
// Some friendly node-info help. You can evaluate parms here and present even
// more specific help, like "Apply [Box/Gaussian]" blur (see Blur COP info)
const char *
{
return "This operation enhances individual edges.";
}
namespace HDK_Sample {
// This class allows the various data types to be abstracted out.
{
public:
cop2_EdgeEnhance(const float *kernel) : myKernel(kernel) {}
const float *myKernel;
};
// This is the template class which defines the image operation
template<class Type,int fast> class cop2_EdgeEnhanceOp
: public RU_FilterOp<Type,fast>
{
public:
: RU_FilterOp<Type,fast>(alg)
{ ; }
int filter(TIL_TileList *output,
const TIL_Region *input, float t,
int thread=-1, void *data=0) override;
int filter(TIL_Region *output,
const TIL_Region *input, float t,
int thread, void *data) override
output, input, t, thread, data); }
};
template<class Type,int fast> int
const TIL_Region *input, float t,
int thread, void *data)
{
PXL_Pixel<Type,fast> pixel(output->myBlack, output->myWhite);
const float *kernel = parm->myKernel;
TIL_Tile *itr=0;
const Type *source_data, *iscan1, *iscan2, *iscan3;
Type *dest_data, *scan;
int ti;
int stride, istride;
float sum;
int x,y;
int w,h;
w = output->myX2 - output->myX1 + 1;
h = output->myY2 - output->myY1 + 1;
stride = w;
istride = w + 2;
FOR_EACH_UNCOOKED_TILE(output, itr, ti)
{
dest_data = (Type *) itr->getImageData();
source_data = (Type *) input->getImageData(ti);
// 3 scanlines for a 3x3 kernel
iscan1 = source_data + 1;
iscan2 = iscan1 + istride;
iscan3 = iscan2 + istride;
scan = dest_data;
for(y=0; y<h; y++)
{
for(x=0; x<w; x++)
{
pixel.set(iscan1[x-1]);
sum = (float)pixel * kernel[0];
pixel.set(iscan1[x]);
sum += (float)pixel * kernel[1];
pixel.set(iscan1[x+1]);
sum += (float)pixel * kernel[2];
pixel.set(iscan2[x-1]);
sum += (float)pixel * kernel[3];
pixel.set(iscan2[x]);
sum += (float)pixel * kernel[4];
pixel.set(iscan2[x+1]);
sum += (float)pixel * kernel[5];
pixel.set(iscan3[x-1]);
sum += (float)pixel * kernel[6];
pixel.set(iscan3[x]);
sum += (float)pixel * kernel[7];
pixel.set(iscan3[x+1]);
sum += (float)pixel * kernel[8];
// Assign to the output array
pixel = sum;
scan[x] = pixel.getValue();
}
scan += stride;
iscan1 += istride;
iscan2 += istride;
iscan3 += istride;
}
}
return 1;
}
} // namespace HDK_Sample
{
// Grab our context data.
static_cast<cop2_SampleFilterContext *>(context.data());
// Grab the input image data that we need for our tile area.
TIL_Region *in = inputRegion(0, context,
tiles->myX1 -1,
tiles->myY1 -1,
tiles->myX2 +1,
tiles->myY2 +1,
TIL_HOLD); // streak edges when outside canvas
if(!in)
{
tiles->clearToBlack();
return error();
}
// call the templated operation
op.filter(tiles, in, context.getTime(), NULL, context.myThreadIndex);
// done - return any errors.
return error();
}
void
{
table->addOperator(new OP_Operator("hdksamplefilt",
"HDK Sample Filter",
1,
2, // optional mask input.
0, // not generator
}