// Copyright (c) 2011 The Foundry Visionmongers Ltd. All Rights Reserved. #include "DDImage/DeepFilterOp.h" #include "DDImage/Knobs.h" static const char* CLASS = "DeepCrop"; using namespace DD::Image; class DeepCrop : public DeepFilterOp { float _zrange[2]; bool _useZMin; bool _useZMax; bool _outsideZRange; float _bbox[4]; bool _useBBox; bool _outsideBBox; public: DeepCrop(Node* node) : DeepFilterOp(node) { _zrange[0] = 1; _zrange[1] = 2; _bbox[0] = _bbox[2] = input_format().width(); _bbox[1] = _bbox[3] = input_format().height(); _bbox[0] *= 0.2; _bbox[1] *= 0.2; _bbox[2] *= 0.8; _bbox[3] *= 0.8; _useZMin = true; _useZMax = true; _useBBox = true; _outsideZRange = false; _outsideBBox = false; } const char* node_help() const override { return "Crop deep data in front of or behind certain planes, or inside or outside of a box."; } const char* Class() const override { return CLASS; } Op* op() override { return this; } void knobs(Knob_Callback f) override { Float_knob(f, &_zrange[0], "znear"); SetRange(f, 0, 1); Tooltip(f, "The near deep value"); Bool_knob(f, &_useZMin, "use_znear", "use"); Tooltip(f, "Whether to use the near deep value"); Float_knob(f, &_zrange[1], "zfar"); SetRange(f, 0, 1); Tooltip(f, "The far deep value"); Bool_knob(f, &_useZMax, "use_zfar", "use"); Tooltip(f, "Whether to use the far deep value"); Bool_knob(f, &_outsideZRange, "outside_zrange", "keep outside zrange"); Tooltip(f, "Whether to keep the samples with deep between the range (false), or outside the range (true)"); BBox_knob(f, &_bbox[0], "bbox"); SetFlags(f, Knob::ALWAYS_SAVE); Tooltip(f, "2D bounding box for clipping"); Bool_knob(f, &_useBBox, "use_bbox", "use"); Tooltip(f, "Whether to use the 2D bounding box"); Bool_knob(f, &_outsideBBox, "outside_bbox", "keep outside bbox"); Tooltip(f, "Whether to keep samples within the 2D bounding box (false), or outside it (true)"); } int knob_changed(DD::Image::Knob* k) override { knob("znear")->enable(_useZMin); knob("zfar")->enable(_useZMax); knob("bbox")->enable(_useBBox); return 1; } void _validate(bool for_real) override { DeepFilterOp::_validate(for_real); if (_useBBox && !_outsideBBox) { _deepInfo.box().set(floor(_bbox[0] - 1), floor(_bbox[1] - 1), ceil(_bbox[2] + 1), ceil(_bbox[3] + 1)); } } bool isCropped(DeepPixel &pixel, size_t sample, DD::Image::Channel channel) { float z = pixel.getUnorderedSample(sample, channel); bool inZ = (!_useZMax || z <= _zrange[1]) && (!_useZMin || z >= _zrange[0]); return ((_useZMin || _useZMax) && inZ == _outsideZRange); } bool doDeepEngine(DD::Image::Box box, const ChannelSet& channels, DeepOutputPlane& plane) override { if (!input0()) return true; DeepOp* in = input0(); DeepPlane inPlane; ChannelSet needed = channels; needed += Mask_DeepFront; if (!in->deepEngine(box, needed, inPlane)) return false; DeepInPlaceOutputPlane outPlane(channels, box); outPlane.reserveSamples(inPlane.getTotalSampleCount()); //samples per pixel after croping std::vector validSamples; for (DD::Image::Box::iterator it = box.begin(), itEnd = box.end(); it != itEnd; ++it) { const int x = it.x; const int y = it.y; bool inXY = (x >= _bbox[0] && x < _bbox[2] && y >= _bbox[1] && y < _bbox[3]); if (_useBBox && inXY == _outsideBBox) { // pixel out of crop bounds outPlane.setSampleCount(y, x, 0); continue; } DeepPixel inPixel = inPlane.getPixel(it); size_t inPixelSamples = inPixel.getSampleCount(); validSamples.clear(); validSamples.reserve(inPixelSamples); // find valid samples for (size_t iSample = 0; iSample < inPixelSamples; ++iSample) { if (isCropped(inPixel, iSample, DD::Image::Chan_DeepFront)) continue; if (isCropped(inPixel, iSample, DD::Image::Chan_DeepBack)) continue; validSamples.push_back(iSample); } outPlane.setSampleCount(it, validSamples.size()); DeepOutputPixel outPixel = outPlane.getPixel(it); // copy valid samples to DeepOutputPlane size_t outSample = 0; for (size_t inSample : validSamples) { const float* inData = inPixel.getUnorderedSample(inSample); float* outData = outPixel.getWritableUnorderedSample(outSample); for ( int iChannel = 0, nChannels = channels.size(); iChannel < nChannels; ++iChannel, ++outData, ++inData ) { *outData = *inData; } ++outSample; } } outPlane.reviseSamples(); mFnAssert(outPlane.isComplete()); plane = outPlane; return true; } static const Description d; static const Description d2; static const Description d3; }; static Op* build(Node* node) { return new DeepCrop(node); } const Op::Description DeepCrop::d(::CLASS, "Image/DeepCrop", build); const Op::Description DeepCrop::d2("DeepMask", "Image/DeepCrop", build); const Op::Description DeepCrop::d3("DeepClip", "Image/DeepCrop", build);