前言
本系列课程结合软件与硬件,以图像为主要研究对象。课程素材来源于《基于MATLAB与FPGA的图像处理教程》,并将MATLAB改成Python。
一、RGB转YCbCr
RGB转YCbCr有以下几个标准,标准不同,转换公式不同,本文只给出标准3的转换公式。
- 标准清晰度电视(SDTV)
- 清晰度电视(HDTV)
- full range 或者 pc range
full range 或者 pc range公式如下:
[
Y
C
b
C
r
]
=
[
0
128
128
]
+
[
0.299
0.587
0.114
−
0.169
−
0.331
0.500
0.500
−
0.419
−
0.081
]
×
[
R
G
B
]
,
其中
{
R
/
G
/
B
∈
[
0
,
255
]
Y
/
C
b
/
C
r
∈
[
0
,
255
]
(1)
\begin{bmatrix} Y\\ Cb\\ Cr\\ \end{bmatrix} = \begin{bmatrix} 0\\ 128\\ 128\\ \end{bmatrix} + \begin{bmatrix} 0.299&0.587&0.114\\ -0.169&-0.331&0.500\\ 0.500&-0.419&-0.081\\ \end{bmatrix} \times \begin{bmatrix} R\\ G\\ B\\ \end{bmatrix},其中 \begin{cases} R/G/B \in &[0, 255]\\ Y/Cb/Cr \in &[0, 255] \end{cases}\tag{1}
YCbCr
=
0128128
+
0.299−0.1690.5000.587−0.331−0.4190.1140.500−0.081
×
RGB
,其中{R/G/B∈Y/Cb/Cr∈[0,255][0,255](1)
为了方便FPGA硬件设计,需要对公式(1)进行稍微的转变。
Y
=
R
×
0.299
+
G
×
0.587
+
B
×
0.114
(2)
Y = R\times0.299 + G\times0.587+B\times0.114\tag{2}
Y=R×0.299+G×0.587+B×0.114(2)
将公式(2)扩大
2
8
2^8
28=256倍。
256
×
Y
=
R
×
76.544
+
G
×
150.272
+
B
×
29.184
≈
R
×
76
+
G
×
150
+
B
×
29
(3)
256 \times Y = R \times 76.544 + G \times 150.272 + B \times 29.184 \approx R\times76 + G \times150 + B \times 29\tag{3}
256×Y=R×76.544+G×150.272+B×29.184≈R×76+G×150+B×29(3)
结果不能四舍五入,去掉小数即可,算出结果再通过向右移动8位缩小256倍得到Y。Cb,Cr同样的操作,如公式(4)所示。
{
Y
=
(
R
×
76
+
G
×
150
+
B
×
29
)
>
>
8
C
b
=
(
−
R
×
43
−
G
×
84
+
B
×
128
+
32768
)
>
>
8
C
b
=
(
R
×
128
−
G
×
107
−
B
×
20
+
32768
)
>
>
8
(4)
\begin{cases} Y = (R\times76 + G \times150 + B \times 29)>>8\\ Cb = (-R\times43 - G \times84 + B \times 128 + 32768)>>8\\ Cb = (R\times128 - G \times107 - B \times 20 + 32768)>>8\end{cases}\tag{4}
⎩
⎨
⎧Y=(R×76+G×150+B×29)>>8Cb=(−R×43−G×84+B×128+32768)>>8Cb=(R×128−G×107−B×20+32768)>>8(4)
二、Python端代码
import matplotlib.pyplot as plt
import numpy as np
img = plt.imread("girl.jpg")#读取图像
h, w, _ = img.shape#获取高宽通道
img_ycbcr = np.zeros(img.shape)
for i in range(h):
for j in range(w):
img_ycbcr[i, j, 0] = (img[i, j, 0] * 76 + img[i, j, 1] * 150 + img[i, j, 2] * 29) >> 8
img_ycbcr[i, j, 1] = ((-img[i, j, 0]) * 43 - img[i, j, 1] * 84 + img[i, j, 2] * 128 + 32768) >> 8
img_ycbcr[i, j, 2] = (img[i, j, 0] * 128 - img[i, j, 1] * 107 - img[i, j, 2] * 20 + 32768) >> 8
#画图
plt.figure(figsize=(10, 10))
plt.subplot(221)
plt.title("RGB image")
plt.imshow(img)
plt.subplot(222)
plt.title("y channel")
plt.imshow(img_ycbcr[:, :, 0], cmap='gray')
plt.subplot(223)
plt.title("cb channel")
plt.imshow(img_ycbcr[:, :, 1], cmap='gray')
plt.subplot(224)
plt.title("cr channel")
plt.imshow(img_ycbcr[:, :, 2], cmap='gray')
保存YCbCr图像。
img_ycbcr = img_ycbcr.astype(np.uint8)#float to int
plt.imsave("ycbcr.jpg", img_ycbcr)#保存转换后的图像
将RGB与YCbCr以十六进制保存到.dat文件里面,供FPGA端代码测试。
import os
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
"""
把图像像素转hex写入.dat文件
"""
def write_file(arr, dat_file):
arr_list = arr.tolist()
f = open(dat_file, "a")
for i in range(len(arr_list)):
row_hex = ' '.join([hex(j)[2:] for j in arr_list[i]]) + '\n'
f.write(row_hex)
f.close()
"""
r,g,b全部写入文件
"""
def gen_dat(image_file, dat_file):
image_file = os.path.join(os.getcwd(), image_file)
dat_file = os.path.join(os.getcwd(), dat_file)
if os.path.exists(dat_file):#如果存在,就删除.dat文件
os.remove(dat_file)
img = Image.open(image_file)
resized_image = img.resize((10,10))#由于原图太大,不便于观察
r, g, b = resized_image.split()
r_array = np.array(r)
g_array = np.array(g)
b_array = np.array(b)
write_file(r_array, dat_file)
write_file(g_array, dat_file)
write_file(b_array, dat_file)
if __name__ == "__main__":
gen_dat("girl.jpg", "img_rgb.dat")
gen_dat("ycbcr.jpg", "img_ycbcr.dat")
原图过大,为了减少计数量,缩放至10x10,r是1到10行,g是11到20行,b是21到30行。
总结
实现的代码还是有所区别,并不是和课本完成一致,但是目标是一致的,就如《基于MATLAB与FPGA的图像处理教程》中说到的,让你的软件起飞。下期将带来RGB转YCbCr——FPGA,敬请期待。