opencv之图像翻转、平移、缩放、旋转、仿射学习笔记

opencv版本:opencv3.4.1

 

目录

1. 图像翻转(坐标映射)

2.  平移

 3. 缩放

4. 旋转


1. 图像翻转(坐标映射)

int main()
{
    cv::Mat srcImage = cv::imread("./lena.jpg");
	if(!srcImage.data){
		return -1;
	}

	int rows = srcImage.rows;
	int cols = srcImage.cols;	

	//输出矩阵定义
	cv::Mat resultImage(rows, cols, srcImage.type());

	//x与y方向的矩阵
	cv::Mat xMapImage(rows, cols, CV_32FC1);
	cv::Mat yMapImage(rows, cols, CV_32FC1);

	//图像遍历
	for(int r = 0; r < rows; r++){
		for(int c = 0; c < cols; c++){
			//x与y均翻转
			xMapImage.at<float>(r,c) = cols-c;
			yMapImage.at<float>(r,c) = rows-r;
		}
	}

	remap(srcImage, resultImage, xMapImage, yMapImage, CV_INTER_LINEAR,   cv::BORDER_CONSTANT,Scalar(0,0,0));

	cv::imwrite("resultImage.jpg", resultImage);
}

效果:

2.  平移

 有两种情况,(a)平移时图像大小保持不变; (b)平移时图像大小变化;

//平移不改变图像
//参数:平移的图像,平移的x和y偏移量;
cv::Mat imageTranslateNoChange(cv::Mat &srcImage, int xOffset, int yOffset)
{
	int nRows = srcImage.rows;
	int nCols = srcImage.cols;

	cv::Mat resultImage(nRows, nCols, srcImage.type());

	//遍历图像
	for(int r = 0 ; r < nRows; r++){
		for(int c = 0; c < nCols; c++){
			int x = c - xOffset;
			int y = r - yOffset;

			//边界判断;
			if(x >= 0 && y >= 0 && x < nCols && y < nRows){
				resultImage.at<cv::Vec3b>(r, c) = srcImage.ptr<cv::Vec3b>(y)[x];
			}
				
		}
	}

	return resultImage;
}

//平移,会改变图像
cv::Mat imageTranslateChange(cv::Mat &srcImage, int xOffset, int yOffset)
{
	int nRows = srcImage.rows + abs(yOffset);//加上y
	int nCols = srcImage.cols + abs(xOffset);//加上x

	cv::Mat resultImage(nRows, nCols, srcImage.type());

	//遍历图像
	for(int r = 0 ; r < nRows; r++){
		for(int c = 0; c < nCols; c++){
			int x = c - xOffset;
			int y = r - yOffset;

			//边界判断;
			if(x >= 0 && y >= 0 && x < nCols && y < nRows){
				resultImage.at<cv::Vec3b>(r, c) = srcImage.ptr<cv::Vec3b>(y)[x];
			}
				
		}
	}

	return resultImage;
}

int main()
{
	cv::Mat srcImage = cv::imread("./lena.jpg");
	if(!srcImage.data){
		return -1;
	}

	//cv::Mat resultImage = imageTranslateNoChange(srcImage, 200, 50);
	//cv::imwrite("translateNoChange.jpg", resultImage);
	
	cv::Mat resultImage = imageTranslateChange(srcImage, 80, 80);
	cv::imwrite("translateChange.jpg", resultImage);
	
	return  0;
}

效果:

 3. 缩放

//等间隔提取图像缩放
cv::Mat imageEquidistant(cv::Mat& srcImage, float kx, float ky)
{
	//获取输出图像分辨率
	int nRows = cvRound(srcImage.rows * kx);
	int nCols = cvRound(srcImage.cols * ky);

	cv::Mat resultImage(nRows, nCols, srcImage.type());
	for(int i = 0; i < nRows; ++i){
		for(int j = 0; j < nCols; ++j){
			//根据水平因子计算坐标
			int x = static_cast<int>((i+1)/kx+0.5)-1;

			//根据垂直因子计算坐标
			int y = static_cast<int>((j+1)/ky+0.5)-1;

			resultImage.at<cv::Vec3b>(i, j) = srcImage.at<cv::Vec3b>(x,y);
		}
	}

	return resultImage;
}

static cv::Vec3b areaAverage(cv::Mat& srcImage, Point_<int> leftPoint, Point_<int> rightPoint)
{
	int temp1 = 0, temp2=0, temp3=0;

	//计算区域子块像素点个数
	int nPix = (rightPoint.x - leftPoint.x) * (rightPoint.y - leftPoint.y);

	//对区域子块各个通道对像素求和
	for(int i = leftPoint.x; i < rightPoint.x ;++i){
		for(int j = leftPoint.y; j < rightPoint.y; ++j){
			temp1 += srcImage.at<cv::Vec3b>(i,j)[0];
			temp2 += srcImage.at<cv::Vec3b>(i,j)[1];
			temp3 += srcImage.at<cv::Vec3b>(i,j)[2];
		}
	}

	//对每个通道求均值
	Vec3b vecTemp;
	vecTemp[0] = temp1 / nPix;
	vecTemp[1] = temp2 / nPix;
	vecTemp[2] = temp3 / nPix;

	return vecTemp;
}

//基于子块提取图像缩放
cv::Mat imageRegionSubBlock(cv::Mat& srcImage, double kx, double ky)
{
	//获取输出图像分辨率
	int nRows = cvRound(srcImage.rows * kx);
	int nCols = cvRound(srcImage.cols * ky);

	cv::Mat resultImage(nRows, nCols, srcImage.type());

	//区域子块的左上角行列坐标
	int leftRowCoordinate = 0;
	int leftColCoordinate = 0;
	for(int i = 0; i < nRows; ++i){
		//根据水平因子计算坐标
		int x = static_cast<int>((i+1)/kx+0.5)-1;
		
		for(int j = 0; j < nCols; ++j){
			
			//根据垂直因子计算坐标
			int y = static_cast<int>((j+1)/ky+0.5)-1;

			//求解区域子块的均值;
			resultImage.at<Vec3b>(i, j) = areaAverage(srcImage,Point_<int>(leftRowCoordinate,leftColCoordinate),Point_<int>(x,y));

			//更新子块左上角的列坐标
			leftColCoordinate = y + 1;
		}
		leftColCoordinate = 0;

		//更新子块左上角的行坐标
		leftRowCoordinate = x + 1;
	}

	return resultImage;
}
int main()
{
	cv::Mat srcImage = cv::imread("./lena.jpg");
	if(!srcImage.data){
		return -1;
	}

	cv::Mat resultImage;
	//resultImage = imageEquidistant(srcImage, 0.5, 0.5);
	resultImage = imageRegionSubBlock(srcImage, 0.5, 0.5);

	cv::imwrite("RegionSubBlock.jpg", resultImage);
	
	return  0;
}

效果: 

4. 旋转

cv::Mat imageRotate(cv::Mat& srcImage, int angle)
{
	//角度转换
	float alpha = angle * CV_PI / 180;

	//构造旋转矩阵
	float rotateMat[3][3] = {
		{cos(alpha), -sin(alpha), 0},
		{sin(alpha), cos(alpha), 0},
		{0 , 0, 1}
	};

	int nSrcRows = srcImage.rows;
	int nSrcCols = srcImage.cols;

	//计算旋转后图像矩阵的各个顶点位置
	float a1 = nSrcCols * rotateMat[0][0] ;
	float b1 = nSrcCols * rotateMat[1][0] ;

	float a2 = nSrcCols * rotateMat[0][0] + nSrcRows * rotateMat[0][1];
	float b2 = nSrcCols * rotateMat[1][0] + nSrcRows * rotateMat[1][1];;
	
	float a3 = nSrcRows * rotateMat[0][1] ;
	float b3 = nSrcRows * rotateMat[1][1] ;

	int kxMin = min(min(min(0.0f,a1),a2),a3);
	int kxMax = max(max(max(0.0f,a1),a2),a3);

	int kyMin = min(min(min(0.0f,b1),b2),b3);
	int kyMax = max(max(max(0.0f,b1),b2),b3);

	//计算输出矩阵的尺寸
	int nRows = abs(kxMax - kxMin);
	int nCols = abs(kyMax - kyMin);

	cv::Mat dst(nRows, nCols, srcImage.type(), cv::Scalar::all(0));

	for(int i = 0; i < nRows; ++i){
		for(int j = 0 ; j < nCols; ++j){
			//旋转坐标转换
			int x = (j + kxMin) * rotateMat[0][0] - (i + kyMin) * rotateMat[0][1];
			int y = -(j + kxMin) * rotateMat[1][0] + (i + kyMin) * rotateMat[1][1];

			//区域旋转
			if((x >= 0) && (x < nSrcCols) && (y >= 0) && y < nSrcRows){
				dst.at<cv::Vec3b>(i, j) = srcImage.at<cv::Vec3b>(y,x);
			}
		}
	}
	
	return dst;
}

4.1 旋转30和60度

int main()
{
	cv::Mat srcImage = cv::imread("./lena.jpg");
	if(!srcImage.data){
		return -1;
	}

	cv::Mat resultImage;
	//resultImage = imageRotate(srcImage,30);
    resultImage = imageRotate(srcImage,60);

	cv::imwrite("rotate60.jpg", resultImage);
	
	return  0;
}

效果:

        图像翻转是图像旋转的特例,opencv中提供了traspose与filp函数对图像进行矩阵转置变换;可以将图像进行水平或垂直翻转;

4.2 图像翻转

int main()
{
	cv::Mat srcImage = cv::imread("./lena.jpg");
	if(!srcImage.data){
		return -1;
	}

	cv::Mat resultImage;
	//transpose(srcImage, resultImage);//逆时针旋转90;
	//flip( srcImage, resultImage, 0); //垂直翻转
	//flip( srcImage, resultImage, 1); //水平翻转
	//flip( srcImage, resultImage, -1);//垂直和水平翻转

	cv::imwrite("flip-1.jpg", resultImage);
	
	return  0;
}

 效果:

5. 仿射变换

int main()
{
	cv::Mat srcImage = cv::imread("./lena.jpg");
	if(!srcImage.data){
		return -1;
	}

	int nRows = srcImage.rows;
	int nCols = srcImage.cols;

	//定义仿射变换的二维点数组
	//源图像和目标图像对应映射的三个点
	cv::Point2f srcPoint[3];
	cv::Point2f resPoint[3];

	srcPoint[0] = cv::Point2f(0, 0);
	srcPoint[1] = cv::Point2f(nCols-1, 0);
	srcPoint[2] = cv::Point2f(0,nRows-1);

	resPoint[0] =cv::Point2f(nCols*0, nRows*0.33);
	resPoint[1] =cv::Point2f(nCols*0.85, nRows*0.25);
	resPoint[2] =cv::Point2f(nCols*0.15, nRows*0.7);

	//定义仿射变换矩阵
	cv::Mat warpMat(cv::Size(2,3), CV_32F);

	cv::Mat resultImage;
	cv::Mat::zeros(nRows, nCols, srcImage.type());

	//计算仿射变换矩阵,即仿射变换的2x3数组
	warpMat = cv::getAffineTransform(srcPoint, resPoint);

	//根据仿射矩阵计算图像仿射变换
	cv::warpAffine(srcImage, resultImage, warpMat, cv::Size(resultImage.rows,resultImage.cols));
	cv::imwrite("warpAffine.jpg", resultImage);

	#if 1
	//设置仿射变换参数
	cv::Point2f centerPoint = cv::Point2f(nCols/2, nRows/2);
	double angle = -50;
	double scale = 0.7;

	//获取仿射变换矩阵
	warpMat = getRotationMatrix2D(centerPoint, angle, scale);

	//对源图像进行角度仿射变换
	cv::warpAffine(srcImage, resultImage, warpMat, cv::Size(resultImage.rows,resultImage.cols));
	
	#endif 

	cv::imwrite("warpAffine-angle.jpg", resultImage);
	
	return  0;
}

 效果:

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天未及海宽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值