IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 基于opencv的c++图像处理(几何变换) -> 正文阅读

[人工智能]基于opencv的c++图像处理(几何变换)

前言

基于opencv的c++接口,实现常用的图像几何变换方法,包括了图像平移、旋转、缩放、坐标映射变换、仿射变换。

相关的opencv接口解析

CV_EXPORTS_W void remap( InputArray src, OutputArray dst,
                         InputArray map1, InputArray map2,
                         int interpolation, int borderMode = BORDER_CONSTANT,
                         const Scalar& borderValue = Scalar());

函数 remap 使用指定的映射转换源图像。
@param src 源图像。
@param dst 目标图像。 它的大小与 map1 相同,类型与 src 相同。
@param map1 第一个 (x,y) 点的映射,或者只是 x 类型为 CV_16SC2 的值,
CV_32FC1 或 CV_32FC2。 有关将浮点表示转换为定点以提高速度的详细信息,请参阅 convertMaps。
@param map2 分别具有 CV_16UC1、CV_32FC1 或 none 类型的 y 值的第二个映射(如果 map1 是 (x,y) 点,则为空映射)。
@param interpolation 插值方法(请参阅#InterpolationFlags)。 此函数不支持方法#INTER_AREA。
@param borderMode 像素外推法(参见#BorderTypes)。 当borderMode=#BORDER_TRANSPARENT 时,表示目标图像中与源图像中的“异常值”对应的像素未被函数修改。
@param borderValue 用于恒定边框的值。 默认为 0。

//! 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
};
CV_EXPORTS Mat getAffineTransform( const Point2f src[], const Point2f dst[] );

该函数计算仿射变换的矩阵。
@param src 源图像中三角形顶点的坐标。
@param dst 目标图像中相应三角形顶点的坐标。

CV_EXPORTS_W void warpAffine( InputArray src, OutputArray dst,
                              InputArray M, Size dsize,
                              int flags = INTER_LINEAR,
                              int borderMode = BORDER_CONSTANT,
                              const Scalar& borderValue = Scalar());

函数 warpAffine 使用指定的矩阵变换源图像。
@param src 输入图像。
@param dst 输出图像,其大小为 dsize 且类型与 src 相同。
@param M 变换矩阵。
@param dsize 输出图像的大小。
@param flags 插值方法的组合(请参阅#InterpolationFlags)和可选标志 #WARP_INVERSE_MAP,这意味着 M 是逆变换。
@param borderMode 像素外推方法(参见#BorderTypes); 当borderMode=#BORDER_TRANSPARENT时,表示目标图像中与源图像中的“异常值”对应的像素未被函数修改。
@param borderValue 值在恒定边框的情况下使用; 默认情况下,它是 0。

示例代码

geometricTransform.h

#pragma once
#include <iostream>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>

using namespace std;
using namespace cv;
#define PROCESS_IMG_SUCESS 0
#define PROCESS_IMG_FAIL 1


namespace ImgEnhance
{
	//图像几何变换
	class GeometricTransformate
	{
	public:
		GeometricTransformate() { cout << "GeometricTransformate is being created" << endl; } // 这是构造函数声明
		~GeometricTransformate() { cout << "GeometricTransformate is being deleted" << endl; } // 这是析构函数声明
		int RemapTransformate(cv::Mat srcImage, cv::Mat &dstImage);//坐标映射变换													   
		cv::Mat imageTranslation1(cv::Mat & srcImage, int xOffset, int yOffset);// 平移操作 图像大小不变															
		cv::Mat imageTranslation2(cv::Mat & srcImage, int xOffset, int yOffset);// 平移操作 图像大小改变 
		int MoveTransformate(cv::Mat srcImage, cv::Mat &dstImage, int xOffset, int yOffset);//图像平移																							
		cv::Mat imageReduction1(cv::Mat &srcImage, float kx, float ky);// 基于等间隔提取图像缩放
		cv::Vec3b areaAverage(const cv::Mat &srcImage, Point_<int> leftPoint, Point_<int> rightPoint);
		cv::Mat  imageReduction2(const Mat &srcImage, double kx, double ky);
		int ScaleTransformate(cv::Mat srcImage, cv::Mat &dstImage, float kx, float ky);//图像缩放
		cv::Mat angelRotate(cv::Mat& src, int angle);//旋转
		int RotateTransformate(cv::Mat srcImage, cv::Mat &dstImage, int angle);//图像旋转
		int AffineTransformate(cv::Mat srcImage, cv::Mat &dstImage, cv::Point2f srcPoint[], cv::Point2f resPoint[]);//仿射变换

	};
}

geometricTransform.cpp

#include"geometricTransform.h"

int ImgEnhance::GeometricTransformate::RemapTransformate(cv::Mat srcImage, cv::Mat &dstImage)
{
	if (srcImage.empty())
	{
		printf("cannot load!!\n");
		return 1;
	}
	// X与Y方向矩阵
	cv::Mat xMapImage(srcImage.size(), CV_32FC1);
	cv::Mat yMapImage(srcImage.size(), CV_32FC1);
	int rows = srcImage.rows;
	int cols = srcImage.cols;
	for (int j = 0; j < rows; j++)
	{
		for (int i = 0; i < cols; i++)
		{
			// x与y均翻转
			xMapImage.at<float>(j, i) = cols - i;
			yMapImage.at<float>(j, i) = rows - j;
		}
	}
	// 重映射操作
	remap(srcImage, dstImage, xMapImage, yMapImage, INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0));
	return 0;
}

// 平移操作 图像大小不变
cv::Mat ImgEnhance::GeometricTransformate::imageTranslation1(cv::Mat & srcImage, int xOffset, int yOffset)
{
	int nRows = srcImage.rows;
	int nCols = srcImage.cols;
	cv::Mat resultImage(srcImage.size(),
		srcImage.type());
	// 遍历图像
	for (int i = 0; i < nRows; ++i)
	{
		for (int j = 0; j < nCols; ++j)
		{
			// 映射变换
			int x = j - xOffset;
			int y = i - yOffset;
			// 边界判断
			if (x >= 0 && y >= 0 && x < nCols && y < nRows)
				resultImage.at<cv::Vec3b>(i, j) =
				srcImage.ptr<cv::Vec3b>(y)[x];
		}
	}
	return resultImage;
}
// 平移操作 图像大小改变 
cv::Mat ImgEnhance::GeometricTransformate::imageTranslation2(cv::Mat & srcImage, int xOffset, int yOffset)
{
	// 设置平移尺寸
	int nRows = srcImage.rows + abs(yOffset);
	int nCols = srcImage.cols + abs(xOffset);
	cv::Mat resultImage(nRows, nCols,
		srcImage.type());
	for (int i = 0; i < nRows; ++i)
	{
		for (int j = 0; j < nCols; ++j)
		{
			// 映射变换
			int x = j - xOffset;
			int y = i - yOffset;
			// 边界判断
			if (x >= 0 && y >= 0 && x < nCols && y < nRows)
				resultImage.at<cv::Vec3b>(i, j) =
				srcImage.ptr<cv::Vec3b>(y)[x];
		}
	}
	return resultImage;
}

int ImgEnhance::GeometricTransformate::MoveTransformate(cv::Mat srcImage, cv::Mat &dstImage, int xOffset, int yOffset)
{
	if (srcImage.empty())
	{
		printf("cannot load!!\n");
		return 1;
	}
	// 图像平移不改变大小
	dstImage = imageTranslation1(srcImage, xOffset, yOffset);
	// 图像平移改变大小
	//dstImage = imageTranslation1(srcImage, xOffset, yOffset);
	return 0;
}

// 基于等间隔提取图像缩放
cv::Mat ImgEnhance::GeometricTransformate::imageReduction1(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;
}

cv::Vec3b ImgEnhance::GeometricTransformate::areaAverage(const cv::Mat &srcImage, Point_<int> leftPoint, Point_<int> rightPoint)
{
	int temp1 = 0, temp2 = 0, temp3 = 0;
	// 计算区域子块像素点个数
	int nPix = (rightPoint.x - leftPoint.x + 1)*
		(rightPoint.y - leftPoint.y + 1);
	// 区域子块各个通道对像素值求和
	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  ImgEnhance::GeometricTransformate::imageReduction2(const 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 ImgEnhance::GeometricTransformate::ScaleTransformate(cv::Mat srcImage, cv::Mat &dstImage, float kx, float ky)
{
	if (srcImage.empty())
	{
		printf("cannot load!!\n");
		return 1;
	}

	dstImage = imageReduction1(srcImage, kx, ky);// 基于等间隔提取图像缩放

												 //dstImage = imageReduction2(srcImage, kx, ky);
	return 0;
}

cv::Mat ImgEnhance::GeometricTransformate::angelRotate(cv::Mat& src, 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 = src.rows;
	int nSrcCols = src.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];
	// 计算出极值点
	float kxMin = min(min(min(0.0f, a1), a2), a3);
	float kxMax = max(max(max(0.0f, a1), a2), a3);
	float kyMin = min(min(min(0.0f, b1), b2), b3);
	float kyMax = max(max(max(0.0f, b1), b2), b3);
	// 计算输出矩阵的尺寸
	int nRows = abs(kxMax - kxMin);
	int nCols = abs(kyMax - kyMin);
	cv::Mat dst(nRows, nCols, src.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) =
					src.at<cv::Vec3b>(y, x);
			}
		}
	}
	return dst;
}

int ImgEnhance::GeometricTransformate::RotateTransformate(cv::Mat srcImage, cv::Mat &dstImage, int angle)
{
	if (srcImage.empty())
	{
		printf("cannot load!!\n");
		return 1;
	}
	dstImage = angelRotate(srcImage, angle);
	return 0;
}

int ImgEnhance::GeometricTransformate::AffineTransformate(cv::Mat srcImage, cv::Mat &dstImage, cv::Point2f srcPoint[], cv::Point2f resPoint[])
{
	if (srcImage.empty())
	{
		printf("cannot load!!\n");
		return 1;
	}
	// 计算仿射变换矩阵,即仿射变换的2*3数组
	// 定义仿射变换矩阵2X3
	cv::Mat warpMat(cv::Size(2, 3), CV_32F);
	warpMat = cv::getAffineTransform(srcPoint, resPoint);
	// 根据仿射矩阵计算图像仿射变换
	cv::warpAffine(srcImage, dstImage, warpMat, dstImage.size());
	return 0;
}

test.cpp


#include"geometricTransform.h"

ImgEnhance::GeometricTransformate ImgG;//几何变换

int main()
{
	// 读取源图像及判断
	cv::Mat srcImage = cv::imread("boudingRect.jpg");
	if (!srcImage.data)
	{
		return 1;
	}
	cv::namedWindow("原始图", 0);
	cv::imshow("原始图", srcImage);
	// 转化为灰度图像
	cv::Mat srcGray;
	if (srcImage.channels() == 3)
	{
		cv::cvtColor(srcImage, srcGray, COLOR_RGB2GRAY);
	}
	else
	{
		srcGray = srcImage.clone();
	}
	cv::namedWindow("灰度图", 0);
	cv::imshow("灰度图", srcGray);
	//坐标映射变换	
	Mat remapImage;
	ImgG.RemapTransformate(srcImage, remapImage);
	cv::namedWindow("坐标映射变换结果图", 0);
	cv::imshow("坐标映射变换结果图", remapImage);
	//图像平移	
	Mat moveImage;
	ImgG.MoveTransformate(srcImage, moveImage,50,50);
	cv::namedWindow("图像平移结果图", 0);
	cv::imshow("图像平移结果图", moveImage);

	//图像缩放
	Mat scaleImage;
	ImgG.ScaleTransformate(srcImage, scaleImage, 0.5, 0.5);
	cv::namedWindow("图像缩放结果图", 0);
	cv::imshow("图像缩放结果图", scaleImage);

	//图像旋转
	Mat rotateImage;
	ImgG.RotateTransformate(srcImage, rotateImage, -30);
	cv::namedWindow("图像旋转结果图", 0);
	cv::imshow("图像旋转结果图", rotateImage);

	仿射变换
	/*vector<cv::Point2f> srcPoint;
	vector<cv::Point2f> resPoint;
	srcPoint.push_back(cv::Point2f(0, 0));
	srcPoint.push_back(cv::Point2f(3, 3));
	srcPoint.push_back(cv::Point2f(15, 15));
	resPoint.push_back(cv::Point2f(0, 0));
	resPoint.push_back(cv::Point2f(3, 3));
	resPoint.push_back(cv::Point2f(25, 25));*/
	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);
	Mat affineImage;
	ImgG.AffineTransformate(srcImage, affineImage, srcPoint, resPoint);

	cv::namedWindow("仿射变换结果图", 0);
	cv::imshow("仿射变换结果图", affineImage);

	cv::waitKey(0);

	return 0;

}

结果展示

在这里插入图片描述

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2022-09-30 00:52:59  更:2022-09-30 00:56:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 21:26:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码