Opencv 图像等比例缩放

在神经网络训练过程中,会使用到大量的数据预处理操作。而缩放操作就是这里面最基础。例如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 <opencv2\opencv.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;

}
  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值