欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Opencv 图像等比例缩放

程序员文章站 2024-02-08 21:44:46
...

在神经网络训练过程中,会使用到大量的数据预处理操作。而缩放操作就是这里面最基础。例如Certernet中的缩放就是使用了等比例缩放,周围填充0的操作。

我们来分析下实现原理和实际效果

Opencv 图像等比例缩放
如上图所示,是将[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 - 目标图像的三个坐标点

我们看下等比例缩放时,输入图形的三个坐标点和输出图像的三个坐标点的计算公式

Opencv 图像等比例缩放

以上是原图和目标图像的三个坐标的空间关系。三个点的相对位置的宽和高分别与图形的宽和高对应,也就是将三个坐标点的空间关系与图像的宽和高进行绑定,以此达到使用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;

}

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;

}