昨天才更新了两篇博客,今天又要更新了,并不是我垃圾产,只不过这些在上个月就已经写好了,只是因为比赛忙,一直腾不出时间整理出来发表而已,但是做完一件事情总感觉不写一博文总结一下就少点什么,所以之后的一段时间里我会把我这学期学到的一些东西陆续整理出来发表,给自己一个总结交代。

  将彩色图像转化为灰度的方法有两种,一个是令RGB三个分量的数值相等,输出后便可以得到灰度图像,另一种是转化为YCbCr格式,将Y分量提取出来,YCbCr格式中的Y分量表示的是图像的亮度和浓度所以只输出Y分量,得到的图像就是灰度图像了。我在这里选择第二种方法实现。

  YCBCr是通过有序的三元组来表示的,三元由Y(Luminance)、Cb(Chrominance-Blue)和Cr(Chrominance-Red)组成,其中Y表示颜色的明亮度和浓度,而Cb和Cr则分别表示颜色的蓝色浓度偏移量和红色浓度偏移量。人的肉眼对由YCbCr色彩空间编码的视频中的Y分量更敏感,而Cb和Cr的微小变化不会引起视觉上的不同,根据该原理,通过对Cb和Cr进行子采样来减小图像的数据量,使得图像对存储需求和传输带宽的要求大大降低,从而达到在完成图像压缩的同时也保证了视觉上几乎没有损失的效果,进而使得图像的传输速度更快,存储更加方便。我们要的到灰度图像,首先要将采集到的彩色图像转化为YCbCr。

  我们配置摄像头采集到的数据是RGB565的格式,官方给出的转化公式是RGB888->YCbCr,所以先需要将RGB565转化为RGB888,转化方法如下:

  24bit RGB888 -> 16bit RGB565 的转换(只取高位)

  24ibt RGB888 {R7 R6 R5 R4 R3 R2 R1 R0} {G7 G6 G5 G4 G3 G2 G1 G0} {B7 B6 B5 B4 B3 B2 B1 B0}

  16bit RGB656 {R7 R6 R5 R4 R3} {G7 G6 G5 G4 G3 G2} {B7 B6 B5 B4 B3}

  同样也可以恢复回去。

  16bit RGB565 -> 24bit RGB888 的转换(高位补低位)

  16bit RGB656 {R4 R3 R2 R1 R0} {G5 G4 G3 G2 G1 G0} {B4 B3 B2 B1 B0}

  24ibt RGB888 {R4 R3 R2 R1 R0 R2 R1 R0} {G5 G4 G3 G2 G1 G0 G1 G0} {B4 B3 B2 B1 B0 B2 B1 B0}

 //--------------------------------------------
//RGB565 to RGB 888
wire [:] cmos_R0;
wire [:] cmos_G0;
wire [:] cmos_B0; assign cmos_R0 = {cmos_R, cmos_R[:]};
assign cmos_G0 = {cmos_G, cmos_G[:]};
assign cmos_B0 = {cmos_B, cmos_B[:]};

  采用高位补低位的方法直接转化即可。

  这是官方给的RGB888 to YCbCr的算法公式,我们可以直接把算法移植到FPGA上,但是我们都知道FPGA无法进行浮点运算,所以我们采取将整个式子右端先都扩大256倍,然后再右移8位,这样就得到了FPGA擅长的乘法运算和加法运算了。

  这个计算式子看起来是十分简单的,但是要是直接用Verilog直接写出来,那么只能说,这个人的代码写的一塌糊涂,所以这里就引出FPGA中流水线的设计思想。

在这里我们选择加3级流水线,就第一个Y分量而言,先计算括号中得乘法运算,消耗一个时钟,然后将括号中的数据求和,消耗一个时钟,这里为了计算方便,将128也扩大256倍,放到括号中,最终结果除以256就行了也就是右移8位,在FPGA中我们只需要舍弃低8位取高8位就行。具体代码如下

 //--------------------------------------------
/*Refer to <OV7725 Camera Module Software Applicaton Note> page 5
Y = (77 *R + 150*G + 29 *B)>>8
Cb = (-43*R - 85 *G + 128*B)>>8 + 128
Cr = (128*R - 107*G - 21 *B)>>8 + 128
--->
Y = (77 *R + 150*G + 29 *B)>>8
Cb = (-43*R - 85 *G + 128*B + 32768)>>8
Cr = (128*R - 107*G - 21 *B + 32768)>>8*/
//--------------------------------------------
//RGB888 to YCrCb
//step1 conmuse 1clk
reg [:] cmos_R1, cmos_R2, cmos_R3;
reg [:] cmos_G1, cmos_G2, cmos_G3;
reg [:] cmos_B1, cmos_B2, cmos_B3;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)begin
cmos_R1 <= 'd0;
cmos_G1 <= 'd0;
cmos_B1 <= 'd0;
cmos_R2 <= 'd0;
cmos_G2 <= 'd0;
cmos_B2 <= 'd0;
cmos_R3 <= 'd0;
cmos_G3 <= 'd0;
cmos_B3 <= 'd0;
end
else begin
cmos_R1 <= cmos_R0 * 'd77;
cmos_G1 <= cmos_G0 * 'd150;
cmos_B1 <= cmos_B0 * 'd29;
cmos_R2 <= cmos_R0 * 'd43;
cmos_G2 <= cmos_G0 * 'd85;
cmos_B2 <= cmos_B0 * 'd128;
cmos_R3 <= cmos_R0 * 'd128;
cmos_G3 <= cmos_G0 * 'd107;
cmos_B3 <= cmos_B0 * 'd21;
end
end //-----------------------------------------------
//step2 consume 1clk
reg [:] img_Y0;
reg [:] img_Cb0;
reg [:] img_Cr0; always @(posedge clk or negedge rst_n)
begin
if(!rst_n)begin
img_Y0 <= 'd0;
img_Cb0 <= 'd0;
img_Cr0 <= 'd0;
end
else begin
img_Y0 <= cmos_R1 + cmos_G1 + cmos_B1;
img_Cb0 <= cmos_B2 - cmos_R2 - cmos_G2 + 'd32768;
img_Cr0 <= cmos_R3 - cmos_G3 - cmos_B3 + 'd32768;
end end
//-------------------------------------------
//step3 conmuse 1clk
reg [:] img_Y1;
reg [:] img_Cb1;
reg [:] img_Cr1; always @(posedge clk or negedge rst_n)
begin
if(!rst_n)begin
img_Y1 <= 'd0;
img_Cb1 <= 'd0;
img_Cr1 <= 'd0;
end
else begin
img_Y1 <= img_Y0 [:];
img_Cb1 <= img_Cb0 [:];
img_Cr1 <= img_Cr0 [:];
end end

流水线

  对于流水线的理论详细解释,请看我另一篇博文:http://www.cnblogs.com/ninghechuan/p/6970750.html

  将RGB565—>YCbCr成功后,提取出Y的值输出,就可以得到灰度色彩的图像了。

  将采集到的RGB565的像素数据,输入到算法处理模块进行操作,由RGB565——>YCbCr——Gray官方给出的公式来算,先将RGB565拆分开R G B三个分量,使用如上公式计算的到Y Cb Cr是三个分量。

  RGB转YCbCr算法的仿真过程,从图中可以看出,加了流水线后的运算过程,每一级运算相差一个时钟,然而每一级都在进行新的运算,我们加了3级流水线,这样运算速度可以提升3倍。

  将行信号、场信号和像素数据使能信号进行延时,消耗多少周期便延时几个周期,保持时钟的同步性。

  最后将Y分量的数据输出,进行位拼接,16位的RGB565像素R、G、B分量分别对应的取Y分量的高位,最后的输出显示出来就是灰度图像了。

 assign lcd_data = {per_img_Y1 [:],per_img_Y1 [:],per_img_Y1 [:]};//Gray

  这里还是采用高位补低位的方法。

  最终下载到FPGA开发板上,显示如下:

  图上为灰度显示,图下为转化前的原图,可以看出,灰度最终显示的是将彩色图像的颜色过滤掉,这样减小了图像的体积,滤掉多余的影响因素,为后面的滤波,边缘检测等图像处理做出关键性的基础。

转载请注明出处:NingHeChuan(宁河川)

个人微信订阅号:开源FPGANingHeChuan

如果你想及时收到个人撰写的博文推送,可以扫描左边二维码(或者长按识别二维码)关注个人微信订阅号

知乎ID:NingHeChuan

微博ID:NingHeChuan

原文地址:http://www.cnblogs.com/ninghechuan/p/6978104.html

基于FPGA的彩色图像转灰度算法实现的更多相关文章

  1. 基于FPGA的中值滤波算法实现

    在这一篇开篇之前,我需要解决一个问题,上一篇我们实现了基于FPGA的均值滤波算法的实现,最后的显示效果图上发现有一些黑白色的斑点,我以为是椒盐噪声,然后在做基于FPGA的中值滤波算法的实验时,我发现黑 ...

  2. 基于FPGA的均值滤波算法的实现

    前面实现了基于FPGA的彩色图像转灰度处理,减小了图像的体积,但是其中还是存在许多噪声,会影响图像的边缘检测,所以这一篇就要消除这些噪声,基于灰度图像进行图像的滤波处理,为图像的边缘检测做好夯实基础. ...

  3. 基于FPGA的腐蚀膨胀算法实现

    本篇文章我要写的是基于的腐蚀膨胀算法实现,腐蚀膨胀是形态学图像处理的基础,,腐蚀在二值图像的基础上做"收缩"或"细化"操作,膨胀在二值图像的基础上做" ...

  4. 基于FPGA的均值滤波算法实现

    我们为了实现动态图像的滤波算法,用串口发送图像数据到FPGA开发板,经FPGA进行图像处理算法后,动态显示到VGA显示屏上,前面我们把硬件平台已经搭建完成了,后面我们将利用这个硬件基础平台上来实现基于 ...

  5. 基于FPGA的Sobel边缘检测的实现

    前面我们实现了使用PC端上位机串口发送图像数据到VGA显示,通过MATLAB处理的图像数据直接是灰度图像,后面我们在此基础上修改,从而实现,基于FPGA的动态图片的Sobel边缘检测.中值滤波.Can ...

  6. 【转】基于FPGA的Sobel边缘检测的实现

    前面我们实现了使用PC端上位机串口发送图像数据到VGA显示,通过MATLAB处理的图像数据直接是灰度图像,后面我们在此基础上修改,从而实现,基于FPGA的动态图片的Sobel边缘检测.中值滤波.Can ...

  7. 基于FPGA的HDTV视频图像灰度直方图统计算法设计

    随着HDTV的普及,以LCD-TV为主的高清数字电视逐渐进入蓬勃发展时期.与传统CRT电视不同的是,这些高清数字电视需要较复杂的视频处理电路来驱动,比如:模数转换(A/D Converter).去隔行 ...

  8. 基于FPGA的RGB565_YCbCr_Gray算法实现

    前面我们讲了基于FPGA用VGA显示一副静态图片,那么接下来我们就接着前面的工程来实现我们图像处理的基础算法里最简单的一个那就是彩色图像转灰度的实现. 将彩色图像转化为灰度的方法有两种,一个是令RGB ...

  9. 基于FPGA的肤色识别算法实现

    大家好,给大家介绍一下,这是基于FPGA的肤色识别算法实现. 我们今天这篇文章有两个内容一是实现基于FPGA的彩色图片转灰度实现,然后在这个基础上实现基于FPGA的肤色检测算法实现. 将彩色图像转化为 ...

随机推荐

  1. Python-一些实用的函数

    一,返回值为bool类型的函数 1.any()函数 any(iterable)->bool 当迭代器中有一个是Ture,则返回Ture:若interable=NUll,则返回False. > ...

  2. 初遇stm32

    刚开始接触32,建一个工程都这么费劲,可能是keil安装时一些文件和库没有安装完整,真是坑啊. 回头可能还要从新安装,然后开始新的学习,争取十天之内入门32,在博客园这个强大的技术支持下, 想不入门都 ...

  3. xmlplus 组件设计系列之二 - 按钮

    除了图标以外,按钮也许是最简单的组件了,现在来看看如何定义按钮组件. 使用原生按钮组件 在 xmlplus 中,HTML 元素也以组件的方式存在.所以,你可以直接通过使用 button 标签或者 in ...

  4. dotnetcore中的IOptionsSnapshot<>的自动更新原理

    1.首先讲讲ChangeToken.OnChange方法: 原理是给一个CancellationToken注册一个消费者委托,调用CancellationToken的Cancel的时候会调用这个Can ...

  5. 【算法系列学习】巧妙建图,暴搜去重 Counting Cliques

    E - Counting Cliques http://blog.csdn.net/eventqueue/article/details/52973747 http://blog.csdn.net/y ...

  6. Android NDK开发之从Java与C互调中详解JNI使用(一)

    生活 这一个礼拜过得真的是苦不堪言,上周因为打球脚踝直接扭伤,肿的想猪蹄一样,然后休息几天消肿了,可以缓慢龟速的行走了,然而五一回来上班第一天,上班鞋子还能穿上,下班脚已插不进鞋子里面了,好吧,又肿回 ...

  7. map中结构体做关键字的注意事项

    序: 今天做一道题,由于递归函数比较恶心,如果用记忆化搜索,数据范围极大却又用不全(二维数组存的话直接炸).所以决定干脆使用stl::map存储(反正有O2优化),但是执行insert的时候,编译器却 ...

  8. JavaScript常用的方法和函数(setInterval和setTimeout)

    1.setInterval:计时器 可以按照指定的周期(以毫秒为单位)来调用函数或计算表达式 调用格式:setinterval(fun,time) 说明:fun为函数体,time为数值,这两个参数是必 ...

  9. Node.js项目APM监控之New Relic

    现在上一个项目,如果没有APM监控服务或应用的运行性能参数,等于是一架没有盲降系统的飞机正在盲降,结果会很悲催.出现了访问失效等问题时,都很难判定是性能瓶颈还是一个藏的深的bug,汇报的时候一顿眼晕, ...

  10. [ERR] Node 172.168.63.202:7001 is not empty. Either the nodealready knows other nodes (check with CLUSTER NODES) or contains some

    关于启动redis集群时: [ERR] Node 172.168.63.202:7001 is not empty. Either the nodealready knows other nodes ...