概述
在神经网络训练过程中,会使用到大量的数据预处理操作。而缩放操作就是这里面最基础。例如Certernet中的缩放就是使用了等比例缩放,周围填充0的操作。
我们来分析下实现原理和实际效果
如上图所示,是将[600,543的图片]等比例缩放为[352,352],边界填充黑色。这样做的好处,是图片不会有变形压缩,使得训练的时候网络输入的图片,更解决与现实中的图像。
实际场景中,图片的变形压缩并不影响图像的检查效果; 这种压缩方式的目的主要是为了在设备端实现推理时,与训练时保持预处理的一致,提高检出率。
warpAffine
以上效果的实现,是通过cv2.warpAffine方法实现的。该方法的参数为
cv2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) → dst
src - 输入图像。
M - 变换矩阵。
dsize - 输出图像的大小。
flags - 插值方法的组合(int 类型!)
borderMode - 边界像素模式(int 类型!)
borderValue - (重点!)边界填充值; 默认情况下,它为0。
在这里,我们需要重点获取M变换矩阵
getAffineTransform
变换矩阵是通过
cv2.getAffineTransform(np.float32(dst), np.float32(src))
src - 输入图像的三个坐标点
dst - 目标图像的三个坐标点
我们看下等比例缩放时,输入图形的三个坐标点和输出图像的三个坐标点的计算公式
其中,在原图中
//图像中心点
src_cen = (src_w/2, src_h/2)
max_src_cen = max(src_w/2, src_h/2)
a0 = (src_cen.x,src_cen.y)
a1 = (src_cen.x,src_cen.y-max_src_cen)
a2 = (src_cen.x-max_src_cen, src_cen.y)
同理,目标图像中:
//图像中心点
dst_cen = (dst_w/2, dst_h/2)
max_det_cen = max(dst_w/2, dst_h/2)
a0 = (dst_cen.x,dst_cen.y)
a1 = (dst_cen.x,dst_cen.y-max_det_cen)
a2 = (dst_cen.x-max_det_cen, dst_cen.y)
三个点的相对位置的宽和高分别与图形的宽和高对应,也就是将三个坐标点的空间关系与图像的宽和高进行绑定,以此达到使用getAffineTransform的时候,能够等比例缩放图片
源码
以下是c++的实现,python的实现,可以参考centernet工程centernet
#include <opencv2opencv.hpp>
struct CenterScale
{
cv::Mat input;
cv::Point2f center;
cv::Point2f scale;
float r = 0;
};
cv::Point2f get_dir(cv::Point2f src_point, float rot_rad)
{
float sn = sin(rot_rad);
float cs = cos(rot_rad);
float src_result[2] = { 0, 0 };
src_result[0] = src_point.x * cs - src_point.y * sn;
src_result[1] = src_point.x * sn + src_point.y * cs;
return{ src_result[0], src_result[1] };
}
cv::Mat get_affine_transform(cv::Point2f center, cv::Point2f scale, float rot,
int output_size[], bool inv)
{
float scale_tmp = scale.x;
float src_w = scale_tmp;
float dst_w = (float)output_size[0];
float dst_h = (float)output_size[1];
float half_1 = 0.5;
float shift[2] = { 0, 0 };
float rot_rad = 3.1415926 * rot / 180;
cv::Point2f src_point = { 0, src_w * -half_1 };
cv::Point2f src_dir = get_dir(src_point, rot_rad);
cv::Point2f dst_dir = { 0, dst_w * -half_1 };
cv::Point2f src[3];
cv::Point2f dst[3];
src[0] = { center.x + scale_tmp * shift[0], center.y + scale_tmp * shift[1] };
src[1] = { center.x + src_dir.x + scale_tmp * shift[0], center.y + src_dir.y + scale_tmp * shift[1] };
dst[0] = { dst_w * half_1, dst_h * half_1 };
dst[1] = { dst_w * half_1 + dst_dir.x, dst_h * half_1 + dst_dir.y };
src[2] = get_3rd_point(src[0], src[1]);
dst[2] = get_3rd_point(dst[0], dst[1]);
cv::Mat trans;
if (inv)
{
trans = cv::getAffineTransform(dst, src);
}
else
{
trans = cv::getAffineTransform(src, dst);
}
return trans;
}
CenterScale getAffineTransImage(const cv::Mat& img, int network_input_size[])
{
int width = img.cols;
int height = img.rows;
CenterScale center_scale;
center_scale.center = { width / (float)2.0, height / (float)2.0 };
float s = std::max(height, width)*1.0;
center_scale.scale = { s, s };
float rolate = 0;
cv::Mat trans = get_affine_transform(center_scale.center, center_scale.scale, rolate, network_input_size, false);
cv::Mat transed_dstImage(network_input_size[0], network_input_size[1], img.type());
cv::warpAffine(img, transed_dstImage, trans, transed_dstImage.size());
CenterScale meta;
meta.input = transed_dstImage;
meta.center = center_scale.center;
meta.scale = center_scale.scale;
meta.r = 0.0;
return meta;
}
cv::Point2f get_3rd_point(cv::Point2f a, cv::Point2f b)
{
cv::Point2f direct = { a.x - b.x, a.y - b.y };
direct = { b.x - direct.y, b.y + direct.x };
return direct;
}
int main()
{
cv::Mat input = cv::imread("G://VS2013//Ai_Demo//images//person.jpg");
int network_input_size[2] = { 352, 352 };
CenterScale meta = getAffineTransImage(input, network_input_size);
cv::imwrite(meta.input);
return 0;
}
最后
以上就是傻傻身影为你收集整理的Opencv 图像等比例缩放的全部内容,希望文章能够帮你解决Opencv 图像等比例缩放所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复