經過前面的知識,我們知道了一個 8 bit 的灰度數據數值是 0~255,代表着從黑到白。而二值圖像則是灰度圖的特殊化,那我們開動腦筋想一想,是否可以設置一下閾值來制造二值圖像,使之能呈現一些特殊的效果呢?
答案是可以的,那本章博客就來整理一下FPGA實現鋼筆畫和浮雕畫的實現。
一、鋼筆畫
鋼筆畫的實現比較簡單,核心思想就是設置一個閾值,灰度圖和閾值進行對比,從而輸出全1或全0的圖像。
1、MATLAB實現
1 %-------------------------------------------------------------------------- 2 % 鋼筆畫 3 %-------------------------------------------------------------------------- 4 clc; 5 clear all; 6 RGB = imread('Lenna.jpg'); %讀取圖像 7 gray = rgb2gray(RGB); %灰度圖 8 9 [ROW,COL,N] = size(gray); %獲得圖像尺寸[高度,長度,維度] 10 pen = zeros(ROW,COL); 11 value = 80; 12 13 for i = 1:ROW 14 for j = 1:COL 15 pen(i,j) = gray(i,j); 16 if gray(i,j) > value 17 pen(i,j) = 255; 18 else 19 pen(i,j) = 0; 20 end 21 end 22 end 23 24 subplot(2,1,1);imshow(gray); title('灰度圖'); 25 subplot(2,1,2);imshow(pen);title('鋼筆畫');
這里我的閾值 value 設置為100,它的范圍是 0-255,點擊運行,我們得到如下結果:
結果還是不錯的,得到了鋼筆畫的效果,唉如果自己用筆能畫得這么好看就好了 ^_^
2、FPGA實現
這個算法比較簡單,Verilog寫起來也是一氣呵成,之前我們實現了 RGB565 轉 YCbCr444,Y 分量代表灰度數據,把它提取出來做閾值比較處理即可,關鍵代碼如下所示:
assign pen_de = Y_de; assign pen_hsync = Y_hsync; assign pen_vsync = Y_vsync; assign pen_data = (Y_data > value) ? 16'hffff : 16'h0000;
為了方便查看不同閾值帶來的效果,閾值 value 由外部引入,從兩個按鍵獲得,按鍵key[0]增加閾值,按鍵[1]減小閾值。按鍵獲得閾值的代碼如下所示:
1 //************************************************************************** 2 // *** 名稱 : key_value.v 3 // *** 作者 : xianyu_FPGA 4 // *** 博客 : https://www.cnblogs.com/xianyufpga/ 5 // *** 日期 : 2019-08-10 6 // *** 描述 : 按鍵獲得閾值 7 //************************************************************************** 8 9 module key_value 10 //========================< 端口 >========================================== 11 ( 12 input wire clk , 13 input wire rst_n , 14 input wire [ 1:0] key_vld , 15 output reg [ 7:0] value 16 ); 17 //========================================================================== 18 //== 代碼 19 //========================================================================== 20 always @(posedge clk or negedge rst_n) begin 21 if(!rst_n) begin 22 value <= 8'd0; 23 end 24 else if(key_vld[0]) begin 25 value <= value + 8'd10; 26 end 27 else if(key_vld[1]) begin 28 value <= value - 8'd10; 29 end 30 else if(value<10) begin //255不是10的倍數,跳過1-10數值 31 value <= 8'd0; 32 end 33 else if(value>245) begin //跳過251-255數值 34 value <= 8'd250; 35 end 36 end 37 38 39 40 endmodule
閾值的增減幅度設置為 10,閾值的范圍是 0~255,但 255 不是10的整數倍,所以閾值為0又按下減閾值按鍵時,會出現246,236等數值,不好看,所以我設計時將閾值改為0-250。
此外,這個閾值除了提供給圖像處理外,我還傳給了數碼管模塊,這樣就能直接看到當前的閾值了。
3、上板驗證
當閾值為80時,我們得到如下的圖像:
和MATLAB進行對比,除了因板卡壞了導致顏色失真外,效果是一樣的,此次實驗成功。
完整實驗的視頻如下所示:(板子有問題,黑色顯示成了紅色)
實現了鋼筆畫后,我們可以通過同樣的方式,改變輸出的灰度值來實現鉛筆畫,此外還有其他更特別的效果也可以采用多個閾值對不同顏色做輸出處理。
二、浮雕效果
浮雕是雕刻的一種,雕刻者在一塊平板上將他要塑造的形象雕刻出來,使它脫離原來材料的平面。浮雕是雕塑與繪畫結合的產物,用壓縮的辦法來處理對象,靠透視等因素來表現三維空間,並只供一面或兩面觀看。浮雕一般是附屬在另一平面上的,因此在建築上使用更多,用具器物上也經常可以看到。由於其壓縮的特性,所占空間較小,所以適用於多種環境的裝飾。近年來,它在城市美化環境中占了越來越重要的地位。浮雕在內容、形式和材質上與原調一樣豐富多彩。浮雕的材料有石頭、木頭、象牙和金屬等。
本次設計同樣是基於 YCbCr 的灰度數據Y分量來進行的,浮雕效果的算法公式為:NewPixel(i,j)= Pixel(i,j+1)- Pixel(i,j)+ value,其中 i 為圖像高度,j 為圖像寬度,pixel為像素點,value為閾值(0-255)。
1、MATLAB實現
1 %-------------------------------------------------------------------------- 2 % 浮雕畫 3 %-------------------------------------------------------------------------- 4 clc; 5 clear all; 6 RGB = imread('sun.jpg'); %讀取圖像 7 gray = rgb2gray(RGB); %灰度圖 8 9 [ROW,COL,N] = size(gray); %獲得圖像尺寸[高度,長度,維度] 10 emboss = zeros(ROW,COL); 11 value = 100; 12 13 for i = 1:ROW 14 for j=1:COL-1 15 emboss(i,j) = gray(i,j+1) - gray(i,j) + value; 16 if emboss(i,j) > 255 17 emboss(i,j) = 255; 18 elseif emboss(i,j) < 0 19 emboss(i,j) = 0; 20 else 21 emboss(i,j) = emboss(i,j); 22 end 23 end 24 end 25 emboss = uint8(emboss); 26 27 subplot(2,1,1);imshow(gray); title('灰度圖'); 28 subplot(2,1,2);imshow(emboss);title('浮雕畫');
原圖如下所示:
閾值范圍是 0-255,這里我設置為80,點擊運行MATLAB,我們得到如下結果:
浮雕效果出來了,
2、FPGA實現
首先考慮一個問題,如何實現公式 NewPixel(i,j)= Pixel(i,j+1)- Pixel(i,j)+ value ,尤其是 j+1 怎么處理?答案是打拍,通過打拍獲得 Y_data_r,它的數據就相當於是 j,而原數據就相當於是 j+1。
always @(posedge clk or negedge rst_n) begin if(!rst_n) begin Y_data_r <= 8'd0; end else begin Y_data_r <= Y_data; end end assign emboss = Y_data - Y_data_r + value; //-255 ~ 510
這里要注意一下 emboss 信號,它可能出現負數,因此我們得從極端情況來設計,極端情況下 Y_data、Y_data_r 和 value 是 0 或255,那 emboss 的值就是 -255~510之間,因此我們可以將emboss信號定義成有符號數: wire signed [ 9:0] emboss; 。
處理好這步后,就可以進行浮雕畫的處理了,代碼如下所示:
always @(posedge clk or negedge rst_n) begin if(!rst_n) begin emboss_data <= 16'h0000; end else if(emboss > 255) begin emboss_data <= 16'hffff; end else if(emboss < 0) begin emboss_data <= 16'h0000; end else begin emboss_data <= {emboss[7:3],emboss[7:2],emboss[7:3]}; end end
到這里數據方面就設計完成了,但是還沒有完,千萬千萬不要忘了信號同步!行場信號、使能信號如果不同步,最后的圖像肯定出問題。信號同步的代碼如下所示:
//========================================================================== //== 信號同步 //========================================================================== always @(posedge clk or negedge rst_n) begin if(!rst_n) begin Y_de_r <= 2'b0; Y_hsync_r <= 2'b0; Y_vsync_r <= 2'b0; end else begin Y_de_r <= {Y_de_r, Y_de}; Y_hsync_r <= {Y_hsync_r, Y_hsync}; Y_vsync_r <= {Y_vsync_r, Y_vsync}; end end assign emboss_de = Y_de_r[1]; assign emboss_hsync = Y_hsync_r[1]; assign emboss_vsync = Y_vsync_r[1];
3、上板驗證
和上面一樣,我用按鍵提供value閾值,數碼管實時顯示當前閾值,當閾值為80時,得到如下結果:
和 MATLAB 效果是一樣的,實驗成功。
完整實驗的視頻如下所示:(板子有問題,黑色顯示成了紅色)
參考資料:[1] OpenS Lee:FPGA開源工作室(公眾號)