一、 按像素点旋转图像
假设有以下一张图像:
Pixel1 Pixel2 Pixel3 Pixel4
Pixel5 Pixel6 Pixel7 Pixel8
其图像分辨率是width x height
- 在顺时针旋转90度后,其内容将会变成:
Pixel5 Pixel1
Pixel6 Pixel2
Pixel7 Pixel3
Pixel8 Pixel4
分辨率变成了height x width
也就是:
- 原始数据第 height - 1 行第 0 列的像素点将会变成目标数据第 0 行第 0 列的像素点
- 原始数据第 height - 1 行第 1 列的像素点将会变成目标数据第 1 行第 0 列的像素点
- …
- 原始数据第 height - 1 行第 width - 1 列的像素点将会变成目标数据第 width - 1 行第 0 列的像素点
- …
- 原始数据第 1 行第 width - 1 列的像素点将会变成目标数据第 width - 1 行第 height - 2 列的像素点
- 原始数据第 1 行第 width - 2 列的像素点将会变成目标数据第 width - 2 行第 height - 2 列的像素点
- 原始数据第 1 行第 width - 3 列的像素点将会变成目标数据第 width - 3 行第 height - 2 列的像素点
- …
- 原始数据第 i 行第 j 列的像素点将会变成目标数据第 j 行第 height - 1 - i 列的像素点
2. 若旋转**180**度,其内容将会变成: >Pixel8 Pixel7 Pixel6 Pixel5
Pixel4 Pixel3 Pixel2 Pixel1
也就是
- 原始数据第 0 行第 0 列的像素点将会变成目标数据第 height - 1 行第 width - 1 列的像素点
- 原始数据第 1 行第 0 列的像素点将会变成目标数据第 height - 2 行第 width - 1 列的像素点
- …
- 原始数据第 height - 1 行第 0 列的像素点将会变成目标数据第 0 行第 width - 1 列的像素点
- 原始数据第 height - 1 行第 1 列的像素点将会变成目标数据第 0 行第 width - 2 列的像素点
- …
- 原始数据第 i 行第 j 列的像素点将会变成目标数据第 height - 1 - i 行第 width - 1 - j 列的像素点
3. 若旋转**270**度,其内容将会变成: >Pixel4 Pixel8
Pixel3 Pixel7
Pixel2 Pixel6
Pixel1 Pixel5
也就是:
- 原始数据第 0 行第 width - 1 列的像素点将会变成目标数据第 0 行第 0 列的像素点
- 原始数据第 0 行第 width - 2 列的像素点将会变成目标数据第 1 行第 0 列的像素点
- …
- 原始数据第 0 行第 0 列的像素点将会变成目标数据第 width - 1 行第 0 列的像素点
- …
- 原始数据第 1 行第 width - 1 列的像素点将会变成目标数据第 0 行第 1 列的像素点
- 原始数据第 1 行第 width - 2 列的像素点将会变成目标数据第 1 行第 1 列的像素点
- …
- 原始数据第 i 行第 j 列的像素点将会变成目标数据第 width - j - 1 行第 i 列的像素点
BGR24 / RGB24 都是以3个byte作为一个像素,因此在旋转时需要将3个byte作为一个整体进行旋转。示例代码如下。
- 旋转90度:
void rotateRgb24Degree90(char *rgb24, char *rotatedRgb24, int width, int height) {
int lineDataSize = width * 3;
int rotatedRgb24Index = 0;
int finalLineStartIndex = (height - 1) * lineDataSize;
for (int w = 0; w < lineDataSize; w += 3) {
int bgr24StartIndex = finalLineStartIndex + w;
int offset = 0;
for (int h = 0; h < height; h++) {
rotatedRgb24[rotatedRgb24Index++] = rgb24[bgr24StartIndex - offset];
rotatedRgb24[rotatedRgb24Index++] = rgb24[bgr24StartIndex - offset + 1];
rotatedRgb24[rotatedRgb24Index++] = rgb24[bgr24StartIndex - offset + 2];
offset += lineDataSize;
}
}
}
- 旋转180度
void rotateRgb24Degree180(char *rgb24, char *rotatedRgb24, int width, int height) {
int lineDataSize = width * 3;
int rotatedRgb24Index = 0;
int rgb24StartIndex = lineDataSize * height - 3;
for (int h = height - 1; h >= 0; h--) {
for (int w = lineDataSize - 3; w >= 0; w -= 3) {
rotatedRgb24[rotatedRgb24Index++] = rgb24[rgb24StartIndex];
rotatedRgb24[rotatedRgb24Index++] = rgb24[rgb24StartIndex + 1];
rotatedRgb24[rotatedRgb24Index++] = rgb24[rgb24StartIndex + 2];
rgb24StartIndex -= 3;
}
}
}
- 旋转270度
void rotateRgb24Degree270(char *rgb24, char *rotatedRgb24, int width, int height) {
int lineDataSize = width * 3;
int rotatedRgb24Index = 0;
int finalColumnStartIndex = lineDataSize;
for (int w = 0; w < lineDataSize; w += 3) {
finalColumnStartIndex -= 3;
int offset = 0;
for (int h = 0; h < height; h++) {
rotatedRgb24[rotatedRgb24Index++] = rgb24[finalColumnStartIndex + offset];
rotatedRgb24[rotatedRgb24Index++] = rgb24[finalColumnStartIndex + offset + 1];
rotatedRgb24[rotatedRgb24Index++] = rgb24[finalColumnStartIndex + offset + 2];
offset += lineDataSize;
}
}
}
对于NV21或NV12数据,其排列是width * height个Y连续存储,接下来是 height / 2 行的UV数据,每一行的UV数据是 width / 2 个U和 width / 2 个V交叉存储(NV21是VU VU VU VU…,NV12是UV UV UV UV …),Y数据的大小刚好就是像素数,可以直接进行旋转,对于U和V数据,需要考虑下标的跳跃情况(由于NV21和NV12的U和V只是刚好位置相反,因此旋转NV21的代码也同样适用于旋转NV12)。示例代码如下。
- 旋转90度
void rotateNv21Degree90(char *nv21, char *rotatedNv21, int width, int height) {
int yFinalLineStartIndex = (height - 1) * width;
int rotatedYIndex = 0;
//rotate y
for (int w = 0; w < width; w++) {
int yStartIndex = yFinalLineStartIndex + w;
int offset = 0;
for (int h = 0; h < height; h++) {
rotatedNv21[rotatedYIndex++] = nv21[yStartIndex - offset];
offset += width;
}
}
//rotate uv
int uvFinalLineStartIndex = width * height * 3 / 2 - width;
int rotatedVIndex = width * height;
int rotatedUIndex = width * height + 1;
for (int w = 0; w < width; w += 2) {
int uvStartIndex = uvFinalLineStartIndex + w;
int offset = 0;
for (int h = 0; h < height; h += 2) {
rotatedNv21[rotatedVIndex] = nv21[uvStartIndex - offset];
rotatedNv21[rotatedUIndex] = nv21[uvStartIndex - offset + 1];
offset += width;
rotatedVIndex += 2;
rotatedUIndex += 2;
}
}
}
- 旋转180度
void rotateNv21Degree180(char *nv21, char *rotatedNv21, int width, int height) {
int yIndex = width * height - 1;
int rotatedYIndex = 0;
//rotate y
for (int h = height - 1; h >= 0; h--) {
for (int w = width - 1; w >= 0; w--) {
rotatedNv21[rotatedYIndex++] = nv21[yIndex];
yIndex--;
}
}
int uvIndex = width * height * 3 / 2 - 2;
int rotatedVIndex = width * height;
int rotatedUIndex = width * height + 1;
//rotate uv
for (int h = height - 1; h >= 0; h -= 2) {
for (int w = width - 1; w >= 0; w -= 2) {
rotatedNv21[rotatedVIndex] = nv21[uvIndex];
rotatedNv21[rotatedUIndex] = nv21[uvIndex + 1];
uvIndex -= 2;
rotatedVIndex += 2;
rotatedUIndex += 2;
}
}
}
- 旋转270度
void rotateNv21Degree270(char *nv21, char *rotatedNv21, int width, int height) {
int rotatedYIndex = 0;
int yFinalColumnStartIndex = width;
//rotate y
for (int w = 0; w < width; w++) {
int offset = 0;
for (int h = 0; h < height; h++) {
rotatedNv21[rotatedYIndex++] = nv21[yFinalColumnStartIndex + offset];
offset += width;
}
yFinalColumnStartIndex--;
}
//rotate uv
int uvFinalColumnStartIndex = width * height + width;
int rotatedVIndex = width * height;
int rotatedUIndex = width * height + 1;
for (int w = 0; w < width; w += 2) {
uvFinalColumnStartIndex -= 2;
int offset = 0;
for (int h = 0; h < height; h += 2) {
rotatedNv21[rotatedVIndex] = nv21[uvFinalColumnStartIndex + offset];
rotatedNv21[rotatedUIndex] = nv21[uvFinalColumnStartIndex + offset + 1];
offset += width;
rotatedVIndex += 2;
rotatedUIndex += 2;
}
}
}
对于I420、YV12数据,其排列是width * height个Y连续存储,接下来是连续的U和V或连续的V和U(I420是UUUUUUUU…VVVVVVVV…,YV12是VVVVVVVV…UUUUUUUU…),Y数据的大小刚好就是像素数,可以直接进行旋转;因为U和V的宽高都只有Y的宽高的一半,所以宽高的循环数各只有Y的一半(由于I420和YV12的U和V只是刚好位置相反,因此旋转I420的代码也同样适用于旋转YV12)。示例代码如下。
- 旋转90度
void rotateI420Degree90(char *i420, char *rotatedI420, int width, int height) {
int halfWidth = width / 2;
int yFinalLineStartIndex = (height - 1) * width;
int rotatedYIndex = 0;
//rotate y
for (int w = 0; w < width; w++) {
int yStartIndex = yFinalLineStartIndex + w;
int offset = 0;
for (int h = 0; h < height; h++) {
rotatedI420[rotatedYIndex++] = i420[yStartIndex - offset];
offset += width;
}
}
//rotate uv
int uFinalLineStartIndex = width * height * 5 / 4 - halfWidth;
int vFinalLineStartIndex = width * height * 3 / 2 - halfWidth;
int rotatedUIndex = width * height;
int rotatedVIndex = width * height * 5 / 4;
for (int w = 0; w < width; w += 2) {
int uStartIndex = uFinalLineStartIndex + w / 2;
int vStartIndex = vFinalLineStartIndex + w / 2;
int offset = 0;
for (int h = 0; h < height; h += 2) {
rotatedI420[rotatedUIndex++] = i420[uStartIndex - offset];
rotatedI420[rotatedVIndex++] = i420[vStartIndex - offset];
offset += halfWidth;
}
}
}
- 旋转180度
void rotateI420Degree180(char *i420, char *rotatedI420, int width, int height) {
int yIndex = width * height - 1;
int rotatedYIndex = 0;
//rotate y
for (int h = height - 1; h >= 0; h--) {
for (int w = width - 1; w >= 0; w--) {
rotatedI420[rotatedYIndex++] = i420[yIndex];
yIndex--;
}
}
int uIndex = width * height * 5 / 4 - 1;
int vIndex = width * height * 3 / 2 - 1;
int rotatedUIndex = width * height;
int rotatedVIndex = width * height * 5 / 4;
//rotate uv
for (int h = height - 1; h >= 0; h -= 2) {
for (int w = width - 1; w >= 0; w -= 2) {
rotatedI420[rotatedUIndex++] = i420[uIndex--];
rotatedI420[rotatedVIndex++] = i420[vIndex--];
}
}
}
- 旋转270度
void rotateI420Degree270(char *i420, char *rotatedI420, int width, int height) {
int halfWidth = width / 2;
int yFinalColumnStartIndex = width;
int rotatedYIndex = 0;
//rotate y
for (int w = 0; w < width; w++) {
int offset = 0;
for (int h = 0; h < height; h++) {
rotatedI420[rotatedYIndex++] = i420[yFinalColumnStartIndex + offset];
offset += width;
}
yFinalColumnStartIndex--;
}
//rotate uv
int uFinalColumnStartIndex = width * height + width;
int vFinalColumnStartIndex = width * height * 5 / 4 + width;
int rotatedUIndex = width * height;
int rotatedVIndex = width * height * 5 / 4;
for (int w = 0; w < width; w += 2) {
uFinalColumnStartIndex--;
vFinalColumnStartIndex--;
int offset = 0;
for (int h = 0; h < height; h += 2) {
rotatedI420[rotatedUIndex++] = i420[uFinalColumnStartIndex + offset];
rotatedI420[rotatedVIndex++] = i420[vFinalColumnStartIndex + offset];
offset += halfWidth;
}
}
}
由于YUYV的排列方式是(YUYV YUYV YUYV …),其共用关系是每2个横向相邻的Y会使用同一组U和V,因此,在旋转180度时,YUV的共用关系可以不被打破,只是更改每4个byte中的2个Y的顺序;但是在旋转90度或270度时,由于原来横向的Y将被修改为纵向,YUV的共用关系也将被打破。示例代码如下。
- 旋转90度
void rotateYuyvDegree90(char *yuyv, char *rotatedYuyv, int width, int height) {
int lineDataSize = width * 2;
int rotatedLineDataSize = height * 2;
int rotatedYuyvIndex = 0;
int finalLineStartIndex = (height - 2) * lineDataSize;
for (int w = 0; w < lineDataSize; w += 4) {
int yuyvStartIndex = finalLineStartIndex + w;
int offset = 0;
for (int h = 0; h < height; h += 2) {
/**
* y1 u1 y2 v2 y3 u2 y4 v2
* -> 旋转后的画面脑补下
* y5 u3 y6 v3 y7 u4 y8 v4
*/
//y5
rotatedYuyv[rotatedYuyvIndex] = yuyv[yuyvStartIndex - offset + lineDataSize];
//u3
rotatedYuyv[rotatedYuyvIndex + 1] = yuyv[yuyvStartIndex - offset + lineDataSize + 1];
//y1
rotatedYuyv[rotatedYuyvIndex + 2] = yuyv[yuyvStartIndex - offset];
//v3
rotatedYuyv[rotatedYuyvIndex + 3] = yuyv[yuyvStartIndex - offset + lineDataSize + 3];
//y6
rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize] = yuyv[yuyvStartIndex + lineDataSize - offset + 2];
//u1
rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize + 1] = yuyv[yuyvStartIndex - offset + 1];
//y2
rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize + 2] = yuyv[yuyvStartIndex - offset + 2];
//v1
rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize + 3] = yuyv[yuyvStartIndex - offset + 3];
rotatedYuyvIndex += 4;
offset += lineDataSize * 2;
}
rotatedYuyvIndex += rotatedLineDataSize;
}
}
- 旋转180度
void rotateYuyvDegree180(char *yuyv, char *rotatedYuyv, int width, int height) {
int lineDataSize = width * 2;
int yuyvIndex = lineDataSize * height - 4;
int rotatedIndex = 0;
//rotate
for (int h = height - 1; h >= 0; h--) {
for (int w = lineDataSize - 4; w >= 0; w -= 4) {
rotatedYuyv[rotatedIndex++] = yuyv[yuyvIndex + 2];
rotatedYuyv[rotatedIndex++] = yuyv[yuyvIndex + 1];
rotatedYuyv[rotatedIndex++] = yuyv[yuyvIndex];
rotatedYuyv[rotatedIndex++] = yuyv[yuyvIndex + 3];
yuyvIndex -= 4;
}
}
}
- 旋转270度
void rotateYuyvDegree270(char *yuyv, char *rotatedYuyv, int width, int height) {
int lineDataSize = width * 2;
int rotatedLineDataSize = height * 2;
int rotatedYuyvIndex = 0;
int finalColumnStartIndex = lineDataSize - 4;
for (int w = 0; w < lineDataSize; w += 4) {
int offset = 0;
for (int h = 0; h < height; h += 2) {
/**
* y1 u1 y2 v1 y3 u2 y4 v2
* -> 旋转后的画面脑补下
* y5 u3 y6 v3 y7 u4 y8 v4
*/
//y4
rotatedYuyv[rotatedYuyvIndex] = yuyv[finalColumnStartIndex + offset + 2];
//u2
rotatedYuyv[rotatedYuyvIndex + 1] = yuyv[finalColumnStartIndex + offset + 1];
//y8
rotatedYuyv[rotatedYuyvIndex + 2] = yuyv[finalColumnStartIndex + offset + lineDataSize +
2];
//v2
rotatedYuyv[rotatedYuyvIndex + 3] = yuyv[finalColumnStartIndex + offset + 3];
//y3
rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize] = yuyv[finalColumnStartIndex + offset];
//u4
rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize + 1] = yuyv[finalColumnStartIndex + lineDataSize + offset + 1];
//y7
rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize + 2] = yuyv[finalColumnStartIndex + lineDataSize + offset];
//v4
rotatedYuyv[rotatedYuyvIndex + rotatedLineDataSize + 3] = yuyv[finalColumnStartIndex + lineDataSize + offset + 3];
rotatedYuyvIndex += 4;
offset += lineDataSize * 2;
}
finalColumnStartIndex -= 4;
rotatedYuyvIndex += rotatedLineDataSize;
}
}