2019-04-17 10:49:11 +08:00
|
|
|
//
|
|
|
|
// ImageProcess.cpp
|
|
|
|
// MNN
|
|
|
|
//
|
|
|
|
// Created by MNN on 2018/12/24.
|
|
|
|
// Copyright © 2018, Alibaba Group Holding Limited
|
|
|
|
//
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <map>
|
2021-11-30 10:10:53 +08:00
|
|
|
#include <MNN/ImageProcess.hpp>
|
2019-12-27 22:16:57 +08:00
|
|
|
#include "core/Macro.h"
|
|
|
|
#include "core/TensorUtils.hpp"
|
2019-04-17 10:49:11 +08:00
|
|
|
#define MNN_OPEN_TIME_TRACE
|
2019-12-27 22:16:57 +08:00
|
|
|
#include <MNN/AutoTime.hpp>
|
2020-02-29 16:44:08 +08:00
|
|
|
#include "backend/cpu/CPUTensorConvert.hpp"
|
2021-11-30 10:10:53 +08:00
|
|
|
#include "backend/cpu/CPUImageProcess.hpp"
|
2020-03-22 20:16:29 +08:00
|
|
|
#include <MNN/MNNForwardType.h>
|
|
|
|
#include "core/Backend.hpp"
|
2021-11-30 10:10:53 +08:00
|
|
|
#ifdef MNN_USE_SSE
|
|
|
|
#include "backend/cpu/x86_x64/AVX2Functions.hpp"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <MNN/Tensor.hpp>
|
|
|
|
#include <MNN/Interpreter.hpp>
|
|
|
|
#include "core/Execution.hpp"
|
|
|
|
#include "core/Backend.hpp"
|
|
|
|
#include "MNN_generated.h"
|
2021-04-08 15:34:23 +08:00
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#include "backend/cpu/x86_x64/cpu_id.h"
|
|
|
|
#endif
|
|
|
|
|
2020-05-20 11:46:12 +08:00
|
|
|
#define CACHE_SIZE 256
|
2019-04-17 10:49:11 +08:00
|
|
|
namespace MNN {
|
2021-11-30 10:10:53 +08:00
|
|
|
|
|
|
|
void registerBackend();
|
|
|
|
|
2019-04-17 10:49:11 +08:00
|
|
|
namespace CV {
|
|
|
|
struct ImageProcess::Inside {
|
|
|
|
Config config;
|
2021-11-30 10:10:53 +08:00
|
|
|
std::unique_ptr<CPUImageProcess> execution;
|
2019-04-17 10:49:11 +08:00
|
|
|
};
|
|
|
|
|
2022-08-12 10:30:48 +08:00
|
|
|
void ImageProcess::destroy(ImageProcess* pro) {
|
|
|
|
if (nullptr != pro) {
|
|
|
|
delete pro;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-17 10:49:11 +08:00
|
|
|
ImageProcess::~ImageProcess() {
|
|
|
|
delete mInside;
|
|
|
|
}
|
|
|
|
|
|
|
|
ImageProcess::ImageProcess(const Config& config) {
|
|
|
|
mInside = new Inside;
|
|
|
|
mInside->config = config;
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
mInside->config.mean[i] = config.mean[i];
|
|
|
|
mInside->config.normal[i] = config.normal[i];
|
|
|
|
}
|
2021-11-30 10:10:53 +08:00
|
|
|
registerBackend();
|
|
|
|
auto coreFunctions =
|
|
|
|
#ifdef MNN_USE_SSE
|
|
|
|
AVX2Functions::get();
|
|
|
|
#else
|
|
|
|
nullptr;
|
|
|
|
#endif
|
|
|
|
mInside->execution.reset(new CPUImageProcess(config, coreFunctions));
|
2019-04-17 10:49:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ImageProcess* ImageProcess::create(const Config& config, const Tensor* dstTensor) {
|
|
|
|
// TODO Get dstTensor' backend
|
|
|
|
return new ImageProcess(config);
|
|
|
|
}
|
|
|
|
|
|
|
|
ImageProcess* ImageProcess::create(const ImageFormat sourceFormat, const ImageFormat destFormat, const float* means,
|
|
|
|
const int meanCount, const float* normals, const int normalCount,
|
|
|
|
const Tensor* dstTensor) {
|
|
|
|
MNN::CV::ImageProcess::Config config;
|
|
|
|
if (means != nullptr && meanCount > 0) {
|
|
|
|
::memcpy(config.mean, means, sizeof(float) * meanCount);
|
|
|
|
}
|
|
|
|
if (normals != nullptr && normalCount > 0) {
|
|
|
|
::memcpy(config.normal, normals, sizeof(float) * normalCount);
|
|
|
|
}
|
|
|
|
config.sourceFormat = sourceFormat;
|
|
|
|
config.destFormat = destFormat;
|
|
|
|
return new ImageProcess(config);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageProcess::setMatrix(const Matrix& matrix) {
|
|
|
|
mTransform = matrix;
|
|
|
|
mTransform.invert(&mTransformInvert);
|
2021-11-30 10:10:53 +08:00
|
|
|
mInside->execution->setMatrix(matrix);
|
2019-04-17 10:49:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int _getBpp(ImageFormat format) {
|
|
|
|
switch (format) {
|
|
|
|
case RGB:
|
|
|
|
case BGR:
|
2021-11-30 10:10:53 +08:00
|
|
|
case YCrCb:
|
|
|
|
case YUV:
|
|
|
|
case HSV:
|
|
|
|
case XYZ:
|
2019-04-17 10:49:11 +08:00
|
|
|
return 3;
|
|
|
|
case RGBA:
|
|
|
|
case BGRA:
|
|
|
|
return 4;
|
|
|
|
case GRAY:
|
|
|
|
return 1;
|
2021-11-30 10:10:53 +08:00
|
|
|
case BGR555:
|
|
|
|
case BGR565:
|
|
|
|
return 2;
|
2019-04-17 10:49:11 +08:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Tensor* ImageProcess::createImageTensor(halide_type_t type, int width, int height, int bpp, void* p) {
|
|
|
|
return Tensor::create(std::vector<int>{1, height, width, bpp}, type, p);
|
|
|
|
}
|
|
|
|
|
2019-08-22 20:13:46 +08:00
|
|
|
static ImageFormat _correctImageFormat(int outputBpp, halide_type_t type, ImageFormat format) {
|
|
|
|
if (outputBpp != 4) {
|
2019-05-05 20:27:57 +08:00
|
|
|
return format;
|
|
|
|
}
|
|
|
|
// TODO, use same judge for uint8 -> float
|
2019-08-22 20:13:46 +08:00
|
|
|
if (type.code == halide_type_float) {
|
2019-05-05 20:27:57 +08:00
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::map<ImageFormat, ImageFormat> imageFormatTable = {{RGB, RGBA}, {BGR, BGRA}, {GRAY, RGBA}};
|
|
|
|
if (imageFormatTable.find(format) != imageFormatTable.end()) {
|
|
|
|
return imageFormatTable.find(format)->second;
|
|
|
|
}
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
2019-04-17 10:49:11 +08:00
|
|
|
ErrorCode ImageProcess::convert(const uint8_t* source, int iw, int ih, int stride, Tensor* destOrigin) {
|
|
|
|
auto dest = destOrigin;
|
|
|
|
if (nullptr == dest || nullptr == source) {
|
|
|
|
MNN_ERROR("null dest or source for image process\n");
|
|
|
|
return INPUT_DATA_ERROR;
|
|
|
|
}
|
2020-12-17 15:19:12 +08:00
|
|
|
if (TensorUtils::getDescribe(dest)->backend == nullptr && destOrigin->buffer().host == nullptr) {
|
2020-11-05 16:41:56 +08:00
|
|
|
MNN_ERROR("Invalid Tensor, the session may not be ready\n");
|
|
|
|
return INPUT_DATA_ERROR;
|
|
|
|
}
|
2019-04-17 10:49:11 +08:00
|
|
|
std::shared_ptr<Tensor> tempTensor;
|
2020-02-29 16:44:08 +08:00
|
|
|
auto ow = dest->width();
|
|
|
|
auto oh = dest->height();
|
|
|
|
auto bpp = dest->channel();
|
|
|
|
auto dimensionFormat = TensorUtils::getDescribe(dest)->dimensionFormat;
|
2020-03-22 20:16:29 +08:00
|
|
|
auto tensorBn = TensorUtils::getDescribe(dest)->backend;
|
|
|
|
auto bnType = MNN_FORWARD_CPU;
|
|
|
|
if(tensorBn){
|
|
|
|
bnType = tensorBn->type();
|
|
|
|
}
|
|
|
|
if (bnType != MNN_FORWARD_CPU) {
|
2020-02-29 16:44:08 +08:00
|
|
|
tempTensor.reset(Tensor::create({1, bpp, oh, ow}, dest->getType(), nullptr, Tensor::CAFFE_C4),[destOrigin] (void* p) {
|
2019-04-17 10:49:11 +08:00
|
|
|
auto hostTensor = (Tensor*)p;
|
|
|
|
destOrigin->copyFromHostTensor(hostTensor);
|
|
|
|
delete hostTensor;
|
|
|
|
});
|
|
|
|
dest = tempTensor.get();
|
|
|
|
}
|
2020-02-29 16:44:08 +08:00
|
|
|
else if (MNN_DATA_FORMAT_NCHW == dimensionFormat) {
|
|
|
|
tempTensor.reset(Tensor::create(dest->shape(), dest->getType(), nullptr, Tensor::CAFFE_C4), [destOrigin](void* p) {
|
|
|
|
auto hostTensor = (Tensor*)p;
|
|
|
|
CPUTensorConverter::convert(hostTensor, destOrigin);
|
|
|
|
delete hostTensor;
|
|
|
|
});
|
|
|
|
dest = tempTensor.get();
|
2019-04-17 10:49:11 +08:00
|
|
|
}
|
2020-02-29 16:44:08 +08:00
|
|
|
dimensionFormat = TensorUtils::getDescribe(dest)->dimensionFormat;
|
2019-04-17 10:49:11 +08:00
|
|
|
if (dimensionFormat == MNN_DATA_FORMAT_NC4HW4) {
|
2019-05-05 20:27:57 +08:00
|
|
|
bpp = 4;
|
2019-04-17 10:49:11 +08:00
|
|
|
}
|
2019-08-22 20:13:46 +08:00
|
|
|
return convert(source, iw, ih, stride, dest->host<void>(), ow, oh, bpp, ow * bpp, dest->getType());
|
|
|
|
}
|
2019-04-17 10:49:11 +08:00
|
|
|
|
2019-08-22 20:13:46 +08:00
|
|
|
ErrorCode ImageProcess::convert(const uint8_t* source, int iw, int ih, int stride, void* dest, int ow, int oh,
|
|
|
|
int outputBpp, int outputStride, halide_type_t type) {
|
2021-11-30 10:10:53 +08:00
|
|
|
auto ic = _getBpp(mInside->config.sourceFormat);
|
|
|
|
auto oc = outputBpp;
|
|
|
|
if (0 == oc) {
|
|
|
|
oc = _getBpp(mInside->config.destFormat);
|
|
|
|
}
|
2022-02-18 11:30:27 +08:00
|
|
|
std::unique_ptr<Tensor> input(createImageTensor(halide_type_of<uint8_t>(), iw, ih, ic, (void*)source)),
|
|
|
|
output(createImageTensor(type, ow, oh, oc, dest));
|
|
|
|
auto ins = { input.get() };
|
|
|
|
auto outs = { output.get() };
|
2021-11-30 10:10:53 +08:00
|
|
|
mInside->execution->setPadVal(this->mPaddingValue);
|
2022-05-06 19:51:20 +08:00
|
|
|
mInside->execution->setStride(stride);
|
2021-11-30 10:10:53 +08:00
|
|
|
mInside->execution->onResize(ins, outs);
|
|
|
|
mInside->execution->onExecute(ins, outs);
|
2019-04-17 10:49:11 +08:00
|
|
|
return NO_ERROR;
|
|
|
|
}
|
2022-02-18 11:30:27 +08:00
|
|
|
|
2022-05-06 19:51:20 +08:00
|
|
|
void ImageProcess::setDraw() {
|
|
|
|
if (mInside && mInside->execution) {
|
|
|
|
mInside->execution->setDraw();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-18 11:30:27 +08:00
|
|
|
void ImageProcess::draw(uint8_t* img, int w, int h, int c, const int* regions, int num, const uint8_t* color) {
|
|
|
|
std::unique_ptr<Tensor> imgTensor(createImageTensor(halide_type_of<uint8_t>(), w, h, c, (void*)img)),
|
|
|
|
regionTensor(Tensor::create(std::vector<int>{num, 3}, halide_type_of<int>(), (void*)regions)),
|
|
|
|
colorTensor(Tensor::create(std::vector<int>{c}, halide_type_of<uint8_t>(), (void*)color));
|
|
|
|
auto ins = { imgTensor.get(), regionTensor.get(), colorTensor.get() };
|
|
|
|
mInside->execution->onResize(ins, {});
|
|
|
|
mInside->execution->onExecute(ins, {});
|
|
|
|
}
|
2019-04-17 10:49:11 +08:00
|
|
|
} // namespace CV
|
|
|
|
} // namespace MNN
|