세선화는 그림을 실선화 시키는 알고리즘이다. 가장 잘 알려진 Zhang Suen 알고리즘을 선택하였다.
코드 작성은 action script를 사용하였으며 wonderfl.net을 사용하였다.

실행하면 왼편이 원본 이미지이고 이를 세선화 한것이 오른 편에 나타나도록 하였다.

thinning on 2011-8-24 - wonderfl build flash online



kevin님이 버그를 발견해주셔서 (감사합니다 ㅎㅎ) 버그를 수정하고 코드를 약간 간략하게 수정하였다.
아래는 코드다.
/*
    2011/09/01
    Kyungmin Cho
    broneri@gmail.com
    thinning test : Zhang Suen algorithm
    --------------------------------------
    2011-09-01 - initial code.
    2011-09-07 - bug fix. thanks to kevin.    
*/
package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.display.*;
        
    
    
    public class FlashTest extends Sprite {

        private var img_width:int = 20;
        private var img_height:int = 20;
        private var img:Array = new Array (
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0],
        [0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
        [0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
        [0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,0,0],
        [0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0],
        [0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0],
        [0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0],
        [0,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0],
        [0,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
                 );        

        private var img_triangle:Array = new Array (
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0],
        [0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0],
        [0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0],
        [0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0],
        [0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0],
        [0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0],
        [0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0],
        [0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
        [0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
                 );        


        private var _bitmap:BitmapData= new BitmapData(stage.stageWidth, stage.stageHeight,
                                   false, 0xff000000);
        private var _image:Bitmap= new Bitmap(_bitmap);

        private var org_img:Array;
        private var org_img_triangle:Array;
        
        public function FlashTest() {
            // write as3 code here..
            addChild( _image );
            
            org_img = make_bitmap_array( img_width, img_height );
            copy_bitmap_array( img, org_img, img_width, img_height );            
            thin_image ( img, img_width, img_height );

            org_img_triangle = make_bitmap_array( img_width, img_height );
            copy_bitmap_array( img_triangle, org_img_triangle, img_width, img_height );            
            thin_image ( img_triangle, img_width, img_height );

            addEventListener( Event.ENTER_FRAME, onEnterFrame );
        }
        
        public function onEnterFrame (event:Event) : void {
            // original image
             draw_image ( org_img, img_width, img_height, 100, 50 );
             // thining image
             draw_image ( img, img_width, img_height, 200, 50 );             

            // original image
             draw_image ( org_img_triangle, img_width, img_height, 100, 150 );
             // thining image
             draw_image ( img_triangle, img_width, img_height, 200, 150 );             
        }
        
        public function draw_image ( img_src:Array, w:int, h:int, xpos:int, ypos:int ) : void {
             for ( var x:int = 0 ; x < w; x ++ ) {             
                 for ( var y:int = 0 ; y < h; y ++ ) {                     
                     var col:int;
                     
                     if ( img_src[y][x] == 1 )
                         col = 0x00ffffff;
                     else
                         col = 0x00000000;

                     _bitmap.setPixel( xpos +x, ypos +y, col );
                 }
             }       
        }
        
        public function thin_pixel_common ( s:Array, x:int, y:int ) : int {
            // pixel removal condition
            // 1. pixel is black            
            if ( s[y][x] == 0 )
                return 0;
            
            var near:Array = new Array( 
            s[y-1][x-1], s[y-1][x], s[y-1][x+1], s[y][x+1], 
            s[y+1][x+1], s[y+1][x], s[y+1][x-1], s[y][x-1], s[y-1][x-1]
            );
 
            var count: int = 0;
            var connect:int = 0;
            for( var i:int = 0; i <= 7; i++ ) {
                if( near[i] == 1 ) count ++;
                if( near[i] == 1 && near[i+1] == 0 ) connect ++;
            }
            
             // 2. near pixels 
            // check black pixels are >=2 and <=6
            if ( count >= 2 && count <= 6 &&
             // 3. connectivity is 1 
                connect == 1 )
                return 1;
                
            return 0;
        }

        public function thin_pixel_loop1 ( s:Array, x:int, y:int ) : int {
             if ( thin_pixel_common ( s, x, y ) == 0 )
                 return 0;
             
             if ( (s[y][x-1] == 0 || s[y-1][x] == 0 || s[y][x+1] == 0) &&
                  (s[y-1][x] == 0 || s[y][x+1] == 0 || s[y+1][x] == 0) ) {
                 return 1;
             }
             return 0;
        }

        public function thin_pixel_loop2 ( s:Array, x:int, y:int ) : int {
             if ( thin_pixel_common ( s, x, y ) == 0 )
                 return 0;
             if ( (s[y][x-1] == 0 || s[y+1][x] == 0 || s[y][x+1] == 0) &&
                  (s[y-1][x] == 0 || s[y][x-1] == 0 || s[y+1][x] == 0) ) {
                 return 1;
            }
             return 0;
        }
        
        public function thin_loop1 ( img_src:Array, width:int, height:int ) : int {
            var del:Array;
            
            del = make_bitmap_array( width, height );
            
            var thin_flag:int = 0; 
            var y:int = 1;
            var x:int = 1;
            for ( y = 1; y < height-1; y++ ) {
                for ( x = 1; x < width-1; x++ ) {
                    if ( thin_pixel_loop1( img_src, x,y ) == 1 ) {
                        del [y][x] = 1;
                    }
                }
            }
            
            thin_flag = del_bitmap_array ( img_src, del, width, height );
            return thin_flag;
        }
        
        public function thin_loop2 ( img_src:Array, width:int, height:int ) : int {
            var del:Array;
            
            del = make_bitmap_array( width, height );
            
            var thin_flag:int = 0; 
            var y:int = 1;
            var x:int = 1;
            for ( y = 1; y < height-1; y++ ) {
                for ( x = 1; x < width-1; x++ ) {
                    if ( thin_pixel_loop2( img_src, x,y ) == 1 ) {
                        del [y][x] = 1;
                    }
                }
            }
            
            thin_flag = del_bitmap_array ( img_src, del, width, height );
            return thin_flag;
        }

        public function del_bitmap_array ( src:Array, del:Array, width:int, height:int ) : int {
            var thin_flag:int = 0; 
            var y:int = 1;
            var x:int = 1;
            for ( y = 1; y < height-1; y++ ) {
                for ( x = 1; x < width-1; x++ ) {
                    if ( del [y][x] == 1 ) {
                        src[y][x] = 0;
                        thin_flag = 1;
                    }
                }
            }
            return thin_flag;
        }
 
        public function make_bitmap_array ( width:int, height:int ) : Array {
            var y:int = 0;
            var x:int = 0;
            var arr:Array = new Array(height);
            for ( y = 0; y < height; y ++ ) {
                arr[y] = new Array(width);
                
                for ( x = 0; x < width; x ++ ) {
                    arr[y][x] = 0;
                }
            }
            
            return arr;
        }

       public function copy_bitmap_array ( s:Array, d:Array, width:int, height:int ) : void {
            var y:int = 0;
            var x:int = 0;
            for ( y = 0; y < height; y ++ ) {
                for ( x = 0; x < width; x ++ ) {
                    d[y][x] = s[y][x];
                }
            }
        }

       // thining algorithm : Zhang Suen
        public function thin_image ( img_src:Array, width:int, height:int ) : void {
            
            var cont : int = 1;
            while (cont) {
                cont = 0;
                if ( thin_loop1( img_src, width, height ) == 1 )
                    cont = 1;
                
                if ( thin_loop2( img_src, width, height ) == 1 )
                    cont = 1;
            }
        }
    }
}

'프로젝트 > motion capture' 카테고리의 다른 글

joint skeleton tracking  (0) 2011.09.07

+ Recent posts