美文网首页
RGB和YUV420旋转90/180/270度

RGB和YUV420旋转90/180/270度

作者: qiuxintai | 来源:发表于2020-05-12 11:02 被阅读0次

    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;
            }
        }
    

    同样需要注意的是,如果是旋转90度或者270度,旋转后宽和高互换了,转换RGB时需要使用旋转后的宽高。

    测试效果:
    测试效果.png

    本文中的代码已经上传到github:https://github.com/qiuxintai/YUV420Converter

    8. 本文参考

    https://www.jianshu.com/p/7e602dea3ca1
    感谢原作者的辛勤付出。

    相关文章

      网友评论

          本文标题:RGB和YUV420旋转90/180/270度

          本文链接:https://www.haomeiwen.com/subject/jmovnhtx.html