分享

RGB图像转为灰度图

 我心依旧quf9ox 2020-05-14

最后结论:

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

代码

  1. #include <cv.h>
  2. #include <highgui.h>
  3. using namespace cv;
  4. int main(){
  5. Mat src= imread('C:\\Users\\Poplar\\Pictures\\ff.jpg');
  6. Mat grey(src.rows, src.cols, CV_8UC1, Scalar(0));
  7. for (inty = 0; y < src.rows; y++)
  8. {
  9. uchar*cp = src.ptr<uchar>(y);
  10. uchar*gp = grey.ptr<uchar>(y);
  11. for(int x = 0; x < src.cols; x++){
  12. *gp= (15*cp[0] + 75*cp[1] + 38*cp[2]) >> 7;
  13. cp+= 3;
  14. gp++;
  15. }
  16. }
  17. imshow('src',src);
  18. imshow('grey',grey);
  19. waitKey(0);
  20. return0;
  21. }

效果如图


下面具体解释

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

图像的基本单元是一个像素,就像一个巨幅电子广告屏上远处看是衣服图像,走近你会看到一个一个的方格,这个方格的颜色是一种,从远处看,觉察不到这个方格的存在。

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

一张9像素的8位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有更加高效的等价形式。

ALU中,位操作快于整数加法,整数加法快于整数乘法(快多少取决于有没有乘法电路,乘法电路的结构),整数运算又比浮点数运算快得多。

所以可以通过将浮点数运算转化为整数运算,整数运算转换为位操作进行优化

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个灰度值的波动

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

R 的系数  =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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多