RGB和YUV420旋转90/180/270度

1. 屏幕像素排列

像素点在屏幕上是按行和列来排列的,我们看到的屏幕的像素坐标系是这样的:

rotate.png

接下的旋转也会根据行列按屏幕像素坐标系来进行。本文以顺时针旋转4:3 的RGB图像为例。假设,有存储原RGB图像像素点的数组src, 存储旋转后的像素点的数组dst,两个数组长度一致。

2. 原图

按照屏幕的像素坐标系,坐标(y, x ) 的像素点即为第y行,第x列的像素点。进行旋转时可以根据原坐标(y, x )计算出新坐标(y', x'),将所有像素点的坐标逐个映射就可以得到旋转后的图像。

rotate0.png

3. 旋转90度

rotate90.png

对比原图和上图的像素排列可知,像素点的原坐标为(y, x),图像的height为3,width为4。旋转90度之后,新坐标为(x, height - y - 1),并且图像宽高也互换了,height为4,width为3。

因此,旋转90度就是将像素点从 src[y * width + x] 映射到 dst[x* height + height - y - 1]。

4. 旋转180度

rotate180.png

对比原图和上图的像素排列可知,像素点的原坐标为(y, x),图像的height为3,width为4。旋转180度之后,新坐标为(height - y - 1, width - x - 1),图像宽高不变。

因此,旋转180度就是将像素点从 src[y * width + x] 映射到 dst[ (height - y - 1) * width + width - x - 1]。

5. 旋转270度

270.png

对比原图和上图的像素排列可知,像素点的原坐标为(y, x),图像的height为3,width为4。旋转270度之后,新坐标为(width - x - 1, y),并且图像宽高也互换了,height为4,width为3。

因此,旋转270度就是将像素点从 src[y * width + x] 映射到 dst[ (width - x - 1) * height + y]。

6. RGB及RGBA旋转

RGB和RGBA的旋转原理是一样的,只不过RGB为3个字节(24位),RGBA为4个字节(32位),旋转时需要使用不同的步长。代码实现:

    static inline void
    rotateRGB90(unsigned char *src, unsigned char *dst, int width, int height, int bpp) {
        int dstIndex = 0;
        int srcIndex = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                //(y, x) -> (x, height - y - 1)
                //y * width + x, -> x* height + height - y - 1
                dstIndex = (x * height + height - y - 1) * bpp;
                for (int i = 0; i < bpp; i++) {
                    dst[dstIndex + i] = src[srcIndex++];
                }
            }
        }
    }

    static inline void rotateRGB90(int *src, int *dst, int width, int height) {
        int dstIndex = 0;
        int srcIndex = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                //(y, x) -> (x, height - y - 1)
                //y * width + x, -> x* height + height - y - 1
                dstIndex = x * height + height - y - 1;
                dst[dstIndex] = src[srcIndex++];
            }
        }
    }

    static inline void
    rotateRGB180(unsigned char *src, unsigned char *dst, int width, int height, int bpp) {
        int dstIndex = 0;
        int srcIndex = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                //(y, x) -> (height - y - 1, width - x - 1)
                //y * width + x, -> (height - y - 1) * width + width - x - 1
                dstIndex = ((height - y - 1) * width + width - x - 1) * bpp;
                for (int i = 0; i < bpp; i++) {
                    dst[dstIndex + i] = src[srcIndex++];
                }
            }
        }
    }

    static inline void rotateRGB180(int *src, int *dst, int width, int height) {
        int dstIndex = 0;
        int srcIndex = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                //(y, x) -> (height - y - 1, width - x - 1)
                //y * width + x, -> (height - y - 1) * width + width - x - 1
                dstIndex = (height - y - 1) * width + width - x - 1;
                dst[dstIndex] = src[srcIndex++];
            }
        }
    }

    static inline void
    rotateRGB270(unsigned char *src, unsigned char *dst, int width, int height, int bpp) {
        int dstIndex = 0;
        int srcIndex = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                //(y, x) -> (width - x - 1, y)
                //y * width + x, -> (width - x - 1) * height + y
                dstIndex = ((width - x - 1) * height + y) * bpp;
                for (int i = 0; i < bpp; i++) {
                    dst[dstIndex + i] = src[srcIndex++];
                }
            }
        }
    }

    static inline void rotateRGB270(int *src, int *dst, int width, int height) {
        int dstIndex = 0;
        int srcIndex = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                //(y, x) -> (width - x - 1, y)
                //y * width + x, -> (width - x - 1) * height + y
                dstIndex = (width - x - 1) * height + y;
                dst[dstIndex] = src[srcIndex++];
            }
        }
    }

    void rotateRGB(unsigned char *src, unsigned char *dst, int width, int height, float degree) {
        if (degree == 90.0f) {
            rotateRGB90(src, dst, width, height, 3);
        } else if (degree == 180.0f) {
            rotateRGB180(src, dst, width, height, 3);
        } else if (degree == 270.0f) {
            rotateRGB270(src, dst, width, height, 3);
        } else {
            return;
        }
    }

    void rotateRGBA(unsigned char *src, unsigned char *dst, int width, int height, float degree) {
        if (degree == 90.0f) {
            rotateRGB90(src, dst, width, height, 4);
        } else if (degree == 180.0f) {
            rotateRGB180(src, dst, width, height, 4);
        } else if (degree == 270.0f) {
            rotateRGB270(src, dst, width, height, 4);
        } else {
            return;
        }
    }

    void rotateRGBAInt(int *src, int *dst, int width, int height, float degree) {
        if (degree == 90.0f) {
            rotateRGB90(src, dst, width, height);
        } else if (degree == 180.0f) {
            rotateRGB180(src, dst, width, height);
        } else if (degree == 270.0f) {
            rotateRGB270(src, dst, width, height);
        } else {
            return;
        }
    }

7. YUV420旋转

YUV420分为YUV420P和YUV420SP。YUV420P每个像素的Y、U、V通道分别连续存储,根据UV通道顺序不同又分为I420和YV12。YUV420SP每个像素的Y通道连续存储,UV通道交替存储,根据UV通道顺序不同又分为NV12和NV21。如果你对YUV420不是很了解,请戳:YUV_420_888介绍及YUV420转RGBA

7.1 YUV420P旋转

YUV420P的Y、U、V通道按4:1:1分别存储在不同矩阵中,并且Y的数量和像素点数量相同。因此我们只要参考RGB的旋转,先旋转Y,再旋转U、V就能完成YUV420P的旋转。由于旋转时我们并不关心U和V顺序,所以I420和YV12旋转可以用同一种方法。

    static inline void
    rotateYUV420P90(unsigned char *srcY, unsigned char *srcU, unsigned char *srcV,
                    unsigned char *dstY, unsigned char *dstU, unsigned char *dstV,
                    int width, int height) {
        //rotate y
        int dstIndex = 0;
        int srcIndex = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                dstIndex = x * height + height - y - 1;
                dstY[dstIndex] = srcY[srcIndex++];
            }
        }

        //rotate uv
        int uvHeight = height / 2;
        int uvWidth = width / 2;
        int dstUVIndex = 0;
        int srcUVIndex = 0;
        for (int y = 0; y < uvHeight; y++) {
            for (int x = 0; x < uvWidth; x++) {
                dstUVIndex = x * uvHeight + uvHeight - y - 1;
                dstU[dstUVIndex] = srcU[srcUVIndex];
                dstV[dstUVIndex] = srcV[srcUVIndex];
                srcUVIndex++;
            }
        }
    }

    static inline void
    rotateYUV420P180(unsigned char *srcY, unsigned char *srcU, unsigned char *srcV,
                     unsigned char *dstY, unsigned char *dstU, unsigned char *dstV,
                     int width, int height) {
        //rotate y
        int dstIndex = 0;
        int srcIndex = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                dstIndex = (height - y - 1) * width + width - x - 1;
                dstY[dstIndex] = srcY[srcIndex++];
            }
        }

        //rotate uv
        int uvHeight = height / 2;
        int uvWidth = width / 2;
        int dstUVIndex = 0;
        int srcUVIndex = 0;
        for (int y = 0; y < uvHeight; y++) {
            for (int x = 0; x < uvWidth; x++) {
                dstUVIndex = (uvHeight - y - 1) * uvWidth + uvWidth - x - 1;
                dstU[dstUVIndex] = srcU[srcUVIndex];
                dstV[dstUVIndex] = srcV[srcUVIndex];
                srcUVIndex++;
            }
        }
    }

    static inline void
    rotateYUV420P270(unsigned char *srcY, unsigned char *srcU, unsigned char *srcV,
                     unsigned char *dstY, unsigned char *dstU, unsigned char *dstV,
                     int width, int height) {
        //rotate y
        int dstIndex = 0;
        int srcIndex = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                dstIndex = (width - x - 1) * height + y;
                dstY[dstIndex] = srcY[srcIndex++];
            }
        }

        //rotate uv
        int uvHeight = height / 2;
        int uvWidth = width / 2;
        int dstUVIndex = 0;
        int srcUVIndex = 0;
        for (int y = 0; y < uvHeight; y++) {
            for (int x = 0; x < uvWidth; x++) {
                dstUVIndex = (uvWidth - x - 1) * uvHeight + y;
                dstU[dstUVIndex] = srcU[srcUVIndex];
                dstV[dstUVIndex] = srcV[srcUVIndex];
                srcUVIndex++;
            }
        }
    }

    void
    rotateYUV420P(unsigned char *src, unsigned char *dst, int width, int height, float degree) {
        unsigned char *pSrcY = src;
        unsigned char *pSrcU = src + width * height;
        unsigned char *pSrcV = src + width * height / 4 * 5;

        unsigned char *pDstY = dst;
        unsigned char *pDstU = dst + width * height;
        unsigned char *pDstV = dst + width * height / 4 * 5;

        if (degree == 90.0f) {
            rotateYUV420P90(pSrcY, pSrcU, pSrcV, pDstY, pDstU, pDstV, width, height);
        } else if (degree == 180.0f) {
            rotateYUV420P180(pSrcY, pSrcU, pSrcV, pDstY, pDstU, pDstV, width, height);
        } else if (degree == 270.0f) {
            rotateYUV420P270(pSrcY, pSrcU, pSrcV, pDstY, pDstU, pDstV, width, height);
        } else {
            return;
        }
    }

需要注意的是,如果是旋转90度或者270度,由于旋转后宽和高互换了,旋转后转换RGB时yRowStride和uvRowStride也要相应的由 width 和 width/2 改为 height 和 height/2。

if (rotationDegree == 90.0f || rotationDegree == 270.0f) {
    NativeUtils.I420ToRGBAInt(rotatedYUV420, rgba, height, width, height, height / 2, 1);
    ImageUtils.RGBAToJPEG(context, rgba, height, width, parent, fileName);
} else if (rotationDegree == 180.0f) {
    NativeUtils.I420ToRGBAInt(rotatedYUV420, rgba, width, height, width, width / 2, 1);
    ImageUtils.RGBAToJPEG(context, rgba, width, height, parent, fileName);
} else {
    NativeUtils.I420ToRGBAInt(yuv420, rgba, width, height, width, width / 2, 1);
    ImageUtils.RGBAToJPEG(context, rgba, width, height, parent, fileName);
}

7.2 YUV420SP旋转

与YUV420P一样,YUV420SP的旋转,也是先旋转Y,再旋转U、V。旋转时也不关心U和V顺序,NV12和NV21可以用同一种方法。
区别在于YUV420SP的UV通道是交替存储的,所以我们像RGB旋转时一样,设置一个步长2。

    static inline void
    rotateYUV420SP90(unsigned char *srcY, unsigned char *srcUV, unsigned char *dstY,
                     unsigned char *dstUV, int width, int height) {
        //rotate y
        int dstIndex = 0;
        int srcIndex = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                dstIndex = x * height + height - y - 1;
                dstY[dstIndex] = srcY[srcIndex++];
            }
        }

        //rotate uv
        int uvHeight = height >> 1;
        int uvWidth = width >> 1;
        int dstUVIndex = 0;
        int srcUVIndex = 0;
        for (int y = 0; y < uvHeight; y++) {
            for (int x = 0; x < uvWidth; x++) {
                dstUVIndex = (x * uvHeight + uvHeight - y - 1) << 1;
                dstUV[dstUVIndex] = srcUV[srcUVIndex++];
                dstUV[dstUVIndex + 1] = srcUV[srcUVIndex++];
            }
        }
    }

    static inline void
    rotateYUV420SP180(unsigned char *srcY, unsigned char *srcUV, unsigned char *dstY,
                      unsigned char *dstUV, int width, int height) {
        //rotate y
        int dstIndex = 0;
        int srcIndex = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                dstIndex = (height - y - 1) * width + width - x - 1;
                dstY[dstIndex] = srcY[srcIndex++];
            }
        }

        //rotate uv
        int uvHeight = height >> 1;
        int uvWidth = width >> 1;
        int dstUVIndex = 0;
        int srcUVIndex = 0;
        for (int y = 0; y < uvHeight; y++) {
            for (int x = 0; x < uvWidth; x++) {
                dstUVIndex = ((uvHeight - y - 1) * uvWidth + uvWidth - x - 1) << 1;
                dstUV[dstUVIndex] = srcUV[srcUVIndex++];
                dstUV[dstUVIndex + 1] = srcUV[srcUVIndex++];
            }
        }
    }

    static inline void
    rotateYUV420SP270(unsigned char *srcY, unsigned char *srcUV, unsigned char *dstY,
                      unsigned char *dstUV, int width, int height) {
        //rotate y
        int dstIndex = 0;
        int srcIndex = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                dstIndex = (width - x - 1) * height + y;
                dstY[dstIndex] = srcY[srcIndex++];
            }
        }

        //rotate uv
        int uvHeight = height >> 1;
        int uvWidth = width >> 1;
        int dstUVIndex = 0;
        int srcUVIndex = 0;
        for (int y = 0; y < uvHeight; y++) {
            for (int x = 0; x < uvWidth; x++) {
                dstUVIndex = ((uvWidth - x - 1) * uvHeight + y) << 1;
                dstUV[dstUVIndex] = srcUV[srcUVIndex++];
                dstUV[dstUVIndex + 1] = srcUV[srcUVIndex++];
            }
        }
    }

    void
    rotateYUV420SP(unsigned char *src, unsigned char *dst, int width, int height, float degree) {
        unsigned char *pSrcY = src;
        unsigned char *pSrcUV = src + width * height;

        unsigned char *pDstY = dst;
        unsigned char *pDstUV = dst + width * height;

        if (degree == 90.0f) {
            rotateYUV420SP90(pSrcY, pSrcUV, pDstY, pDstUV, width, height);
        } else if (degree == 180.0f) {
            rotateYUV420SP180(pSrcY, pSrcUV, pDstY, pDstUV, width, height);
        } else if (degree == 270.0f) {
            rotateYUV420SP270(pSrcY, pSrcUV, pDstY, pDstUV, width, height);
        } else {
            return;
        }
    }

猜你喜欢

转载自blog.csdn.net/qq_21743659/article/details/129531770