欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

RGB 转换灰度图像原理

程序员文章站 2022-07-14 16:36:49
...
#include <cv.h>
#include <highgui.h>

int main(){
         cv::Mat src= cv::imread("C:\\Users\\Poplar\\Pictures\\ff.jpg");
         cv::Mat grey(src.rows, src.cols, CV_8UC1, Scalar(0));
         for (inty = 0; y < src.rows; y++)
         {
                   uchar*cp = src.ptr<uchar>(y);
                   uchar*gp = grey.ptr<uchar>(y);
                   for(int x = 0; x < src.cols; x++){
                            *gp= (15*cp[0] + 75*cp[1] + 38*cp[2]) >> 7;
                            cp+= 3;
                            gp++;
                   }
         }
         cv::imshow("src",src);
         cv::imshow("grey",grey);
         cv::waitKey(0);
         return0;
}
        以上代码是在OPENCV中,RGB图像转换为灰度图像的实现方式。

RGB彩色图像中,一种彩色由R(红色),G(绿色),B(蓝色)三原色按比例混合而成。

图像的基本单元是一个像素,一个像素需要3块表示,分别代表R,G,B,如果8为表示一个颜色,就由0-255区分不同亮度的某种原色。

一张9像素的8位RGB图像,在计算机内存中的分布大概示意如下:

RGB 转换灰度图像原理

实际中数都是二进制形式的,并且未必按照R,G,B顺序,比如OpenCV是按照B,G,R顺序将三个色值保存在3个连续的字节里

        灰度图像是用不同饱和度的黑色来表示每个图像点,比如用8位 0-255数字表示“灰色”程度,每个像素点只需要一个灰度值,8位即可,这样一个3X3的灰度图,只需要9个byte就能保存

        RGB值和灰度的转换,实际上是人眼对于彩色的感觉到亮度感觉的转换,这是一个心理学问题,有一个公式:

Grey = 0.299*R + 0.587*G + 0.114*B

        根据这个公式,依次读取每个像素点的RGB值,进行计算灰度值(转换为整型数),将灰度值赋值给新图像的相应位置,所有像素点遍历一遍后完成转换。一张500X500的图像转换为同样大小的灰度图需要进行25万次上述公式的计算。进行优化是很有必要的,这个简单的算法O(n)复杂度的,应该是不能优化了(或者用并行进行优化,本文不涉及),但是Grey = 0.299*R + 0.587*G + 0.114*B有更加高效的等价形式。可以通过将浮点数运算转化为整数运算,整数运算转换为位操作进行优化。

Grey = 0.299*R + 0.587*G + 0.114*B

可以转化为

Grey = (299*R + 587*G + 114*B + 500) /1000

整数运算会截断小数部分,加上500是为了四舍五入(找两个例子便可理解),减少精度损失。

这里的除法即使是整数除法计算也是很耗时,转换为移位操作可以优化,那么怎么转换为位操作?左右移位对应于乘除2的幂,为了把除法转为右移操作,做如下处理:

Grey = 0.299*R + 0.587*G + 0.114*B

Grey = 299*R+ 587*G + 114*B÷ 1000

Grey = 1024*299*R+ 1024*587*G + 1024*114*B÷1024*1000

Grey = 306176*R+601088*G + 116736*B÷1024*1000

Grey = 306.176*R+601.088*G + 116.736*B÷1024

Grey = 306*R+601*G + 116*B÷1024//截断误差

Grey = 306*R+601*G + 116*B >> 10;

误差最大是多少?

(0.176*255+0.088*255 + 0.736*255) ÷1024 = 255÷1024=0.249,可能会导致1个灰度值的波动

有一种计算方法可以降低误差

的系数  =1024*0.229= 306.176306

G的系数   =1024*0.587 + 0.176 =601.264 ≈601

B的系数   =1024*0.114 + 0.264 = 117

保留了小数部分的作用,可以得到一个误差较小的公式:

Grey = 306*R +601*G + 117*B >> 10;

这样得来的是10位精度的。

同样的方法可以获得其他精度的,比如

Grey = (R*1 + G*2 + B*1) >> 2  ( Grey = (R + G<<1 + B) >> 2 )

Grey= (R*38 + G*75 + B*15) >> 7

Grey= (R*76 + G*150 + B*30) >> 8

Grey = (R*19595 + G*38469 + B*7472) >> 16

可以看出来,7位和8位精度是一样的,比较好用的是7位精度的公式。

 

        实际编写代码时,还要考虑图像文件的读取问题,不同格式的RGB位图,结构不同,读取时也不同,本文不涉及图像读取问题,这里以openCV提供的图像读取方式,展示转灰度图的实际代码,见文章开头。

 2-10位精度的公式

Grey = (R*1 + G*2 + B*1) >> 2

Grey= (R*2 + G*5 + B*1) >> 3

Grey= (R*4 + G*10 + B*2) >> 4

Grey = (R*9 + G*19 + B*4) >> 5

Grey = (R*19 + G*37 + B*8) >> 6

Grey= (R*38 + G*75 + B*15) >> 7

Grey= (R*76 + G*150 + B*30) >> 8

Grey = (R*153 + G*300 + B*59) >> 9

Grey = (R*306 + G*601 + B*117) >> 10

Grey = (R*612 + G*1202 + B*234) >> 11

Grey = (R*1224 + G*2405 + B*467) >> 12

Grey= (R*2449 + G*4809 + B*934) >> 13

Grey= (R*4898 + G*9618 + B*1868) >> 14

Grey = (R*9797 + G*19235 + B*3736) >> 15

Grey = (R*19595 + G*38469 + B*7472) >> 16

Grey = (R*39190 + G*76939 + B*14943) >> 17

Grey = (R*78381 + G*153878 + B*29885) >> 18

Grey =(R*156762 + G*307757 + B*59769) >> 19

Grey= (R*313524 + G*615514 + B*119538) >> 20

相关标签: opencv RGB