FFmpeg之yuv旋转(十九)

48 阅读2分钟
  • 原始数据第 1 行第 width - 1 列的像素点将会变成目标数据第 0 行第 1 列的像素点

  • 原始数据第 1 行第 width - 2 列的像素点将会变成目标数据第 1 行第 1 列的像素点

  • 原始数据第 i 行第 j 列的像素点将会变成目标数据第 width - j - 1 行第 i 列的像素点

二、 旋转 BGR24 / RGB24 数据

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数据

对于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数据

对于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 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

  •                          ->    旋转后的画面脑补下