package WebService::Kaolabo; use warnings; use strict; use Carp; use version; our $VERSION = qv('0.0.3'); use LWP::UserAgent; use HTTP::Request; use Data::Average; use Imager; use File::Spec; our $errstr; use base qw(Class::Accessor); __PACKAGE__->mk_accessors( qw( socks_proxy proxy target_file convert_file uri apikey imager request_content response_xml face_data area face_area unface_area ave_face_width ave_face_height error)); sub new { my $self = shift->SUPER::new(@_); my $target_file = $self->target_file; $self->uri('https://kaolabo.com/api/detect?apikey=') unless ( $self->uri ); my $imager = Imager->new; if ( $target_file && $target_file !~ /(jpg|jpeg)$/ ) { $errstr = 'Target file is not jpeg'; return; } unless ( $imager->read( file => $target_file ) ) { $errstr = 'Cannot read target file ' . $imager->errstr(); return; } $self->area([]); $self->face_area([]); $self->unface_area([]); $self->imager($imager); $self; } sub scale { my $self = shift; my $imager = $self->imager; unless ( $imager ) { $errstr = 'Not found Imager object'; return; } unless ( @_ ) { $errstr = 'Not found scale param'; return; } my $imager_s = $imager->scale(@_); $self->imager($imager_s); return $imager_s; } sub write { my $self = shift; my $convert_file = shift; $convert_file ||= $self->convert_file; my $imager = $self->imager; $imager->write( file => $convert_file, jpegquality => 100 ) or die $imager->errstr; return; } sub access { my $self = shift; if ( $self->socks_proxy ) { if ( eval { require LWP::Protocol::https::SocksChain } ) { LWP::Protocol::implementor( https => 'LWP::Protocol::https::SocksChain' ); @LWP::Protocol::https::SocksChain::EXTRA_SOCK_OPTS = ( Chain_Len => 1, Debug => 0, Chain_File_Data => $self->socks_proxy, Random_Chain => 1, Auto_Save => 1, Restore_Type => 1 ); } } my $uri = $self->uri . $self->apikey; my $request_content; my $imager = $self->imager; $imager->write( type => 'jpeg', data => \$request_content ); my $request = HTTP::Request->new( 'POST' => $uri ); $request->header( 'Content-Type' => 'image/jpeg' ); $request->content($request_content) if ( $request_content ); my $ua = LWP::UserAgent->new; $ua->proxy( [ 'http', 'ftp' ], $self->proxy ) if ( $self->proxy ); my $response = $ua->request($request); unless ( $response->is_success ) { $errstr = 'Failed access ' . $response->status_line; } else { $self->response_xml( $response->content ); $self->_parser(); $self->_area_score(); } return $response; } sub _parser { my $self = shift; my $content = $self->response_xml(); my $face_data = []; my $ave_width = Data::Average->new; my $ave_height = Data::Average->new; while ( $content =~ s/add($width); $ave_height->add($height); push @{$face_data}, { height => $height, score => $score, width => $width, face_x => $face_x, face_y => $face_y, left_eye_x => $left_eye_x, left_eye_y => $left_eye_y, right_eye_x => $right_eye_x, right_eye_y => $right_eye_y, # left_eye_x => $left_eye_x, # left_eye_y => $left_eye_y, # right_eye_x => $right_eye_x, # right_eye_y => $right_eye_y, center_x => $center_x, center_y => $center_y, }; } $self->ave_face_width( $ave_width->avg ); $self->ave_face_height( $ave_height->avg ); $self->face_data($face_data); return; } sub _area_score { my $self = shift; my $w = $self->imager->getwidth(); my $h = $self->imager->getheight(); my $ddx = $w / 3; my $ddy = $h / 3; my @area; my $area_number = 0; for my $i ( 1 .. 3 ) { $area_number++; push @area, { area_number => $area_number, min_x => $ddx * ( $i - 1 ), min_y => 0, max_x => $ddx * $i, max_y => $ddy, point => 0 }; } for my $i ( 1 .. 3 ) { $area_number++; push @area, { area_number => $area_number, min_x => $ddx * ( $i - 1 ), min_y => $ddy, max_x => $ddx * $i, max_y => $ddy * 2, point => 0 }; } for my $i ( 1 .. 3 ) { $area_number++; push @area, { area_number => $area_number, min_x => $ddx * ( $i - 1 ), min_y => $ddy * 2, max_x => $ddx * $i, max_y => $ddy * 3, point => 0 }; } my $face_data = $self->face_data(); for my $f ( @{$face_data} ) { for my $a (@area) { if ( $a->{max_x} > $f->{center_x} && $a->{max_y} > $f->{center_y} ) { $a->{point}++; last; } } } $self->area( \@area ); my @unface_area = grep( { $_->{point} == 0 } @area ); $self->unface_area( \@unface_area ); my @face_area = grep( { $_->{point} != 0 } @area ); $self->face_area( \@face_area ); return; } sub effect_face { my $self = shift; my $args = shift; my $effect = $args->{type} || 'line'; my $color = $args->{color} || '#000000'; my $imager = $self->imager; my $face_data = $self->face_data || []; for my $f ( @{$face_data} ) { $imager->box( xmin => $f->{face_x}, ymin => $f->{face_y}, xmax => $f->{face_x} + $f->{width}, ymax => $f->{face_y} + $f->{height}, color => $color, filled => 1, ) if ( $effect eq "box" ); my $border_h = $f->{height} * 0.1; my $ymin = 0; my $ymax = 0; my $i = abs( $f->{right_eye_y} - $f->{left_eye_y} ); if ( $f->{left_eye_y} < $f->{right_eye_y} ) { $ymin = $f->{left_eye_y} - $border_h; $ymax = $f->{right_eye_y} + $border_h; } else { $ymin = $f->{right_eye_y} - $border_h; $ymax = $f->{left_eye_y} + $border_h; } $imager->box( xmin => $f->{face_x}, ymin => $ymin, xmax => $f->{face_x} + $f->{width}, ymax => $ymax, color => $color, filled => 1, ) if ( $effect eq "line" ); } return; } 1; __END__ =head1 NAME WebService::Kaolabo - This module call Kaolabo API (http://kaolabo.com/). =head1 SYNOPSIS use WebService::Kaolabo; $kaolab = WebService::Kaolabo->new({ target_file => 'sample.jpg', apikey => 'hogefuga' }); unless ( $kaolab->scale( xpixels => 50, ypixels => 50, type => 'max') ) { warn "Failed scale $WebService::Kaolabo::errstr"; } my $res = $kaolab->access(); if ( $res->is_success ) { warn "Success "; } #$kaolab->unface_area(); for my $k ( @{$kaolab->face_area()} ){ $k->{area_number} $k->{min_x}; $k->{min_y}; $k->{max_x}; $k->{max_y}; $k->{point}; } my $face_data = $kaolab->face_data; for my $f ( @{$face_data} ){ $f->{face_x}; $f->{face_y}; $f->{height}; $f->{width}; $f->{right_eye_y}; $f->{left_eye_y}; } $kaolab->effect_face({type=>'box', color=>'#FF0000'}); $kaolab->write('output.jpg'); #my $imager = $kaolab->imager; #$imager->write(type=>'jpeg', file=>'output.jpg'); =head1 METHODS =over 4 =item new({target_file => '...', apikey => '....'}) The image file and api_key are passed. And Create new instance. The image should be JPEG. =item access Call The Kaolab API . The return value is a response object. See L. =item scale Call L scale method. See L. =item effect_face This method draws the line or box on the face. The line is drawn on eyes. $kaolab->effect_face({type=>'line', color=>'#FF0000'}); The box is drawn on faces. $kaolab->effect_face({type=>'box', color=>'#FF0000'}); =item write('...') Write an image to a file. =item imager The L instance is returned. =item face_area The image file is delimited to nine areas. Return face area. =item unface_area Return no face area. =item ave_face_width Return average width of all faces. =item ave_face_height Return average height of all faces. =item errstr Error message. warn "$WebService::Kaolabo::errstr"; =back =head1 SEE ALSO Kaolab API L Kaolab L =head1 AUTHOR Akihito Takeda C<< >> =head1 LICENCE AND COPYRIGHT Copyright (c) 2008, Akihito Takeda C<< >>. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L.