// // CPUCropAndResize.cpp // MNN // // Created by MNN on 2018/08/23. // Copyright © 2018, Alibaba Group Holding Limited // #include "backend/cpu/CPUCropAndResize.hpp" #include #include "backend/cpu/CPUBackend.hpp" namespace MNN { template CPUCropAndResize::CPUCropAndResize(Backend* backend, const Op* op) : Execution(backend) { auto cr = op->main_as_CropAndResize(); mMethod = cr->method(); mExtrapolationValue = cr->extrapolationValue(); } template const ErrorCode CPUCropAndResize::CropAndResize(const Tensor* image, const Tensor* boxes, const Tensor* boxIndex, Tensor* crops) { const int batchSize = image->buffer().dim[0].extent; const int imageHeight = image->buffer().dim[1].extent; const int imageWidth = image->buffer().dim[2].extent; const int imageDepth = image->buffer().dim[3].extent; MNN_ASSERT(imageWidth > 0 && imageHeight > 0); const int numBoxes = crops->buffer().dim[0].extent; const int cropHeight = crops->buffer().dim[1].extent; const int cropWidth = crops->buffer().dim[2].extent; const int depth = crops->buffer().dim[3].extent; // init memset(crops->host(), 0, crops->size()); // Sharding across boxes. auto CropAndResizePerBox = [&](int startBox, int limitBox) { for (int b = startBox; b < limitBox; ++b) { const float y1 = boxes->host()[b * 4]; const float x1 = boxes->host()[b * 4 + 1]; const float y2 = boxes->host()[b * 4 + 2]; const float x2 = boxes->host()[b * 4 + 3]; const int32_t bIn = boxIndex->host()[b]; if (0 > bIn || bIn >= batchSize) { continue; } const float heightScale = (cropHeight > 1) ? (y2 - y1) * (imageHeight - 1) / (cropHeight - 1) : 0; const float widthScale = (cropWidth > 1) ? (x2 - x1) * (imageWidth - 1) / (cropWidth - 1) : 0; int32_t cropsHeight = crops->buffer().dim[1].extent; int32_t cropsWidth = crops->buffer().dim[2].extent; int32_t cropsDepth = crops->buffer().dim[3].extent; for (int y = 0; y < cropHeight; ++y) { const float inY = (cropHeight > 1) ? y1 * (imageHeight - 1) + y * heightScale : 0.5 * (y1 + y2) * (imageHeight - 1); if (inY < 0 || inY > imageHeight - 1) { for (int x = 0; x < cropWidth; ++x) { for (int d = 0; d < depth; ++d) { crops->host()[b * cropsHeight * cropsWidth * cropsDepth + y * cropsWidth * cropsDepth + x * cropsDepth + d] = mExtrapolationValue; } } continue; } if (mMethod == CropAndResizeMethod_BILINEAR) { const int topYIndex = floorf(inY); const int bottomYIndex = ceilf(inY); const float yLerp = inY - topYIndex; for (int x = 0; x < cropWidth; ++x) { const float inX = (cropWidth > 1) ? x1 * (imageWidth - 1) + x * widthScale : 0.5 * (x1 + x2) * (imageWidth - 1); if (inX < 0 || inX > imageWidth - 1) { for (int d = 0; d < depth; ++d) { crops->host()[b * cropsHeight * cropsWidth * cropsDepth + y * cropsWidth * cropsDepth + x * cropsDepth + d] = mExtrapolationValue; } continue; } const int leftXIndex = floorf(inX); const int rightXIndex = ceilf(inX); const float xLerp = inX - leftXIndex; for (int d = 0; d < depth; ++d) { const float topLeft( static_cast(image->host()[bIn * imageHeight * imageWidth * imageDepth + topYIndex * imageWidth * imageDepth + leftXIndex * imageDepth + d])); const float topRight( static_cast(image->host()[bIn * imageHeight * imageWidth * imageDepth + topYIndex * imageWidth * imageDepth + rightXIndex * imageDepth + d])); const float bottomLeft( static_cast(image->host()[bIn * imageHeight * imageWidth * imageDepth + bottomYIndex * imageWidth * imageDepth + leftXIndex * imageDepth + d])); const float bottomRight( static_cast(image->host()[bIn * imageHeight * imageWidth * imageDepth + bottomYIndex * imageWidth * imageDepth + rightXIndex * imageDepth + d])); const float top = topLeft + (topRight - topLeft) * xLerp; const float bottom = bottomLeft + (bottomRight - bottomLeft) * xLerp; crops->host()[b * cropsHeight * cropsWidth * cropsDepth + y * cropsWidth * cropsDepth + x * cropsDepth + d] = top + (bottom - top) * yLerp; } } } else if (mMethod == CropAndResizeMethod_NEAREST) { // method == "nearest" for (int x = 0; x < cropWidth; ++x) { const float inX = (cropWidth > 1) ? x1 * (imageWidth - 1) + x * widthScale : 0.5 * (x1 + x2) * (imageWidth - 1); if (inX < 0 || inX > imageWidth - 1) { for (int d = 0; d < depth; ++d) { crops->host()[b * cropsHeight * cropsWidth * cropsDepth + y * cropsWidth * cropsDepth + x * cropsDepth + d] = mExtrapolationValue; } continue; } const int closestXIndex = roundf(inX); const int closestYIndex = roundf(inY); for (int d = 0; d < depth; ++d) { crops->host()[b * cropsHeight * cropsWidth * cropsDepth + y * cropsWidth * cropsDepth + x * cropsDepth + d] = static_cast(image->host()[bIn * imageHeight * imageWidth * imageDepth + closestYIndex * imageWidth * imageDepth + closestXIndex * imageDepth + d]); } } } else { MNN_ASSERT(false); } } } }; for (int i = 0; i < numBoxes; i++) { CropAndResizePerBox(i, i + 1); } return NO_ERROR; } template ErrorCode CPUCropAndResize::onExecute(const std::vector& inputs, const std::vector& outputs) { // The shape of 'image' is [batch_size, image_height, image_width, // channels]. const Tensor* image = inputs[0]; // The shape of 'boxes' is [num_boxes, 4]. const Tensor* boxes = inputs[1]; // The shape of 'box_index' is [num_boxes]. const Tensor* boxIndex = inputs[2]; const ErrorCode status = CropAndResize(image, boxes, boxIndex, outputs[0]); return status; } class CPUCropAndResizeCreator : public CPUBackend::Creator { public: virtual Execution* onCreate(const std::vector& inputs, const std::vector& outputs, const MNN::Op* op, Backend* backend) const { return new CPUCropAndResize(backend, op); } }; REGISTER_CPU_OP_CREATOR(CPUCropAndResizeCreator, OpType_CropAndResize); } // namespace MNN