Imagerで画像を回転させる

回転させるだけならrotateで出来るんだけど、
小ネギを直立させつつ、重なっている小ネギの判定も行う。

やることは、ラベリングした画像の回転と範囲の取得を繰り返して、
幅が最小になる角度を取得する。
重なりがあると、幅と高さの比がしきい値を下回らないので、
その範囲に存在する小ネギは無視する。

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 $cur_angle = .0;
my $cur_value = calc_evaluation_value( $img, $cur_angle );

my $step = 32.0;
while ( 0.1 < abs($step) ) {
    my $new_angle = $cur_angle + $step;
    my $new_value = calc_evaluation_value( $img, $new_angle );
    printf( "Current %5.1f: %3d, New %5.1f: %3d\n",
        $cur_angle, $cur_value,
        $new_angle, $new_value );

    if ( $new_value < $cur_value ) {
        $cur_value = $new_value;
        $cur_angle = $new_angle;
    }
    else {
        $step *= -0.5;
    }
}

$img->rotate( degrees => $cur_angle, back => 'black' )->write( file => $0 . '.png' );

sub calc_evaluation_value {
    my ( $img, $rot_angle ) = @_;

    my $img_tmp = $img->rotate(
        degrees => $rot_angle, back => 'black' );

    my $xmin = int( $img_tmp->getwidth() / 2 );
    my $xmax = int( $img_tmp->getwidth() / 2 );
    my $iy = $img_tmp->getheight();
    while ( 0 < $iy-- ) {
        my $tmp = $img_tmp->getsamples( y => $iy, channels => [0] );
        my @pixels = unpack( 'C*', $tmp );

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

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

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

    return $xmax - $xmin + 1;
}

あんまし、うまく書けた感じはしないけど、
前回に切り抜いて出力した画像を入力すると、ちゃんと直立する。

たとえば、これは、
20140729-1

こうなる。
20140729-2

こっちは、
20140729-3

こうなる。
20140729-4

後者は重なっているので、幅と高さの比を見て重なり判定ができる。
もちろん、その前に余白を除いた画像を作り直す必要はあるけど。

おしまい。

Leave a Comment