File Coverage

blib/lib/WebService/Kaolabo.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package WebService::Kaolabo;
2              
3 2     2   47949 use warnings;
  2         5  
  2         59  
4 2     2   11 use strict;
  2         3  
  2         87  
5 2     2   11 use Carp;
  2         7  
  2         200  
6              
7 2     2   1717 use version; our $VERSION = qv('0.0.3');
  2         4718  
  2         10  
8              
9 2     2   2252 use LWP::UserAgent;
  2         110476  
  2         67  
10 2     2   18 use HTTP::Request;
  2         4  
  2         45  
11 2     2   2224 use Data::Average;
  0            
  0            
12             use Imager;
13             use File::Spec;
14             our $errstr;
15              
16             use base qw(Class::Accessor);
17              
18             __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));
19              
20             sub new {
21             my $self = shift->SUPER::new(@_);
22              
23             my $target_file = $self->target_file;
24              
25             $self->uri('https://kaolabo.com/api/detect?apikey=')
26             unless ( $self->uri );
27              
28             my $imager = Imager->new;
29             if ( $target_file && $target_file !~ /(jpg|jpeg)$/ ) {
30             $errstr = 'Target file is not jpeg';
31             return;
32             }
33             unless ( $imager->read( file => $target_file ) ) {
34             $errstr = 'Cannot read target file ' . $imager->errstr();
35             return;
36             }
37              
38             $self->area([]);
39             $self->face_area([]);
40             $self->unface_area([]);
41             $self->imager($imager);
42             $self;
43             }
44              
45             sub scale {
46             my $self = shift;
47             my $imager = $self->imager;
48              
49             unless ( $imager ) {
50             $errstr = 'Not found Imager object';
51             return;
52             }
53              
54             unless ( @_ ) {
55             $errstr = 'Not found scale param';
56             return;
57             }
58              
59             my $imager_s = $imager->scale(@_);
60             $self->imager($imager_s);
61              
62             return $imager_s;
63             }
64              
65             sub write {
66             my $self = shift;
67             my $convert_file = shift;
68             $convert_file ||= $self->convert_file;
69             my $imager = $self->imager;
70             $imager->write( file => $convert_file, jpegquality => 100 )
71             or die $imager->errstr;
72             return;
73             }
74              
75             sub access {
76             my $self = shift;
77             if ( $self->socks_proxy ) {
78             if ( eval { require LWP::Protocol::https::SocksChain } ) {
79             LWP::Protocol::implementor(
80             https => 'LWP::Protocol::https::SocksChain' );
81             @LWP::Protocol::https::SocksChain::EXTRA_SOCK_OPTS = (
82             Chain_Len => 1,
83             Debug => 0,
84             Chain_File_Data => $self->socks_proxy,
85             Random_Chain => 1,
86             Auto_Save => 1,
87             Restore_Type => 1
88             );
89             }
90             }
91              
92             my $uri = $self->uri . $self->apikey;
93              
94             my $request_content;
95             my $imager = $self->imager;
96             $imager->write( type => 'jpeg', data => \$request_content );
97              
98             my $request = HTTP::Request->new( 'POST' => $uri );
99             $request->header( 'Content-Type' => 'image/jpeg' );
100              
101             $request->content($request_content)
102             if ( $request_content );
103              
104             my $ua = LWP::UserAgent->new;
105             $ua->proxy( [ 'http', 'ftp' ], $self->proxy ) if ( $self->proxy );
106              
107             my $response = $ua->request($request);
108             unless ( $response->is_success ) {
109             $errstr = 'Failed access ' . $response->status_line;
110             }
111             else {
112             $self->response_xml( $response->content );
113             $self->_parser();
114             $self->_area_score();
115             }
116             return $response;
117             }
118              
119             sub _parser {
120             my $self = shift;
121              
122             my $content = $self->response_xml();
123             my $face_data = [];
124             my $ave_width = Data::Average->new;
125             my $ave_height = Data::Average->new;
126             while ( $content =~ s/<face(.+?)<\/face// ) {
127             my $node = $1;
128             my ( $height, $score, $width, $face_x, $face_y, $left_eye_x, $left_eye_y, $right_eye_x, $right_eye_y)
129             = ( 0, 0, 0, 0, 0, 0, 0, 0, 0 );
130              
131             ( $height, $score, $width, $face_x, $face_y ) = ( $1, $2, $3, $4, $5 )
132             if ( $node =~
133             /height="(\d+)" score="(\d+)" width="(\d+)" x="(\d+)" y="(\d+)"/ );
134              
135             ( $left_eye_x, $left_eye_y ) = ( $1, $2 )
136             if ( $node =~ /left\-eye x="(\d+)" y="(\d+)"/i );
137              
138             ( $right_eye_x, $right_eye_y ) = ( $1, $2 )
139             if ( $node =~ /right\-eye x="(\d+)" y="(\d+)"/i );
140              
141             my $center_x = $width / 2 + $face_x;
142             my $center_y = $height / 2 + $face_y;
143              
144             # Maybe API bugs ??
145             if ( $left_eye_x == $right_eye_x ) {
146             $right_eye_y = $right_eye_y<$left_eye_y?$right_eye_y:$left_eye_y;
147             $left_eye_y = $right_eye_y<$left_eye_y?$right_eye_y:$left_eye_y;
148             }
149              
150             $ave_width->add($width);
151             $ave_height->add($height);
152             push @{$face_data},
153             {
154             height => $height,
155             score => $score,
156             width => $width,
157             face_x => $face_x,
158             face_y => $face_y,
159             left_eye_x => $left_eye_x,
160             left_eye_y => $left_eye_y,
161             right_eye_x => $right_eye_x,
162             right_eye_y => $right_eye_y,
163             # left_eye_x => $left_eye_x,
164             # left_eye_y => $left_eye_y,
165             # right_eye_x => $right_eye_x,
166             # right_eye_y => $right_eye_y,
167             center_x => $center_x,
168             center_y => $center_y,
169             };
170             }
171             $self->ave_face_width( $ave_width->avg );
172             $self->ave_face_height( $ave_height->avg );
173             $self->face_data($face_data);
174             return;
175             }
176              
177             sub _area_score {
178             my $self = shift;
179              
180             my $w = $self->imager->getwidth();
181             my $h = $self->imager->getheight();
182             my $ddx = $w / 3;
183             my $ddy = $h / 3;
184              
185             my @area;
186             my $area_number = 0;
187             for my $i ( 1 .. 3 ) {
188             $area_number++;
189             push @area,
190             {
191             area_number => $area_number,
192             min_x => $ddx * ( $i - 1 ),
193             min_y => 0,
194             max_x => $ddx * $i,
195             max_y => $ddy,
196             point => 0
197             };
198             }
199             for my $i ( 1 .. 3 ) {
200             $area_number++;
201             push @area,
202             {
203             area_number => $area_number,
204             min_x => $ddx * ( $i - 1 ),
205             min_y => $ddy,
206             max_x => $ddx * $i,
207             max_y => $ddy * 2,
208             point => 0
209             };
210             }
211             for my $i ( 1 .. 3 ) {
212             $area_number++;
213             push @area,
214             {
215             area_number => $area_number,
216             min_x => $ddx * ( $i - 1 ),
217             min_y => $ddy * 2,
218             max_x => $ddx * $i,
219             max_y => $ddy * 3,
220             point => 0
221             };
222             }
223              
224             my $face_data = $self->face_data();
225              
226             for my $f ( @{$face_data} ) {
227             for my $a (@area) {
228             if ( $a->{max_x} > $f->{center_x} && $a->{max_y} > $f->{center_y} ) {
229             $a->{point}++;
230             last;
231             }
232             }
233             }
234             $self->area( \@area );
235              
236             my @unface_area = grep( { $_->{point} == 0 } @area );
237             $self->unface_area( \@unface_area );
238              
239             my @face_area = grep( { $_->{point} != 0 } @area );
240             $self->face_area( \@face_area );
241             return;
242             }
243              
244             sub effect_face {
245             my $self = shift;
246             my $args = shift;
247             my $effect = $args->{type} || 'line';
248             my $color = $args->{color} || '#000000';
249             my $imager = $self->imager;
250              
251             my $face_data = $self->face_data || [];
252             for my $f ( @{$face_data} ) {
253             $imager->box(
254             xmin => $f->{face_x},
255             ymin => $f->{face_y},
256             xmax => $f->{face_x} + $f->{width},
257             ymax => $f->{face_y} + $f->{height},
258             color => $color,
259             filled => 1,
260             ) if ( $effect eq "box" );
261              
262             my $border_h = $f->{height} * 0.1;
263              
264             my $ymin = 0;
265             my $ymax = 0;
266             my $i = abs( $f->{right_eye_y} - $f->{left_eye_y} );
267             if ( $f->{left_eye_y} < $f->{right_eye_y} ) {
268             $ymin = $f->{left_eye_y} - $border_h;
269             $ymax = $f->{right_eye_y} + $border_h;
270             }
271             else {
272             $ymin = $f->{right_eye_y} - $border_h;
273             $ymax = $f->{left_eye_y} + $border_h;
274             }
275              
276             $imager->box(
277             xmin => $f->{face_x},
278             ymin => $ymin,
279             xmax => $f->{face_x} + $f->{width},
280             ymax => $ymax,
281             color => $color,
282             filled => 1,
283             ) if ( $effect eq "line" );
284             }
285             return;
286             }
287              
288             1;
289             __END__
290              
291             =head1 NAME
292              
293             WebService::Kaolabo - This module call Kaolabo API (http://kaolabo.com/).
294              
295              
296             =head1 SYNOPSIS
297              
298             use WebService::Kaolabo;
299             $kaolab = WebService::Kaolabo->new({
300             target_file => 'sample.jpg',
301             apikey => 'hogefuga'
302             });
303              
304             unless ( $kaolab->scale( xpixels => 50, ypixels => 50, type => 'max') ) {
305             warn "Failed scale $WebService::Kaolabo::errstr";
306             }
307              
308             my $res = $kaolab->access();
309             if ( $res->is_success ) {
310             warn "Success ";
311             }
312            
313             #$kaolab->unface_area();
314             for my $k ( @{$kaolab->face_area()} ){
315             $k->{area_number}
316             $k->{min_x};
317             $k->{min_y};
318             $k->{max_x};
319             $k->{max_y};
320             $k->{point};
321             }
322            
323             my $face_data = $kaolab->face_data;
324             for my $f ( @{$face_data} ){
325             $f->{face_x};
326             $f->{face_y};
327             $f->{height};
328             $f->{width};
329             $f->{right_eye_y};
330             $f->{left_eye_y};
331             }
332            
333             $kaolab->effect_face({type=>'box', color=>'#FF0000'});
334             $kaolab->write('output.jpg');
335             #my $imager = $kaolab->imager;
336             #$imager->write(type=>'jpeg', file=>'output.jpg');
337              
338              
339             =head1 METHODS
340              
341              
342             =over 4
343              
344             =item new({target_file => '...', apikey => '....'})
345              
346             The image file and api_key are passed. And Create new instance.
347             The image should be JPEG.
348              
349             =item access
350              
351             Call The Kaolab API . The return value is a response object.
352             See L<HTTP::Response>.
353              
354             =item scale
355              
356             Call L<Imager> scale method. See L<Imager::Transformations/scale>.
357              
358             =item effect_face
359              
360             This method draws the line or box on the face.
361              
362             The line is drawn on eyes.
363              
364             $kaolab->effect_face({type=>'line', color=>'#FF0000'});
365              
366             The box is drawn on faces.
367              
368             $kaolab->effect_face({type=>'box', color=>'#FF0000'});
369              
370             =item write('...')
371              
372             Write an image to a file.
373              
374             =item imager
375              
376             The L<Imager> instance is returned.
377              
378             =item face_area
379              
380             The image file is delimited to nine areas. Return face area.
381              
382             =item unface_area
383              
384             Return no face area.
385              
386             =item ave_face_width
387              
388             Return average width of all faces.
389              
390             =item ave_face_height
391              
392             Return average height of all faces.
393              
394             =item errstr
395              
396             Error message.
397              
398             warn "$WebService::Kaolabo::errstr";
399              
400             =back
401              
402             =head1 SEE ALSO
403              
404             Kaolab API L<http://kaolabo.com/webapi>
405             Kaolab L<http://kaolabo.com/>
406              
407             =head1 AUTHOR
408              
409             Akihito Takeda C<< <takeda.akihito@gmail.com> >>
410              
411             =head1 LICENCE AND COPYRIGHT
412              
413             Copyright (c) 2008, Akihito Takeda C<< <takeda.akihito@gmail.com> >>. All rights reserved.
414              
415             This module is free software; you can redistribute it and/or
416             modify it under the same terms as Perl itself. See L<perlartistic>.
417              
418