简介
copyMakeBorder是OpenCV里一个边界填充的API,其API接口如下:
// OpenCV 4.5.2 源码
void cv::copyMakeBorder( InputArray _src, OutputArray _dst, int top, int bottom,
int left, int right, int borderType, const Scalar& value )
大部分的贴子只介绍了其支持的填充类型,没有对输入的图像格式进行说明,这里主要是讲下后者。
填充类型:
// OpenCV 4.5.2 源码
//! Various border types, image boundaries are denoted with `|`
//! @see borderInterpolate, copyMakeBorder
enum BorderTypes {
BORDER_CONSTANT = 0, //!< `iiiiii|abcdefgh|iiiiiii` with some specified `i`
BORDER_REPLICATE = 1, //!< `aaaaaa|abcdefgh|hhhhhhh`
BORDER_REFLECT = 2, //!< `fedcba|abcdefgh|hgfedcb`
BORDER_WRAP = 3, //!< `cdefgh|abcdefgh|abcdefg`
BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba`
BORDER_TRANSPARENT = 5, //!< `uvwxyz|abcdefgh|ijklmno`
BORDER_REFLECT101 = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
BORDER_DEFAULT = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
BORDER_ISOLATED = 16 //!< do not look outside of ROI
};
图像类型:
这个API的实现就是在图像内存空间的四周填充像素,如果是RGB|BGR|GRAY这样的单平面类型,执行没有问题,但如果是YUV这样的多平面,这个填充逻辑就达不到预期效果,因为YUV类型图像需要分别对Y\U\V分量进行填充。实验代码如下:
Mat image = cv::Mat(height, width, CV_8UC3); // 默认像素全是0
Mat yuv = convert_color(image, cv::COLOR_BGR2YUV_I420);
Mat dst;
int top = 2;
int bottom = top;
int left = 2;
int right = left;
cv::Scalar values(50, 150, 250);
cv::copyMakeBorder(yuv, dst, top, bottom, left, right, cv::BorderTypes::BORDER_CONSTANT, values);
save_mat_as_bin(dst, save_path);
输入:16x16,转成YUV后:16x24(24=16 + 16/2)
输出:20x28
预期结果:20x30(20 + 20/2)
将保存后的BIN用hex editor工具打开查看,发现只填充不对。
YUV类型处理思路:
将YUV分成Y、U、V三个分量单独处理,每个分量可以看成是一个Gray图像。如果是NV12图像类型,可以分成Y、UV分量,其中UV当成一个有2个通道的图像。
以NV12图像处理为例,DEMO代码如下:
cv::Mat image = CreateImage(fpath); // 读取图像并转成YUV420SP格式
cv::Mat nv12 = YUV420_SP_to_NV12(image);
cv::Mat gray = cv::Mat(image.rows, image.cols, CV_8UC1);
gray.data = nv12.data;
cv::copyMakeBorder();
cv::Mat uv = cv::Mat(image.rows / 2, image.cols / 2, CV_8UC2);
uv.data = nv12.data + (image.rows * image.cols);
cv::copyMakeBorder();
测试代码链接:linglx/OpenCV