Imagerでラベリングするのおさらい

久々なので、リファクタリングしておさらいしてみる。

あんまし、お行儀の良い書き方じゃないけど、
こんな感じで、塗りつぶし領域の結果を取得すると、
ちょっと無理矢理だけど、手を抜くことが出来る。

use v5.14;
use strict;
use warnings;
use Imager;

if ( (not @ARGV) or (not -e $ARGV[0]) ) {
    say "Usage:
    perl $0 file_path";
    exit( 0 );
}

my $img = Imager->new( file => $ARGV[0] )
    or die Imager->errstr();
my $img_dst = $img->convert( preset => 'rgb' ); # gray -> rgb

my $h = $img->getheight();
my ( $ix, $iy ) = ( 0, 0 );
my $area_no = 1;
while ( $iy < $h ) {
    my $tmp = $img->getsamples( y => $iy, channels => [0] );
    my @pixels = unpack( 'C*', $tmp );

    my $found = 0;
    while ( $ix < scalar(@pixels) ) {
        if ( $pixels[$ix] == 255 ) {
            my $c = Imager::Color->new( $area_no, $area_no, $area_no );
            $img->flood_fill( x => $ix, y => $iy, color => $c );

            my $area = calc_filled_area( $img, $area_no, $ix, $iy );
            print_area_info( $area_no, $area );
            $img_dst->box( color => 'red', %{$area} );

            $area_no++;
            $found = 1;
            last;
        }

        $ix++;
    }

    if ( not $found ) {
        $ix = 0;
        $iy++;
    }

    if ( 255 < $area_no ) {
        die 'area_no = ', $area_no, ' too many areas! sorry.';
    }
}

sub calc_filled_area {
    my ( $img, $area_no, $filled_x, $filled_y ) = @_;

    my ( $xmin, $xmax ) = ( $filled_x, $filled_x );
    my ( $ymin, $ymax ) = ( $filled_y, $filled_y );

    my $h = $img->getheight();
    my $iy = $ymin;
    while ( $iy < $h ) {
        my $tmp = $img->getsamples( y => $iy, channels => [0] );
        my @pixels = unpack( 'C*', $tmp );

        my $found = grep { $_ == $area_no } @pixels;
        if ( $found ) {
            my $st = 0;
            $st++ while $pixels[$st] != $area_no;

            my $en = scalar(@pixels) - 1;
            $en-- while $pixels[$en] != $area_no;

            $xmin = $st if $st < $xmin;
            $xmax = $en if $xmax < $en;
        }

        if ( not $found ) {
            last;
        }
        else {
            $ymax = $iy;
            $iy++;
        }
    }

    return +{
        xmin => $xmin,
        ymin => $ymin,
        xmax => $xmax,
        ymax => $ymax
    };
}

sub print_area_info {
    my ( $area_no, $area ) = @_;
    printf( "area_no = %3d, (x, y) = (%4d, %4d), w = %4d, h = %4d",
        $area_no,
        $area->{xmin},
        $area->{ymin},
        $area->{xmax} - $area->{xmin} + 1,
        $area->{ymax} - $area->{ymin} + 1 );
    print "\n";
}

$img_dst->write( file => $0 . '.png' );

結果は、こんな感じ。

20140823-1

入力画像がモノクロだったので、
赤線で枠を描画する前に、RGBに変換してる。

やりたいことは、
1つ1つ切り抜いて、垂直になるように回転して、
高さでソートしつつ、それをGIFアニメとしてファイル出力したい。

次は、回転後の画像を1つの画像に集めることかな。

おしまい。

Leave a Comment