賽璐璐風格圖像線稿提取(算法優化中)(原Matlab代碼提供)(基於Canny邊緣提取算法)


一、導讀

    在日系插畫領域,賽璐璐風格不會過多地塑造出復雜的光影和小調子。以線條造型為主,清晰的光影表達,以及明快的配色是這個風格的畫師普遍追求的目標。基於這個前提,賽璐璐風格就是做自動線稿提取算法的很好切入點。能否詳盡地提取出原始圖像線稿中所有的線條,並且結果能讓人在視覺基礎上感到滿意,應該是這個算法應該追求的主要目標。與此同時,這樣的線稿提取算法也能成為未來自動草稿細化的基礎。為了更好地實現機器輔助設計,我們應該深刻地從圖像處理的立場上研究這種風格。

二、基本算法構架的解釋以及測試集的選擇

      算法主要分為三個部分:

     1.首先對圖像進行降噪,並且平滑整個圖像,抹去圖像中的噪音和一些小調子,以更好地進行邊緣提取.其中,我選擇的降噪算法是經典的ROF降噪模型:其優點在於經歷多次迭代后仍然可以保持圖像的邊緣,並且同時可以刪去噪音和小調子。相關知識請參考如下網站:

        https://blog.csdn.net/cyh706510441/article/details/45194223

     2.使用Canny邊緣提取算法,提取出圖像中的主要細節。

        有關Canny邊緣提取算法的前置知識請參考:

        https://blog.csdn.net/Jamence/article/details/84645554

     3.結果的后續美觀性上的處理。

     測試集的選擇為:1.畫師Anmi;2.畫師Rei;3.畫師Kirarin;4.動畫《JOJO的奇妙冒險~黃金之風》

     步驟1對於一些有小調子並且存在噪音的圖,例如插畫師rei以及Anmi的插畫,就很有必要進行這一步,其可以防止Canny算法過渡地提取邊緣。另外值得注意的是:不論是JPG格式還是PNG格式的圖像,其在MATLAB中都是以紅綠藍三個通道(也就是一個三維數組),儲存了三個不同的灰度值,最后疊加形成的彩色圖。我們提取邊緣的時候,應該對三個通道都進行提取,最后疊加成圖。在圖像降噪的環節中,我們的ROF降噪模型的迭代步數都是100,下面是原圖以及實驗結果:

     

 

 

 

 

 

     

 

 

 

 

       可以看見,整體的形體被提取了出來,而且也沒有損失細節。后續算法的改進會更加注重線條的優化,以及噪點的刪去。同時,我們可以看到對紅綠藍三層通道進行處理后的的線稿提取結果,還可以加強外輪廓(這在線稿細化中是非常重要的)。雖然仍然會有小調子和噪音的出現。但不論如何,Canny邊緣提取算法和ROF降噪模型的完美配合,顯示出了廣闊的應用前景。

       對於步驟二,為何使用Canny邊緣提取算法,是因為例如傳統的Sobel梯度邊緣提取算法,提取出的是圖像梯度值較高的“強邊緣”,但會忽視圖像中的弱邊緣。下圖中咦Kirarin老師的圖為例,左起至右依次是原圖—Sobel邊緣提取算法—Canny算子的結果圖:

       

       

 

      對於線稿提取,我們自然是不想遺漏任何的線條信息的。但Canny算子此時也會顯出它的弊端:我們也不是毫無保留地提取所有線條。如何更好地改進Canny算法,使得其可以提取出賽璐璐圖片的“主要信息”而忽略其“次要信息”呢?首先我們需要定義何種線條信息在圖片中是“主要的”,何種是“次要的”。緊接着我們可以考慮設置一個閾值——即當某一像素點的梯度大於一定取值的時候,這個邊緣信息才被保留下來,否則便舍棄。或許在未來,這一領域會和神經網絡緊密聯系起來,而且值得一提的是,日本的很多學者也已經開始用神經網絡解決此類問題了。

     對於步驟三,對提取結果的進一步優化,使其符合我們的視覺審美,這一步我目前仍然在優化和進行中:目前我的思路如下:一個好的線稿無非要滿足一下兩個特整:1.外輪廓粗而內輪廓細(例如上圖的向日葵的花瓣外輪廓很粗很實在,而內輪廓很細或者很淡。)2.對一些閉塞進行加深(通俗一點講就是兩條線相交的位置,臨近交點的線條都要加深) 而除此之外,便是要靠藝術家自身的設計功力去塑造漂亮的線稿了。請記住我們的目的永遠不是用機器取代人來設計,而是使用機器解決藝術創作中的重復性工作,解放創造力。

三、代碼的公布以及使用范例

      首先是ROF降噪算法的代碼(注意是函數,本體要調用這個代碼)

function J = tv(I,iter,dt,ep,lam,I0,C)
 
%% Total Variation denoising.
 
%% Example: J=tv(I,iter,dt,ep,lam,I0)
%% Input: I   - 樣本圖像 (二進制浮點數 array 灰度圖 level 1-256),
 
%       iter - 迭代次數
%       dt   - 時間步長             [0.2],
 
%       ep   - epsilon (正規化因子) [1],
%       lam  - 松弛因子             [0],
 
%       I0   - 無噪聲圖像           [I0=I]
%       ([]內為默認值)
 
%       Output: 像素矩陣
%% 判斷是否輸入相關參數
if ~exist('ep')
    ep=1;
end
if ~exist('dt')
    dt=ep/5; % dt需滿足CFL條件
 
end
 
if ~exist('lam')
 
    lam=0;
 
end
 
if ~exist('I0')
 
    I0=I;
 
end
 
if ~exist('C')
 
     C=0;
 
end
 
[ny,nx]=size(I); ep2=ep^2;
 
 
%% 循環迭代 
for i=1:iter, 
% 構建數值微分
 
 I_x = (I(:,[2:nx nx])-I(:,[1 1:nx-1]))/2;
 
 I_y = (I([2:ny ny],:)-I([1 1:ny-1],:))/2;
 
 I_xx = I(:,[2:nx nx])+I(:,[1 1:nx-1])-2*I;
 
 I_yy = I([2:ny ny],:)+I([1 1:ny-1],:)-2*I;
 
 Dp = I([2:ny ny],[2:nx nx])+I([1 1:ny-1],[1 1:nx-1]);
 
 Dm = I([1 1:ny-1],[2:nx nx])+I([2:ny ny],[1 1:nx-1]);
 
 I_xy = (Dp-Dm)/4;
 
 % 計算遞推關系式
 
 Num = I_xx.*(ep2+I_y.^2)-2*I_x.*I_y.*I_xy+I_yy.*(ep2+I_x.^2);
 
 Den = (ep2+I_x.^2+I_y.^2).^(3/2);
 
 I_t = Num./Den + lam.*(I0-I+C);
 
 I=I+dt*I_t; %% 更新圖像
 
 J= I;
end 

 

      然后是Canny邊緣提取算法:

function  m = canny1step( src,  lowTh)
%canny函數第一步,求去x,y方向的偏導,模板如下:
% Gx
% 1  -1
% 1  -1
% Gy
% -1  -1
%  1    1
%------------------------------------
% 輸入:
% src:圖像,如果不是灰度圖轉成灰度圖
% lowTh:低閾值
% 輸出:
% m: 兩個偏導的平方差,反映了邊緣的強度
% theta:反映了邊緣的方向
% sector:將方向分為3個區域,具體如下
% 2 1 0
% 3 X 3
% 0 1 2
% canny1:非極大值
% canny2:雙閾值抑制
% bin :     二值化
%--------------------------------------- 


[Ay Ax dim ] = size(src);
%轉換為灰度圖
if dim>1
    src = rgb2gray(src);
end


src = double(src);
m = zeros(Ay, Ax); 
theta = zeros(Ay, Ax);
sector = zeros(Ay, Ax);
canny1 = zeros(Ay, Ax);%非極大值抑制
canny2 = zeros(Ay, Ax);%雙閾值檢測和連接
bin = zeros(Ay, Ax);
for y = 1:(Ay-1)
    for x = 1:(Ax-1)
        gx =  src(y, x) + src(y+1, x) - src(y, x+1)  - src(y+1, x+1);
        gy = -src(y, x) + src(y+1, x) - src(y, x+1) + src(y+1, x+1);
        m(y,x) = (gx^2+gy^2)^0.5 ;
        %--------------------------------
        theta(y,x) = atand(gx/gy)  ;
        tem = theta(y,x);
        %--------------------------------
        if (tem<67.5)&&(tem>22.5)
            sector(y,x) =  0;    
        elseif (tem<22.5)&&(tem>-22.5)
            sector(y,x) =  3;    
        elseif (tem<-22.5)&&(tem>-67.5)
            sector(y,x) =   2;    
        else
            sector(y,x) =   1;    
        end
        %--------------------------------        
    end    
end
%-------------------------
%非極大值抑制
%------> x
%   2 1 0
%   3 X 3
%y  0 1 2
for y = 2:(Ay-1)
    for x = 2:(Ax-1)        
        if 0 == sector(y,x) %右上 - 左下
            if ( m(y,x)>m(y-1,x+1) )&&( m(y,x)>m(y+1,x-1)  )
                canny1(y,x) = m(y,x);
            else
                canny1(y,x) = 0;
            end
        elseif 1 == sector(y,x) %豎直方向
            if ( m(y,x)>m(y-1,x) )&&( m(y,x)>m(y+1,x)  )
                canny1(y,x) = m(y,x);
            else
                canny1(y,x) = 0;
            end
        elseif 2 == sector(y,x) %左上 - 右下
            if ( m(y,x)>m(y-1,x-1) )&&( m(y,x)>m(y+1,x+1)  )
                canny1(y,x) = m(y,x);
            else
                canny1(y,x) = 0;
            end
        elseif 3 == sector(y,x) %橫方向
            if ( m(y,x)>m(y,x+1) )&&( m(y,x)>m(y,x-1)  )
                canny1(y,x) = m(y,x);
            else
                canny1(y,x) = 0;
            end
        end        
    end%end for x
end%end for y

%---------------------------------
%雙閾值檢測
ratio = 2;
for y = 2:(Ay-1)
    for x = 2:(Ax-1)        
        if canny1(y,x)<lowTh %低閾值處理
            canny2(y,x) = 0;
            bin(y,x) = 0;
            continue;
        elseif canny1(y,x)>ratio*lowTh %高閾值處理
            canny2(y,x) = canny1(y,x);
            bin(y,x) = 1;
            continue;
        else %介於之間的看其8領域有沒有高於高閾值的,有則可以為邊緣
            tem =[canny1(y-1,x-1), canny1(y-1,x), canny1(y-1,x+1);
                       canny1(y,x-1),    canny1(y,x),   canny1(y,x+1);
                       canny1(y+1,x-1), canny1(y+1,x), canny1(y+1,x+1)];
            temMax = max(tem);
            if temMax(1) > ratio*lowTh
                canny2(y,x) = temMax(1);
                bin(y,x) = 1;
                continue;
            else
                canny2(y,x) = 0;
                bin(y,x) = 0;
                continue;
            end
        end
    end%end for x
end%end for y




end%end of function

 

     最后是我們的主體代碼

close all; clear all; clc;

mI = double( imread( 'C:/Matlab/IMG_3228.png' ) );%此處輸入你的圖像

sigma1 = 15;           %高斯正態分布標准差
gausFilter = fspecial('gaussian',[3 3],sigma1);   %高斯濾波器
%設置高斯濾波是提取邊緣后的進一步去除噪音點

mI1 = mI(:,:,1);
mI2 = mI(:,:,1);
mI3 = mI(:,:,1);%三個圖層分別對應紅綠藍三個通道的灰度圖

% mI1 = TVSample(mI( :, :, 1 ),100);
% mI2 = TVSample(mI( :, :, 2 ),100);
% mI3 = TVSample(mI( :, :, 3 ),100);

[ny,nx]=size(mI1);

A = canny(mI1,graythresh(mI1)); %將圖像進行TV迭代, TV函數的格式為:TV(圖像,迭代次數,(后續參數))
% A = edge(mI1);%為了讓你方便和其他邊緣提取算法比較,這里直接用MAT內置的edge來做,使用的時候去除 % 就行
A = abs(A);%取像素絕對值
A = imcomplement(A);%二值化
A = mat2gray(A);%轉為標准灰度(0-255)
A = imfilter(A,gausFilter,'replicate'); %高斯濾波除一下結果圖的噪音
% A = imbinarize(A,'global');%像素取反,更清楚地看見邊緣

B = canny(mI2,graythresh(mI1)); %將圖像進行TV迭代, TV函數的格式為:TV(圖像,迭代次數,(后續參數))
% B = edge(mI2);
B = abs(B);
B = imcomplement(B);
B = mat2gray(B);
B = imfilter(B,gausFilter,'replicate'); 
% B = imbinarize(B,'global');

C = canny(mI3,graythresh(mI1)); %將圖像進行TV迭代, TV函數的格式為:TV(圖像,迭代次數,(后續參數))
% C = edge(mI3);
C = abs(C);
C = imcomplement(C);
C = mat2gray(C);
C = imfilter(C,gausFilter,'replicate'); 
% C = imbinarize(C,'global');

D = A+B+C;
% D = 1 * D.^5
% tresh = min(min(D))
% D(find(tresh+1<D<tresh+3))=D(find(tresh+1<D<tresh+3))-1;

% [ny,nx]=size(A);
% E = A
% for i = 3:ny-2
%   for j = 3:nx-2
%      if A(i,j) == 0
%         E(i+1,j) = 0; E(i-1,j) = 0;E(i,j+1) = 0;E(i,j-1) = 0;
%         E(i+1,j+1) = 0;E(i+1,j-1) = 0;E(i-1,j+1) = 0;E(i-1,j-1) = 0;
%       
%      end
%   end
% end  %這里的一段代碼是為后續優化設計的,還沒完善。

figure(1); imagesc( A ); colormap( gray ); axis off;%輸出提取后1通道圖像
figure(2); imagesc( B ); colormap( gray ); axis off;%輸出提取后的2通道圖像
figure(3); imagesc( C ); colormap( gray ); axis off;%輸出提取后的3通道圖像
figure(4); imagesc( D ); colormap( gray ); axis off; %輸出提取后的復合圖像

 

 四、寫在最后

     我曾經一直在迷茫:其實我數學並不好,學什么都要比別人吃力很多,我喜歡畫畫,我為什么不能選自己喜歡的東西。但不管如何抱怨,命運還是把我帶到了國內數學系很牛的一個大學。我從自暴自棄到現在,思考了很多東西:大概這一切的確冥冥之中有天意,我的很多畫家朋友用他們自己的努力,去推動着商業美術的發展,那我也能用我自己的方式去實現一些東西,不管會繞多遠的路,遇到多少挫折也好,就像老謝的lesson3教的:最快的捷徑就是繞遠路。

       我很喜歡布加拉提說的:“我不后悔,雖然是身處於這樣的世界,可我還是想走在“自己所堅信的道路”上,雖然現在我只能逃走,但只要找到弱點,就一定能打敗老板,我一定會找出他的弱點!”即使滾石早就預言着他不論如何都會死,但他用他的覺悟詮釋了什么叫做“知其不可而為之”。
       ARRIVEDERC!

       

 

 

 

       

 

 

 

 

 

      

       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM