参考资料:
C语言数字图像处理—1.5图像基本变换之平移缩放旋转
c语言数字图像处理(三):仿射变换
书籍:数字图像处理与机器视觉 —— Visual C++ 与 Matlab 实现
1、几何变换的介绍
图像几何变换又称为图像的空间变换,它将一幅图像中的坐标位置映射到另一幅图像中的新坐标位置。几何变换的关键就是确定这种空间映射关系,以及映射过程中的变换参数。
几何变换不改变图像的像素值,只是在图像平面上进行像素的重新安排。一个几何变换通常需要两个部分运算。
- 空间变换所需要的运算。例如,平移、镜像等。需要用它来表示输出图像与输入图像之间的像素映射关系。
- 插值算法。因为在计算变换关系的时候,有可能输出图像的像素会被映射到输入图像的非整数坐标上。
2、平移
1、公式说明:

2、实现
/************************************************************
*Function: inMove
*Description: 图像的平移
*Params: inArray - 原始图像的有效二维数组
* width - 图像宽度
* height - 图像高度
* x - 横轴平移的图像信息,正值表示向右平移,负值表示向左平移
* y - 纵轴平移的图像信息,正值表示向下平移,负值表示向上平移
*Return: outArr - 结果图像的有效二维数组
************************************************************/
uint8_t** inMove(uint8_t** inArray, uint32_t width, uint32_t height, int x, int y)
{
// 生成结果图片的二维数组
uint8_t** outArr = new uint8_t * [height];
for (uint32_t i = 0; i < height; i++) {
outArr[i] = new uint8_t[width];
}
// 平移
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int srcY = i - y, srcX = j - x; // 映射的原始坐标计算
outArr[i][j] = isInArray(srcX, srcY, width, height) ? inArray[srcY][srcX] : 0;
}
}
return outArr;
}
3、镜像
1、公式说明


2、实现
/************************************************************
*Function: horMirror
*Description: 图像的水平镜像
*Params: inArray - 原始图像的有效二维数组
* width - 图像宽度
* height - 图像高度
*Return: outArr - 结果图像的有效二维数组
************************************************************/
uint8_t** horMirror(uint8_t** inArray, uint32_t width, uint32_t height)
{
uint8_t** outArr = new uint8_t * [height];
for (uint32_t i = 0; i < height; i++) {
outArr[i] = new uint8_t[width];
}
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
outArr[i][j] = inArray[i][width - 1 - j];
}
}
return outArr;
}
/************************************************************
*Function: verMirror
*Description: 图像的垂直镜像
*Params: inArray - 原始图像的有效二维数组
* width - 图像宽度
* height - 图像高度
*Return: outArr - 结果图像的有效二维数组
************************************************************/
uint8_t** verMirror(uint8_t** inArray, uint32_t width, uint32_t height)
{
uint8_t** outArr = new uint8_t * [height];
for (uint32_t i = 0; i < height; i++) {
outArr[i] = new uint8_t[width];
}
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
outArr[i][j] = inArray[height - 1 - i][j];
}
}
return outArr;
}
4、转置
1、公式说明


/************************************************************
*Function: transpose
*Description: 图像的转置,x、y 的坐标相互转置为 y、x
*Params: inArray - 原始图像的有效二维数组
* width - 图像宽度
* height - 图像高度
*Return: outArr - 结果图像的有效二维数组
************************************************************/
uint8_t** transpose(uint8_t** inArray, uint32_t width, uint32_t height)
{
uint32_t outHeight = width, outWidth = height;
uint8_t** outArr = new uint8_t * [outHeight];
for (uint32_t i = 0; i < outHeight; i++) {
outArr[i] = new uint8_t[outWidth];
}
for (int i = 0; i < outHeight; i++) {
for (int j = 0; j < outWidth; j++) {
outArr[i][j] = inArray[j][i];
}
}
return outArr;
}
5、缩放
1、公式说明

- 插值算法的相关博客介绍:图像处理+双线性插值法
2、实现
/************************************************************
*Function: NearInterpolation
*Description: 临近插值,图像的缩放
*Params: inArray - 原始图像的有效二维数组
* width - 图像宽度
* height - 图像高度
* outArray - 目标图像的有效二维数组
* outWidth - 图像宽度
* outHeight - 图像高度
*Return: None
************************************************************/
void NearInterpolation(uint8_t** inArray, uint32_t width, uint32_t height, uint8_t** outArray, uint32_t outWidth, uint32_t outHeight)
{
// 计算放大、缩小的倍数
double heightTimes = (double)height / (double)outHeight;
double widthTimes = (double)width / (double)outWidth;
// 进行坐标计算
for (uint32_t i = 0; i < outHeight; i++) {
for (uint32_t j = 0; j < outWidth; j++) {
int yPoint = Clipping((int)round(i * heightTimes), 0, height - 1); // 临近插值,进行四舍五入、限幅之后计算原始坐标位置
int xPoint = Clipping((int)round(j * widthTimes), 0, width - 1);
outArray[i][j] = inArray[yPoint][xPoint];
}
}
}
/************************************************************
*Function: BilinearInterpolation
*Description: 双线性插值,图像的缩放
*Params: inArray - 原始图像的有效二维数组
* width - 图像宽度
* height - 图像高度
* outArray - 目标图像的有效二维数组
* outWidth - 图像宽度
* outHeight - 图像高度
*Return: None
************************************************************/
void BilinearInterpolation(uint8_t** inArray, uint32_t width, uint32_t height, uint8_t** outArray, uint32_t outWidth, uint32_t outHeight)
{
// 计算放大、缩小的倍数
double heightTimes = (double)height / (double)outHeight;
double widthTimes = (double)width / (double)outWidth;
// 定义相关变量
double x = 0, y = 0; // 定义 原始坐标对应位置
int x1 = 0, y1 = 0, x2 = 0, y2 = 0; // 对应范围的四个点的坐标(左上、右上、左下、右下)
int w1 = 0, w2 = 0, w3 = 0, w4 = 0; // 定义相关权值
uint8_t f11 = 0, f12 = 0, f21 = 0, f22 = 0; // 对应范围的四个点的像素值(左上、右上、左下、右下)
// 循环计算
for (uint32_t i = 0; i < outHeight; i++) {
for (uint32_t j = 0; j < outWidth; j++) {
// 像素信息
x = widthTimes * (j + 0.5) - 0.5, y = heightTimes * (i + 0.5) - 0.5; // 将起点转换为中心坐标
x1 = (int)(x), x2 = (int)(x + 1); // 计算 对应范围的四个点的坐标,并转换为整型变量
y1 = (int)(y), y2 = (int)(y + 1);
// 得到 对应范围的四个点的像素值,当该点不在范围内的时候,默认为 0
f11 = isInArray(x1, y1, width, height) ? inArray[y1][x1] : 0;
f12 = isInArray(x1, y2, width, height) ? inArray[y2][x1] : 0;
f21 = isInArray(x2, y1, width, height) ? inArray[y1][x2] : 0;
f22 = isInArray(x2, y2, width, height) ? inArray[y2][x2] : 0;
// 扩大取整后,进行移位计算,节省时间
w1 = (int)((x - x1) * 2048), w2 = 2048 - w1;
w3 = (int)((y - y1) * 2048), w4 = 2048 - w3;
// 计算、限幅
outArray[i][j] = (f11 * w2 * w4 + f21 * w1 * w4 + f12 * w2 * w3 + f22 * w1 * w3) >> 22;
outArray[i][j] = Clipping(outArray[i][j], 0, 255);
}
}
}
/************************************************************
*Function: resize
*Description: 图像的缩放
*Params: inArray - 原始图像的有效二维数组
* width - 图像宽度
* height - 图像高度
* outWidth - 缩放图像宽度
* outHeight - 缩放图像高度
* mode - 模式选择
*Return: outArr - 结果图像的有效二维数组
************************************************************/
uint8_t** resize(uint8_t** inArray, uint32_t width, uint32_t height, uint32_t outWidth, uint32_t outHeight, int mode)
{
// 生成结果图片的二维数组
uint8_t** outArr = new uint8_t * [outHeight];
for (uint32_t i = 0; i < outHeight; i++) {
outArr[i] = new uint8_t[outWidth];
}
// 模式选择
switch (mode)
{
case Near:
NearInterpolation(inArray, width, height, outArr, outWidth, outHeight);
break;
default:
BilinearInterpolation(inArray, width, height, outArr, outWidth, outHeight);
break;
}
return outArr;
}
6、旋转
1、公式说明





2、实现
/************************************************************
*Function: originRotate
*Description: 以原点为中心进行旋转,临近插值,图片大小限制为原图的大小,超出范围的进行舍去
*Params: inArray - 原始图像的有效二维数组
* width - 图像宽度
* height - 图像高度
* angle - 角度
*Return: outArr - 结果图像的有效二维数组
************************************************************/
uint8_t** originRotate(uint8_t** inArray, uint32_t width, uint32_t height, double angle)
{
double Cos = cos(angle * PI / 180), Sin = sin(angle * PI / 180);
uint8_t** outArr = new uint8_t * [height];
for (uint32_t i = 0; i < height; i++) {
outArr[i] = new uint8_t[width];
}
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int xPoint = (int)(j * Cos + i * Sin + 0.5);
int yPoint = (int)(i * Cos - j * Sin + 0.5);
outArr[i][j] = isInArray(xPoint, yPoint, width, height) ? inArray[yPoint][xPoint] : 0;
}
}
return outArr;
}
/************************************************************
*Function: centreRotate
*Description: 围绕图片中心进行旋转,双线性插值,图片大小保持原图的完整性
*Params: inArray - 原始图像的有效二维数组
* width - 图像宽度
* height - 图像高度
* rotateWidth - 结果图片宽度
* rotateheight- 结果图片高度
* angle - 角度
*Return: outArr - 结果图像的有效二维数组
************************************************************/
uint8_t** centreRotate(uint8_t** inArray, uint32_t width, uint32_t height, uint32_t* rotateWidth, uint32_t* rotateheight, double angle)
{
double Cos = cos(angle * PI / 180), Sin = sin(angle * PI / 180);
int outWidth = width * Cos + height * Sin, outHeight = height * Cos + width * Sin;
*rotateWidth = outWidth;
*rotateheight = outHeight;
uint8_t** outArr = new uint8_t * [outHeight];
for (uint32_t i = 0; i < outHeight; i++) {
outArr[i] = new uint8_t[outWidth];
}
// 定义双线性插值相关变量
double x = 0, y = 0; // 定义 原始坐标对应位置
int x1 = 0, y1 = 0, x2 = 0, y2 = 0; // 对应范围的四个点的坐标(左上、右上、左下、右下)
int w1 = 0, w2 = 0, w3 = 0, w4 = 0; // 定义相关权值
uint8_t f11 = 0, f12 = 0, f21 = 0, f22 = 0; // 对应范围的四个点的像素值(左上、右上、左下、右下)
// 定义 坐标旋转之后的常量
double cx = -0.5 * outWidth * Cos - 0.5 * outHeight * Sin + 0.5 * width;
double cy = 0.5 * outWidth * Sin - 0.5 * outHeight * Cos + 0.5 * height;
for (int i = 0; i < outHeight; i++) {
for (int j = 0; j < outWidth; j++) {
x = Cos * j + Sin * i + cx; // 计算真实的坐标位置
y = Cos * i - Sin * j + cy;
x1 = (int)(x), x2 = (int)(x + 1); // 计算 对应范围的四个点的坐标,并转换为整型变量
y1 = (int)(y), y2 = (int)(y + 1);
// 得到 对应范围的四个点的像素值,当该点不在范围内的时候,默认为 0
f11 = isInArray(x1, y1, width, height) ? inArray[y1][x1] : 0;
f12 = isInArray(x1, y2, width, height) ? inArray[y2][x1] : 0;
f21 = isInArray(x2, y1, width, height) ? inArray[y1][x2] : 0;
f22 = isInArray(x2, y2, width, height) ? inArray[y2][x2] : 0;
// 扩大取整后,进行移位计算,节省时间
w1 = (int)((x - x1) * 2048), w2 = 2048 - w1;
w3 = (int)((y - y1) * 2048), w4 = 2048 - w3;
// 计算、限幅
outArr[i][j] = (f11 * w2 * w4 + f21 * w1 * w4 + f12 * w2 * w3 + f22 * w1 * w3) >> 22;
outArr[i][j] = Clipping(outArr[i][j], 0, 255);
}
}
return outArr;
}