前言
本章,我们开启新的图像处理内容,在均值滤波、中值滤波中均介绍Python和FPGA实现,高斯和双边滤波只做Python的实现,感兴趣的小伙伴可以自己实现。
一、为什么要滤波
在图像传感器成像过程中,光电转换及数模放大时,不可避免的产生噪声。噪声公式如下。
f
(
x
,
y
)
=
I
(
x
,
y
)
+
n
o
i
s
e
f(x, y) = I(x, y) + noise
f(x,y)=I(x,y)+noise
二、对图像增加噪音
椒(0)盐(255)噪声,椒是黑色,盐是白色。可以在noise_img = np.where(noise == 0, img, 255).astype(np.uint8)代码中进行更改。保存增噪的图像,用于FPGA的处理。
import numpy as np
import matplotlib.pyplot as plt
img = plt.imread("lenna.png")
img = img * 255#图像是[0-1]--->[0-255],确认一下自己的图像是[0-1]还是[0-255]
img = img.astype(np.uint8)
prob = 0.92#生成盐噪生的概率为1-0.92=0.08
noise = np.random.choice((0, 255), size=img.shape, p=[prob, 1 - prob])#产生和图像维度一样的噪音矩阵
noise_img = np.where(noise == 0, img, 255).astype(np.uint8)#生成噪音图像,椒(0黑色)盐噪声的盐(255白色)
plt.imsave("noise.png", noise_img)#保存噪音图像,用于FPGA去噪
fig = plt.figure(figsize=(10, 6))
ax = plt.subplot(1, 2, 1)
ax.set_title("raw image")
ax.set_xlabel("width")
ax.set_ylabel("height")
plt.imshow(img)
ax = plt.subplot(1, 2, 2)
ax.set_title("salt image")
ax.set_xlabel("width")
ax.set_ylabel("height")
plt.imshow(noise_img)
三、Python均值滤波
均值滤波之前,对图像进行灰度处理,灰度处理的函数,和之前的方法rbg通道,乘以相应的系数有所改进。
均值滤波原理如下。
#改进的灰度处理
def image_gray(image):
gray = np.dot(image[:, :, ...], [0.299, 0.587, 0.114])#等同0.299 * image[:, :, 0] + 0.587 * image[:, :, 1] + 0.114 * image[:, :, 2]
return gray.astype(np.uint8)
def mean_filter(image, n):
h, w = image.shape#原图高宽
filtered_image = np.zeros((h, w))#滤波后的图像
m = int((n - 1) / 2)
for i in range(m, h - m):
for j in range(m, w - m):
kernel = image[i - m: i + m + 1, j - m: j + m + 1]
filtered_image[i, j] = np.sum(kernel) / (n * n)#核内元素相加除以元素个数
return filtered_image.astype(np.uint8)
noise_gray = image_gray(noise_img)
mean_image = mean_filter(noise_gray, 3)#设置核大小3x3
fig = plt.figure(figsize=(10, 6))
ax = plt.subplot(1, 2, 1)
ax.set_title("raw image")
ax.set_xlabel("width")
ax.set_ylabel("height")
plt.imshow(noise_gray, cmap="gray")
ax = plt.subplot(1, 2, 2)
ax.set_title("mean image")
ax.set_xlabel("width")
ax.set_ylabel("height")
plt.imshow(mean_image, cmap="gray")
四、FPGA均值滤波
在FPGA均值滤波中,需要rgb888_to_ycbcr模块输出的y分量信号,前面章节已经讲过,也就是灰度处理的信号,然后在此基础上进行均值滤波。在这部分会调用移位寄存器ip进行设计,这部分内容会在本课程最后进行详细的讲解。
//3*3图像
//P11 P12 P13
//P21 P22 P23
//P31 P32 P33
//卷积核
//1 1 1
//1 1 1
//1 1 1
module ycbcr_to_mean(
input wire vga_clk ,//vga时钟
input wire sys_rst_n ,//复位信号
input wire rgb_valid ,//vga控制模块给的图像有效信号
input wire [7:0] y_data ,//rgb888_to_ycbcr处理的y分量,灰度像素
output wire [15:0] mean_data//输出均值
);
//Y分量有效信号
reg y_valid ;
//shift ram
wire [7:0] data_row1 ;
wire [7:0] data_row2 ;
wire [7:0] data_row3 ;
//3*3像素数据
reg [7:0] p11 ;
reg [7:0] p12 ;
reg [7:0] p13 ;
reg [7:0] p21 ;
reg [7:0] p22 ;
reg [7:0] p23 ;
reg [7:0] p31 ;
reg [7:0] p32 ;
reg [7:0] p33 ;
//均值
wire [7: 0] temp;
assign data_row3 = y_data ;
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
y_valid <= 1'b0 ;
else
y_valid <= rgb_valid ;
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
{p11,p12,p13} <= 24'd0 ;
{p21,p22,p23} <= 24'd0 ;
{p31,p32,p33} <= 24'd0 ;
end
else if(y_valid == 1'b1)
begin
{p11,p12,p13} <= {p12,p13,data_row1} ;
{p21,p22,p23} <= {p22,p23,data_row2} ;
{p31,p32,p33} <= {p32,p33,data_row3} ;
end
else
begin
{p11,p12,p13} <= 24'd0 ;
{p21,p22,p23} <= 24'd0 ;
{p31,p32,p33} <= 24'd0 ;
end
shift_ram_gen shift_ram_gen_inst//需要调用移位寄存器ip
(
.clock (sys_clk ),
.shiftin (data_row3 ),
.shiftout ( ),
.taps0x (data_row2 ),
.taps1x (data_row1 )
);
assign temp = (p11 + p12 + p13 + p21 + p22 + p23 + p31 + p32 + p33) / 9;
assign mean_data = {temp[7:3],temp[7:2],temp[7:3]};//输出,vga565
endmodule
加椒盐噪声的原图,采集卡采集到的图像。
均值滤波处理,采集卡采集到的图像。
总结
均值滤波是滤波算法的入门课程。Python实现这一算法还算比较easy,FPGA实现稍微复杂一点,但是效果是真的很nice。按下按键图像就被嗖的一下处理掉了,速度快到难以形容。咦~,好像在哪一章节说过这句话。不管了,下期继续中值滤波,敬请期待。