Metoda aktywnych konturów Grzegorz Szuba (grzegorzszuba@poczta.onet.pl) informatyka IV rok Metody Rozpoznawania Obrazów 2005/2006 Treść zadania: Celem zadania jest stworzenie konturu, który będzie dążył do otoczenia plamy na czarnobiałym obrazie. W zamierzeniu, kontur powinien dokładnie się przykleić do plamy. Kod programu: %BW=imread('3lis.bmp'); %BW=imread('3mapa.bmp'); %BW=imread('3plama.bmp'); BW=imread('3plamy.bmp'); imshow(bw); [tmp, bx,by] = roipoly(); snake = zeros(size(bx,1)-1,2); for i = 1:size(bx,1)-1 snake(i,1)=int16(bx(i,1)); snake(i,2)=int16(by(i,1)); sasiad=[1 0; 0 1; -1 0; 0-1]; kierunek=[-1 1; -1-1; 1-1 1 1]; for i = 1: size(snake,1)-1 line([snake(i,1) snake(i+1,1)],[snake(i,2) snake(i+1,2)],'color', 'b','erasemode','none','marker','.','markersize', 20); line([snake(size(snake,1),1) snake(1,1)],[snake(size(snake,1),2) snake(1,2)],'color', 'b','erasemode','none','marker','.','markersize', 20); %pause; skok=51; while(skok>3) skok=skok-1
czy=1; while (czy==1) czy=0; %%%%%%%%%%%%%%%%%%%5 rozmiar=size(snake,1); for i = 1:size(snake,1) x=snake(i,1); y=snake(i,2); if(i==1) srodekx = int16((snake(2,1)+snake(rozmiar,1))/2); srodeky = int16((snake(2,2)+snake(rozmiar,2))/2); elseif(i==rozmiar) srodekx = int16((snake(1,1)+snake(rozmiar-1,1))/2); srodeky = int16((snake(1,2)+snake(rozmiar-1,2))/2); else srodekx = int16((snake(i-1,1)+snake(i+1,1))/2); srodeky = int16((snake(i-1,2)+snake(i+1,2))/2); ret = 0; sizes=0; while( ret==0) sizes=sizes+1; ktory=0; while(ret==0 && ktory<4) ktory=ktory+1; ii=-1; while(ret==0 && ii<sizes-1) ii=ii+1; nx = x + sasiad(ktory,1)*sizes + kierunek(ktory,1)*ii; ny = y + sasiad(ktory,2)*sizes + kierunek(ktory,2)*ii; if(nx<1) nx=1; if(ny<1) ny=1; if(nx>256) nx=256; if(ny>256) ny=256; if(bw(ny,nx)==1) r=int16(sqrt(double((x-srodekx)^2+(y-srodeky)^2))); d=int16(sqrt(double((x-nx)^2+(y-ny)^2))); d+r)); d+r)); %nowex=int16((((r*9+d)/10)*x+((d*9+r)/10)*srodekx)/( %nowey=int16((((r*9+d)/10)*y+((d*9+r)/10)*srodeky)/( nowex=int16((r*x+d*srodekx)/(d+r)); nowey=int16((r*y+d*srodeky)/(d+r)); nowex=int16(((skok-1)*nowex + nx)/skok); nowey=int16(((skok-1)*nowey + ny)/skok); %nowex=int16(((skok-1)*x + nx)/skok); %nowey=int16(((skok-1)*y + ny)/skok);
if(bw(nowey,nowex)==1) nowex=snake(i,1); nowey=snake(i,2); if(snake(i,1)~=nowex) czy = 1; if(snake(i,2)~=nowey) czy = 1; snake(i,1)=nowex; snake(i,2)=nowey; ret=1; b= [nx ny]; %%%%%%%%%%%%%%%%%%%%%%%%%%%5 pause(0.0001); imshow(bw); for i = 1: size(snake,1)-1 %hold on; line([snake(i,1) snake(i+1,1)],[snake(i,2) snake(i+1,2)],'color', 'b','erasemode','none','marker','.','markersize', 20); %hold on; line([snake(size(snake,1),1) snake(1,1)],[snake(size(snake,1),2) snake(1,2)],'color', 'b','erasemode','none','marker','.','markersize', 20);
Przykładowe wyniki: a) Najprostsza plama. Kontury działały na niej poprawnie. Zawiera jeden element wklęsły, ale niezbyt ostry, dlatego zwykle był on także dobrze odwzorowywany. b) Nieco trudniejsza plama. Zawiera wąską ale głęboką wklęsłość. Zwykle kontur nie potrafił jej dobrze odwzorować. Punkty układały się wzdłuż prostej łączącej dwa krańce wejścia a następnie były do nich przyciągane (zamiast do wnętrza plamy). c) Jeszcze jeden przykład z wklęsłością, tym razem jest ona głównym elementem plamy. Możemy dokładnie obserwować, w jaki sposób snake omija wnętrze.
d) Ciekawy przykład. Wbrew oczekiwaniom, kontur osiągał całkiem dobre rezultaty. Myślę, że było to spowodowane tym, że ponieważ nie potrafi wchodzić do wnętrza wklęsłości, traktował wolne przestrzenie jako właśnie takie miejsca i ostatecznie potraktował plamy tak jakby była jedną plamą z wieloma wklęsłościami. Ciekawe było także zachowanie na drobnych plamkach z prawej u góry. Wędrujący kontur czasem się na nich zatrzymywał, czasem je omijał, a czasem wybiórczo część pomijał, a część uznawał za znaczące. e) Na koniec przykład nie działający poprawnie. Jest to plama, która nie jest jednolita, ale składająca się z dużej ilości małych elementów. Dla ludzkiego oka jest tu oczywisty kształt, ale algorytm nie mający ogólnego spojrzenia a jedynie działający na pojedynczych pikselach czasami popełnia błędy i jak widać na załączonym przykładzie, niektóre punkty konturu wnikają do wnętrza plamy. Wnioski: Użyty algorytm doskonale nadaje się do wyznaczania konturów obłych, wypukłych kształtów. Problemy pojawiają się jednak w przypadkach, gdy część plamy jest wklęsła. Po licznych próbach i eksperymentach udało mi się osiągnąć tyle, że wklęsłości niezbyt głębokie a jednocześnie dosyć szerokie są w miarę dobrze odwzorowywane. Jednak nadal wklęsłości wąskie i głębokie nie są wychwytywane przez program, lub też są tylko nieznacznie sygnalizowane wgięciem konturu do wnętrza wklęsłości. Mankamentem algorytmu jest to, że nadaje się on do rozpoznawania kształtów jednolitych obszarów. Dla obszarów stworzonych z wielu małych plamek algorytm zawodzi, gdyż operując na pojedynczych pikselach, czasem wnika do wnętrza plamy, gdyż na poziomie pikseli jest to zwykła wklęsłość w figurze.