package LCDOCR; ########################################### # Mike Schilli, 2007 (m@perlmeister.com) ########################################### use strict; use Imager; use Log::Log4perl qw(:easy); use YAML qw(LoadFile); ########################################### sub new { ########################################### my($class, %options) = @_; my $refd = LoadFile("/etc/fobs.yml")-> {$options{name}}; my $self = { name => "RSA1", threshold => 0.85, debug => 0, digits => $refd->{digits}, %options, }; # Adapt coordinates to real image my $stretch = ref_dist($self) / ref_dist($refd); for (qw(x_off y_off digit_width digit_height digit_dist)) { $self->{$_} = $refd->{$_} * $stretch; } $self->{angle} = atan2 ( $self->{y2_ref}-$self->{y1_ref}, $self->{x2_ref}-$self->{x1_ref}); bless $self, $class; } ########################################### sub ref_dist { ########################################### my($h) = @_; return sqrt( ($h->{x2_ref} - $h->{x1_ref})**2 + ($h->{y2_ref} - $h->{y1_ref})**2) } ########################################### sub reco { ########################################### my($self) = @_; my @digits; my %seg_orient = qw( 1 h 2 v 3 v 4 h 5 v 6 v 7 h); for (1..$self->{digits}) { my $coords = $self->seg_coords($_); my $segstring = ""; my $bkground = ( xybrightness($self->{image}, @{$coords->{8}}) + xybrightness($self->{image}, @{$coords->{9}}) ) / 2; for my $c (1..7) { my($x, $y) = @{$coords->{$c}}; if(pixel_dark($self->{image}, $x, $y, $bkground, $self->{debug}, $c, $seg_orient{$c}, $self->{threshold})) { $segstring .= "$c"; } if($self->{debug}) { my $red = Imager::Color->new( 255, 0, 0); $self->{image}->circle( color=>$red, r=>1, x=>$x, y=>$y); } } my $digit = seg2digit($segstring); push @digits, defined $digit ? $digit : "X"; } return \@digits; } ########################################### sub seg_coords { ########################################### my($self, $digit) = @_; my $x = $self->{x_off} + ($digit-1) * $self->{digit_dist}; my $y = $self->{y_off}; my $w = $self->{digit_width}; my $h = $self->{digit_height}; my $r = sub { [ $self->rotate(@_) ] }; return { 1 => $r->($x, $y), 2 => $r->($x + $w/2, $y + $h/4), 3 => $r->($x + $w/2, $y + 3*$h/4), 4 => $r->($x, $y + $h), 5 => $r->($x - $w/2, $y + 3*$h/4), 6 => $r->($x - $w/2, $y + $h/4), 7 => $r->($x, $y + $h/2), # ref points 8 => $r->($x, $y + $h/4), 9 => $r->($x, $y + 3*$h/4), }; } ########################################### sub seg2digit { ########################################### my %h = ( "23" => 1, "12457" => 2, "12347" => 3, "2367" => 4, "13467" => 5, "134567" => 6, "123" => 7, "1234567" => 8, "123467" => 9, "123456" => 0, ); return $h{$_[0]}; } ########################################### sub rotate { ########################################### my($self, $xd, $yd) = @_; my $r = sqrt($xd*$xd + $yd*$yd); my $phi = atan2($yd,$xd); $phi += $self->{angle}; my $xd_rot = $r * cos($phi); my $yd_rot = $r * sin($phi); my $x_abs = $self->{x1_ref} + $xd_rot; my $y_abs = $self->{y1_ref} + $yd_rot; return($x_abs, $y_abs); } use Inline C => <<'EOT' => WITH => 'Imager'; int pixel_dark(Imager im, int x, int y, int threshold, int debug, int seg, char *direction, float percent) { i_color val; int br, i, j, dark=0, min=-1; int imin=0, imax=1, jmin=0, jmax=1; float rel; if(direction == 'h') { jmin = -1; jmax = 2; } else { imin = -1; imax = 2; } for(i=imin; i br) min = br; } } rel = 1.0*min/threshold; if(rel < percent) dark = 1; if(debug) { printf("TH[%d]: %d (%d %.1f%%: %d)\n", seg, min, threshold, rel*100.0, dark); } return dark; } int brightness(i_color *val) { return((val->channel[0] + val->channel[1] + val->channel[2])/3); } int xybrightness(Imager im, int x, int y) { i_color val; i_gpix(im, x, y, &val); return brightness(&val); } EOT 1;