Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Simple Photo Processing and Web Display with Perl

12,633 views

Published on

I have a small photo gallery on my website and in this presentation, I share
some steps I used in creating a nearly automatic workflow of getting
pictures from my camera to his gallery using Perl.

Published in: Technology, Art & Photos
  • DOWNLOAD FULL BOOKS, INTO AVAILABLE FORMAT ......................................................................................................................... ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD FULL BOOKS, INTO AVAILABLE FORMAT ......................................................................................................................... ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD FULL BOOKS, INTO AVAILABLE FORMAT ......................................................................................................................... ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y8nn3gmc } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • thanq its very nice along with useful to me in thinking about innovation comes along with think of in which.... really nice function.... tanq for this.....
    Teisha
    http://dashinghealth.com http://healthimplants.com
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • this sucks. dont ever make me watch this crap again.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Simple Photo Processing and Web Display with Perl

  1. 1. Simple Photo Processing and Web Display with Perl Kent Cowgill
  2. 2. Unfortunately, nothing super fancy.
  3. 3. The first camera I used
  4. 4. The pictures were ... ok
  5. 5. The phone stayed in my pocket. Along with pocket lint. A lot of pocket lint.
  6. 6. And a lot of lint got in the lens
  7. 7. And the pictures started in the blurry And a lot of lint got getting lens
  8. 8. Especially with all my daily activities.
  9. 9. Especially with all my daily activities.
  10. 10. And the pictures got blurrier
  11. 11. ... and blurrier ...
  12. 12. And practically unrecognizable.
  13. 13. So I got a new phone.
  14. 14. This has been my camera
  15. 15. The pictures were better
  16. 16. No lint problem, even in harsh conditions
  17. 17. Until my phone dropped out of my pocket and was picked up by Ricardo Signes at YAPC
  18. 18. But that blurriness was user error.
  19. 19. Thankfully, he returned my phone to me.
  20. 20. What a nice guy.
  21. 21. My RAZR has served me well.
  22. 22. Until Recently
  23. 23. More on that later.
  24. 24. Step 1: Get the picture from the phone to the server.
  25. 25. This is what I looked like
  26. 26. Taking pictures with my RAZR
  27. 27. The obligatory cat
  28. 28. www.kentcowgill.org
  29. 29. Step 2: Get the picture from the email to the filesystem.
  30. 30. strip image from mail cowgill motorola v551 2/9/05 1
  31. 31. use MIME::Parser; use MIME::Entity; use MIME::Base64 qw(decode_base64); use Image::Magick::Thumbnail;
  32. 32. my $parser = MIME::Parser->new(); $parser->output_dir( '/www/kentcowgill/photos/data' ); my $message = $parser->parse( *STDIN ) };
  33. 33. DFS? for my $part( $message->parts_DFS ){ if( $part->bodyhandle ){ if( $part->mime_type eq 'image/jpeg' ){ $filename = $part->bodyhandle->path; $data .= $part->as_string; $data =~ s/.*nn(.*)/$1/ms; } Ew. $data = decode_base64($data); ...
  34. 34. ... open ARCHIVE, '>', $archive_image; binmode ARCHIVE; Error checking print ARCHIVE $data; is left as an exercise close ARCHIVE; for the reader. my $src = new Image::Magick; $src->Read($archive_image); ...
  35. 35. ... my( $thumb, $x, $y ) =Image::Magick::Thumbnail::create( $src, 64 ); $thumb->Write($archive_thumb); } }
  36. 36. Worked great.
  37. 37. Flawlessly.
  38. 38. For a while.
  39. 39. Until I upgraded... Perl Image::Magick And with all their dependencies... God only knows what else.
  40. 40. WTF?
  41. 41. Then something broke. WTF?
  42. 42. It didn't make thisbroke. Then something blurry. WTF?
  43. 43. No, thatsomething broke. Then make this blurry. It didn'twas poor technique. WTF?
  44. 44. No, thatsomethingmy photos. It started make off broke. Then cutting this blurry. It didn'twas poor technique. WTF?
  45. 45. It was really annoying. WTF?
  46. 46. And inconsistent. WTF?
  47. 47. Cockroaches Termites (Not to scale) Bunnies (Also not to scale)
  48. 48. Tough to reliably reproduce.
  49. 49. So I spent my time and energy elsewhere.
  50. 50. Step 3: Get the picture from the filesystem to the web browser.
  51. 51. This is the site that I made
  52. 52. To view the pictures that I took
  53. 53. Click on a thumbnail...
  54. 54. And see the full image.
  55. 55. But not that one. ;-)
  56. 56. <?php $start = $_GET[quot;startquot;] ? $_GET[quot;startquot;] : quot;0quot;; $filearray = array(); $dir = quot;/www/kentcowgill/photos/archquot;; $mydir = quot;/photos/archquot;; $thumbdir = quot;/photos/thumbsquot;; if( $handle = opendir( $dir ) ){ while( false !== ( $file = readdir( $handle ) ) ){ if( $file != quot;.quot; && $file != quot;..quot;){ array_push( $filearray, $file ); } } closedir( $handle ); } ...
  57. 57. PHP?
  58. 58. Didn't I say Perl at the beginning?
  59. 59. $_ == 0 && do { $table .= quot;<tr><td align=center height=$CELLHEIGHT quot; . quot;width=$CELLWIDTH valign=bottom>quot;; last SWITCH; }
  60. 60. $_ == 0 && do { $table .= quot;<tr><td align=center height=$CELLHEIGHT quot; . quot;width=$CELLWIDTH valign=bottom>quot;; last SWITCH; } My only excuse is that I wrote it a really long time ago
  61. 61. :-p~
  62. 62. The picture viewer was updated as well.
  63. 63. I also got a dog. Cat Dog
  64. 64. And I stored the users' preferences...
  65. 65. User Preferences
  66. 66. Step 4: Fix the image stripping bug.
  67. 67. strip image from mail cowgill motorola RAZR v3 7/25/06 2
  68. 68. Add module version requirements.
  69. 69. use MIME::Parser; use MIME::Entity; use MIME::Base64 qw(decode_base64); use Image::Magick::Thumbnail;
  70. 70. use MIME::Parser 5.415; use MIME::Entity 5.415; use MIME::Base64 3.07 qw(decode_base64); use Image::Magick::Thumbnail;
  71. 71. Add debugging and logging.
  72. 72. my $parser = MIME::Parser->new(); $parser->output_dir( '/www/kentcowgill/photos/data' ); my $message = $parser->parse( *STDIN ) };
  73. 73. my $parser = MIME::Parser->new(); $parser->output_to_core(1); $parser->output_dir( '/www/kentcowgill/photos/data' ); my $message; eval { $message = $parser->parse( *STDIN ) }; if( $@ ){ my $results = $parser->results; open ERR, '>', '/www/kentcowgill/photos/err.txt'; print ERR $results; Additional error close ERR; } checking is left as an exercise for the reader.
  74. 74. for my $part( $message->parts_DFS ){ if( $part->bodyhandle ){ if( $part->mime_type eq 'image/jpeg' ){ $filename = $part->bodyhandle->path; $data .= $part->as_string; $data =~ s/.*nn(.*)/$1/ms; } $data = decode_base64($data); ...
  75. 75. for my $part( $message->parts_DFS ){ if( $part->bodyhandle ){ if( $part->mime_type eq 'image/jpeg' ){ $filename = $part->bodyhandle->path; $data .= $part->as_string; print LOG2 $data; print LOG3 $filename; my @raw_part = split( /n/, $data ); my @edited_part; for my $line( @raw_part ){ if( $line =~ m/^$/ ){ $found++; next; } next unless $found; push @edited_part, $line; } $data =~ s/.*nn(.*)/$1/ms; $data = join( quot;nquot;, @edited_part ); print LOG $data; } $data = decode_base64($data);
  76. 76. ... my $src = new Image::Magick; $src->Read($archive_image); ...
  77. 77. ... my $src = new Image::Magick; my $debug = $src->Read($archive_image); print LOG quot;WARNING: ImageMagick::Read quot; . quot;had problem: $debugnquot; if $debug; ...
  78. 78. ... my( $thumb, $x, $y ) =Image::Magick::Thumbnail::create( $src, 64 ); $thumb->Write($archive_thumb); } }
  79. 79. No debugging here. ... my( $thumb, $x, $y ) =Image::Magick::Thumbnail::create( $src, 64 ); $thumb->Write($archive_thumb); } }
  80. 80. No problem with thumbnails.  The cut-off images scaled fine.
  81. 81. The result?
  82. 82. No hints.
  83. 83. No clues.
  84. 84. No fix.
  85. 85. :-(
  86. 86. Step 5: Replace the image stripping program.
  87. 87. strip image from mail cowgill motorola RAZR v3 1/5/07 3
  88. 88. Replace the modules used.
  89. 89. use MIME::Parser 5.415; use MIME::Entity 5.415; use MIME::Base64 3.07 qw(decode_base64); use Image::Magick::Thumbnail;
  90. 90. use Email::MIME; use Email::MIME::Attachment::Stripper; use MIME::Base64 qw(decode_base64); use Imager; Still Used
  91. 91. my $parser = MIME::Parser->new(); $parser->output_to_core(1); $parser->output_dir( '/www/kentcowgill/photos/data' ); my $message; eval { $message = $parser->parse( *STDIN ) }; if( $@ ){ my $results = $parser->results; open ERR, '>', '/www/kentcowgill/photos/err.txt'; print ERR $results; close ERR; }
  92. 92. for my $part( $message->parts_DFS ){ if( $part->bodyhandle ){ if( $part->mime_type eq 'image/jpeg' ){ $filename = $part->bodyhandle->path; $data .= $part->as_string; print LOG2 $data; print LOG3 $filename; my @raw_part = split( /n/, $data ); my @edited_part; for my $line( @raw_part ){ if( $line =~ m/^$/ ){ $found++; next; } next unless $found; push @edited_part, $line; } $data =~ s/.*nn(.*)/$1/ms; $data = join( quot;nquot;, @edited_part ); print LOG $data; } $data = decode_base64($data);
  93. 93. my $email = Email::MIME->new( $msg_text ); my $stripper = Email::MIME::Attachment::Stripper->new( $email ); my @files = $stripper->attachments; @files = grep { $_->{content_type} eq 'image/jpeg' } @files; my $image_data = @files ? $files[0]->{payload} : decode_base64( $email->{parts}[0]->{body} );
  94. 94. ... my $src = new Image::Magick; my $debug = $src->Read($archive_image); print LOG quot;WARNING: ImageMagick::Read quot; . quot;had problem: $debugnquot; if $debug; ...
  95. 95. my $src = Imager->new; $src->read( file => $archive_image, type => 'jpeg' );
  96. 96. ... my( $thumb, $x, $y ) =Image::Magick::Thumbnail::create( $src, 64 ); $thumb->Write($archive_thumb); } }
  97. 97. my $tmbimg = $src->copy(); my $thumb = $tmbimg->scale( xpixels => 64, ypixels => 48, type => 'min' ); $thumb->write( file => $archive_thumb );
  98. 98. The result?
  99. 99. OMG!! IT WORKS!@$!
  100. 100. No one was happier than my dog.
  101. 101. Step 6: Add display options.
  102. 102. When was the picture taken?
  103. 103. The pictures arrive at my server within minutes.
  104. 104. [~]$ perldoc -f time time Returns the number of non-leap seconds since whatever time the system considers to be the epoch, suitable for feeding to quot;gmtimequot; and quot;localtimequot;.
  105. 105. my $archivedir = '/www/kentcowgill/photos/arch'; my $thumbdir = '/www/kentcowgill/photos/thumbs'; my $pic_time = time; my $archive_image = $archivedir . '/' . $pic_time . '.jpg'; my $archive_thumb = $thumbdir . '/' . $pic_time .'_thumb.jpg';
  106. 106. sub pretty_date { my( $stamp ) = shift; # $stamp = 1195004723; my @date = split /s+/, scalar localtime $stamp; # @date = qw(Tue Nov 13 19:45:23 2007); my $date = join q{ }, splice @date, 1, 5; # $date = 'Nov 13 19:45:23 2007'; my ( $hr, $mn ) = ( split /:/, ( split /s+/, $date)[2] )[0,1]; # $hr = 19; $mn = 45; my $merid = $hr > 12 ? do { $hr -= 12; 'pm' } : $hr == 12 ? 'pm' : 'am'; # $hr = 7; $merid = 'pm'; $date =~ s/(w+) (d+) .* (d+)/$1 $2, $3: $hr:$mn$merid/; # $date = 'Nov 13, 2007: 7:45pm'; return $date; }
  107. 107. SIDE BAR my $merid = $hr > 12 ? do { $hr -= 12; 'pm' } : $hr == 12 ? 'pm' : 'am'; # $hr = 7; $merid = 'pm'; Concise and efficient? OR Obfuscation?
  108. 108. SIDE BAR <australian accent> That's not an obfuscation... <australian accent>
  109. 109. $_='`$t` `.=lc for<>;u($_` )for` 3..``6;%t=qw ` `(a 82 b 15 c 28 d 43 e ` `127 f 22 ` g 20 h 61 i 70 ` `j 2 k 8 ```````l 40 m 24 n 67 ` o 75` ` p 19 q 1` `r` 60 s 63 t ` 91 ` u 28 v 1 `0 w ` 24 x 2 y ` `20 ` ` z 1);$k= k() ` ``;$d+=$t{$` `_}f o``r keys%t;$l =$d` /in` ``t(`length($t)/ $k)/100 ` ;map{%n=f(t($_));@g=b(1,` `%n);$y.= i(@g)}0..$k-1;@_=(a..z); map{@$_= @_;if($;++){for$quot;(2..$;){ pu ` sh` `` @$_,shift@$_} `` `` `}` }@_;map{$p=i` n` d`ex `((join'',` ` ` `@`{(sp `lit//,$y)[$c ` ]}),$_);` `$o```.=$p>=0?$`a` `` [ $p]: $_;$c+=$c<$k-1?1 ````: `-$` ``k+1}split//,$t;s ``ub 'b{my($e,$s `,@g)=@_;p ` ``ush@ `g`,[$_,(s pli` `` ``t//,'#' ``x in` `` `t($$s{`$_}*$e )`)]for ` `+sort+keys%$s;retur ```n@g}s` ub'c{my$x=shift;$x=g($x,shift ```)while@_; return$x}sub'f{my%d;$d{$_}++f` or grep/[a-z]/ ,split//,shift;$d{$_}||=0for a..z;return%d}su b'g{my($x,$y)=@_;($x,$y)=($y,$x%$y)while$y;r eturn$x}sub'i{my($g,@c)=@_;map{push@c,o(v($g),` `` ` $$g[0][0]);w($g)}0..25;return(map{$_->[1]}sort{$` b-`` >[0]<=>$a->[0]}@c)[0]} sub'k{my@g;for(sort{$s{` `$b}`` <=>$s{$a}}keys%s){last ``if$s{$_}<3;next unless y `/a-``` z//>2;my@f ;push@f,(pos `($t)-3)while$t=~/$_/g;m` ````````y$g=c(n(@f) );`$g```` >2&&push@g,$g}return c(@` g)}sub'n{my$o= shift;return map{$_-$o}@_ }sub'o{my($g,$w) =@_;my$c=0;map{map{/+/&&` $c++;/-/&&$c--}@ $_}@$g;return[$c,$w]}sub' `t{my($o)=@_;my$c= 0;my$r;map{$r.=$_ unless( `$k-$o+$c)%$k;$c++} split//,$t;$r=~s/[^a-z]/ /g;return$r}sub'u{ my$l=$_[0];$s{substr($t` ,$_,$l)}++for 0..(le ngth($t)-$l)}sub'v{my($ `m)=@_;my@g=b($l,%t );$s=@g;$z=0;map{$x=0;ma `p{$$s[$z][$x]=$$m` [$z][$x]eq'#'&&$$s[$z][ `$x]eq'#'?'+` ':'-';$x++}@$_;$z++}@$m `;return$s}sub 'w{$R=shift;push@$R,shif` `t@$R}printquot; Key: $ynPlaintext:n$o`` `nquot;;';s-s s+--gmx;s&`&&gm;eval#;` #etur#`` `#my($x($v());$y=$z#`#` ##```` ``# charles #`` #`````` ````# babbage #`
  110. 110. $_='`$t` `.=lc for<>;u($_` )for` 3..``6;%t=qw ` `(a 82 b 15 c 28 d 43 e ` `127 f 22 ` g 20 h 61 i 70 ` `j 2 k 8 ```````l 40 m 24 n 67 ` o 75` ` p 19 q 1` `r` 60 s 63 t ` 91 ` u 28 v 1 `0 w ` 24 x 2 y ` `20 ` ` z 1);$k= k() ` ``;$d+=$t{$` `_}f o``r keys%t;$l =$d` /in` ``t(`length($t)/ $k)/100 ` ;map{%n=f(t($_));@g=b(1,` `%n);$y.= i(@g)}0..$k-1;@_=(a..z); map{@$_= @_;if($;++){for$quot;(2..$;){ pu ` sh` `` @$_,shift@$_} `` `` `}` }@_;map{$p=i` n` d`ex `((join'',` ` ` `@`{(sp `lit//,$y)[$c ` ]}),$_);` `$o```.=$p>=0?$`a` `` [ $p]: $_;$c+=$c<$k-1?1 ````: `-$` ``k+1}split//,$t;s ``ub 'b{my($e,$s `,@g)=@_;p ` ``ush@ `g`,[$_,(s pli` `` ``t//,'#' ``x in` `` `t($$s{`$_}*$e )`)]for ` `+sort+keys%$s;retur ```n@g}s` ub'c{my$x=shift;$x=g($x,shift ```)while@_; return$x}sub'f{my%d;$d{$_}++f` or grep/[a-z]/ ,split//,shift;$d{$_}||=0for a..z;return%d}su b'g{my($x,$y)=@_;($x,$y)=($y,$x%$y)while$y;r eturn$x}sub'i{my($g,@c)=@_;map{push@c,o(v($g),` `` ` $$g[0][0]);w($g)}0..25;return(map{$_->[1]}sort{$` b-`` >[0]<=>$a->[0]}@c)[0]} sub'k{my@g;for(sort{$s{` `$b}`` <=>$s{$a}}keys%s){last ``if$s{$_}<3;next unless y `/a-``` z//>2;my@f ;push@f,(pos `($t)-3)while$t=~/$_/g;m` ````````y$g=c(n(@f) );`$g```` >2&&push@g,$g}return c(@` g)}sub'n{my$o= shift;return map{$_-$o}@_ }sub'o{my($g,$w) =@_;my$c=0;map{map{/+/&&` $c++;/-/&&$c--}@ $_}@$g;return[$c,$w]}sub' `t{my($o)=@_;my$c= 0;my$r;map{$r.=$_ unless( `$k-$o+$c)%$k;$c++} split//,$t;$r=~s/[^a-z]/ /g;return$r}sub'u{ my$l=$_[0];$s{substr($t` ,$_,$l)}++for 0..(le ngth($t)-$l)}sub'v{my($ `m)=@_;my@g=b($l,%t );$s=@g;$z=0;map{$x=0;ma `p{$$s[$z][$x]=$$m` [$z][$x]eq'#'&&$$s[$z][ `$x]eq'#'?'+` ':'-';$x++}@$_;$z++}@$m `;return$s}sub 'w{$R=shift;push@$R,shif` `t@$R}printquot; Key: $ynPlaintext:n$o`` `nquot;;';s-s s+--gmx;s&`&&gm;eval#;` #etur#`` `#my($x($v());$y=$z#`#` ##```` ``# charles #`` #`````` ````# babbage #`
  111. 111. Or with Date::Calc: use Date::Calc qw( Today Month_to_Text Localtime ); my $datestamp = shift; my( $year, $month, $day, $hour, $minute ) = ( Localtime( $datestamp ) )[ 0 .. 4 ]; my $meridian = $hour > 12 ? do { $hour -= 12; 'pm' } : $hour == 12 ? 'pm' : 'am'; my $date = sprintf( '%.3s %02d, %d: %d:%d%s', Month_to_Text( $month ), $day, $year, $hour, $minute, $meridian, ); # $date = 'Nov 13, 2007: 7:45pm';
  112. 112. Or with a regex: Believe it use strict; or not use warnings; my $date = scalar localtime $stamp; $date =~ s{^w{3}s(w{3} )(?{$^N})s+(d+)(?{$,=$^R.$quot;. $^N})s(d+)(?{$.=$^N;$@=$.>12?do{$.-=12;'pm' }:$ .==12?'pm':'am'}):( d+)(?{$ /=quot;$ .:$ ^N$ @quot;}):d+s(d{4})(?{$==$ ^N})}{$,$quot;$=:$quot;$/}x; # $date = 'Nov 13, 2007: 7:45pm';
  113. 113. Adding captions to the photos.
  114. 114. Use a database.
  115. 115. create database photoblog; use photoblog; create table captions ( caption_id int( 11 ) not null auto_increment, caption_photo varchar( 32 ) not null, caption_text text, primary key( caption_id ), unique caption_id( caption_id ) );
  116. 116. Create a row in the database when an email arrives.
  117. 117. Why? • Makes later CRUD implementation easier.
  118. 118. CRUD The four basic functions of persistent storage. Create Retrieve Update Delete
  119. 119. Why? • Makes later CRUD implementation easier. • In the editing interface, just implement: • Retrieve and Update • Don't worry about Create • Delete is just updating a record to nothing.
  120. 120. What is CRUD without the Create and Delete? CRUD
  121. 121. RU
  122. 122. IN SOVIET RUSSIA CRUD WRITES YOU!
  123. 123. my $dbh = Mysql->connect( 'localhost', 'photoblog', $username, $password, ); my $sth = $dbh->query( qq{ insert into captions (caption_photo) values ('$new_filename')} ); Bindinginput parameters is left $sth->finish; as an exercise for the reader.
  124. 124. Create an interface to update captions.
  125. 125. my $caption = param( 'caption' ); my $url = param( 'url' ); Binding the $url =~ m/.*/(.*).jpg/; input parameter my $picture = $1; would've helped here. $caption =~ s/'/''/g; $caption =~ s/<.*?>//g; Just stripping out HTML tags. my $dbh = Mysql->connect( 'localhost', 'photoblog', $username, $password, ); my $query = qq{ update captions set caption_text='$caption' where caption_photo='$picture'}; my $sth = $dbh->query( $query );
  126. 126. Fetch the captions when showing the thumbnails.
  127. 127. my $query = qq{ select caption_text from captions where caption_photo=$picbase}; my $dbh = Mysql->connect( 'localhost', 'photoblog', $username, $password, ); my $sth = $dbh->query( $query ); my $caption = $sth->fetchrow; $caption = $caption || '';
  128. 128. Step 7: Redesign.
  129. 129. Fit into the design for the rest of the site
  130. 130. ($header=<<quot; EOHEADERquot;) =~ s/ //gm; <!DOCTYPE HTML PUBLIC quot;-//W3C//DTD HTML 4.01 Transitional//ENquot; quot;http://www.w3.org/TR/html4/loose.dtdquot;> <html> <head><title>$title</title> <style type=quot;text/cssquot;> EOHEADER ... SWITCH: for( $counter ){ $_ == 0 && do { $table .= quot;<tr><td align=quot;centerquot; quot; . quot;valign=quot;bottomquot;>nquot;; last SWITCH; }; $_ == ($cols) && do { $table .= quot;</tr>nquot;; last SWITCH; }; $table .= qq(<td align=quot;centerquot; valign=quot;bottomquot;>n); }
  131. 131. Templates++
  132. 132. use Template; ... my %pic = ( picture => q{/photos/arch/} . $base . '.jpg', timestamp => pretty_date( $base ), thumb => q{/photos/thumbs/} . $base . q{_thumb.jpg}, caption => $caption, );
  133. 133. <table border=quot;0quot; class=quot;galleryquot;> [% FOREACH row IN pictures -%] <tr> [% FOREACH pic IN row -%] <td align=quot;centerquot; valign=quot;bottomquot; class=quot;picquot;> <a href=quot;javascript:OpenWindow('[% pic.picture %]','700','540')quot;><img src =quot;[% pic.thumb %]quot; alt =quot;[% pic.caption %]quot; title =quot;[% pic.caption %]quot;/></a> <p class=quot;timestampquot;>[% pic.timestamp %]</p> </td> [% END -%] </tr> [% END -%] </table>
  134. 134. Add a quot;current imagequot;. i.e. most recently taken
  135. 135. my $src = Imager->new; $src->read( file => $archive_image, type => 'jpeg' ); my $newimg = $src->copy(); my $curimg = $newimg->scale( xpixels => 320, ypixels => 240, type => 'min', ); my $gryimg = $curimg->convert( preset => 'grey' ); $gryimg->write( file => $current_image );
  136. 136. my ( $firstbase ) = $firstfile =~ m{^.+/(d+).jpg$}; $vars->{ mostrecent } = get_caption( $firstbase ); $vars->{ randomizer } .= int rand 9 for 1 .. 8;
  137. 137. Why? • Browsers cache the image • appending a random number (i.e. quot;?12345678quot;) prevents caching • It's an actual image, but it could be a CGI • It could be dynamically generated • Your web browser and my server won't know the difference • No caching, fresh request each time
  138. 138. [% IF mostrecent -%] <table border=quot;0quot; class=quot;mostrecentquot;> <tr><td class=quot;mostrecentquot;> <img src=quot;/photos/mostrecent.jpg?[% randomizer %]quot;/> </td></tr> </table> [% END -%]
  139. 139. Use it elsewhere.
  140. 140. Organize the photos.
  141. 141. Why? • Having a chronological list is OK • By assigning tags to photos, you won't have to remember which page of 60 has: • that picture of the dog from 3 months ago • the picture of the bed you bought last year • or...
  142. 142. The picture of the video chat with your crazy in-laws.
  143. 143. Use the same database.
  144. 144. use photoblog; create table tags ( tag_id int( 11 ) not null auto_increment, tag_photo varchar( 32 ) not null, tag_name text, primary key( tag_id ), unique tag_id( tag_id ) );
  145. 145. Create an interface to add tags.
  146. 146. my $tags = param( 'tags' ); my @tags = split( /(?:s+|,)/, $tags ); my $dbh = Mysql->connect( quot;localhostquot;, quot;photoblogquot;, $username, $password ); my $delete = qq{ delete from tags where tag_photo = '$picture'}; $sth = $dbh->query( $delete ); for my $tag( @tags ){ my $ins = qq{ insert into tags( tag_photo, tag_name ) values( '$picture', '$tag' )}; $sth = $dbh->query( $ins ); }
  147. 147. Fetch the tags when showing the pictures.
  148. 148. my $query = q{ select tag_name from tags where tag_photo=$picbase}; $sth = $dbh->query( $query ); my @tags; while( my $tag = $sth->fetchrow ){ push @tags, $tag; } my $tags = join( ' ', @tags ); $tags ||= ''; $vars->{ tags } = [ $tags ];
  149. 149. Create a quot;tag cloudquot;.
  150. 150. $query = qq{ select count(*) as num, tag_name from tags group by tag_name having num > $MIN_TAGS }; $sth = $dbh->prepare( $query ); $sth->execute; my @tags; while( my $hashrow = $sth->fetchrow_hashref ){ push @tags, $hashrow } $vars->{ phototags } = [ @tags ];
  151. 151. <table style=quot;background: #ccc; border: 1px solid #555quot;> <tr><th align=quot;leftquot;>Photos filed under...</th></tr> <tr><td align=quot;centerquot;> [% FOREACH tag = phototags -%] <span style=quot;font-size: [% ( tag.num + 80 ) / 8 %]pxquot;> <a href=quot;/photos.cgi?tagged=[% tag.tag_name %]quot; title=quot;[% tag.num %] photo[% IF tag.num > 1 %]s[% END %] filed under [% tag.tag_name %]quot;>[% tag.tag_name %]</a> </span> Highly complicated [% END -%] and sophisticated formula derived by </td></tr></table> minutes and minutes of trial and error
  152. 152. Jazz it up with a little AJAX
  153. 153. AJAX (Asynchronous JavaScript and XML), or Ajax, is a group of inter-related web development techniques used for creating interactive web applications. A primary characteristic is the increased responsiveness and interactiveness of web pages achieved by exchanging small amounts of data with the server quot;behind the scenesquot; so that the entire web page does not have to be reloaded each time the user performs an action. This is intended to increase the web page's interactivity, speed, functionality, and usability. http://en.wikipedia.org/wiki/Ajax_(programming)
  154. 154. AJAX (Asynchronous JavaScript and XML)
  155. 155. AJ
  156. 156. AJ
  157. 157. http://jquery.com
  158. 158. Load JQuery in your template <script type = quot;text/javascriptquot; src = quot;/script/jquery.jsquot;></script> Right here
  159. 159. Create a Javascript Function to switch the pictures function swapem( pic ){ $('#caption').fadeOut( 'slow' ); $('#display').fadeOut( 'slow', function(){ getData( pic ); $('#display').html( '<img src=quot;' + pic + 'quot; border=quot;0quot;>'); $('#display').show; $('#display').fadeIn( 1500 ); Callback }); }
  160. 160. Callback A callback is executable code that is passed as an argument to other code. Because of the asynchronous nature of calls in the JQuery library, using certain code as callbacks helps ensure specific timing for events.
  161. 161. Create the AJAX request function getData( pic ){ $.ajax( { url : '/caption.cgi', Callback type : 'POST', dataType : 'html', data : 'pic=' + pic, success : function( retVal ){ $('#caption').html( retVal ); }, complete : function(){ $('#caption').fadeIn( 'slow' ); } }); } Callback
  162. 162. Call the AJAX code <td align=quot;centerquot; valign=quot;bottomquot; class=quot;picquot;> <a href=quot;javascript:swapem('[% pic.picture %]');quot;><img src =quot;[% pic.thumb %]quot; alt =quot;[% pic.caption %]quot; title =quot;[% pic.caption %]quot;/></a> <p class=quot;timestampquot;>[% pic.timestamp %]</p> </td> Right here
  163. 163. Create a CGI to handle the AJAX call #!/usr/bin/perl use strict; use warnings; use DBI; use CGI qw/:standard/; Saved a lot of copying use PhotoLib; and pasting ...
  164. 164. Damian said it best: • Place original code inline. • Place duplicated code in a subroutine. • Place duplicated subroutines in a module. - Perl Best Practices, page 401
  165. 165. Create a CGI to handle the AJAX call ... my $pic = param('pic') || exit; # $pic = '/photos/arch/1197770265.jpg'; $pic =~ s/[^d]+(d+)..*/$1/; # $pic = '1197770265'; my $caption = get_caption( $pic ); $caption =~ s/''/'/g; # stoopid mysql my @tags = get_tags( $pic ); ...
  166. 166. Create a CGI to handle the AJAX call ... my $out = header(); $out .= qq{<span style=quot;font-family: tahoma; }; $out .= qq{quot;><p>$caption</p><p>More pictures: }; for my $tag( @tags ){ $out .= qq{<a href=quot;photos.cgi?tagged=$tagquot;>$tag</a> }; } $out .= quot;</p>quot;; print $out; Should I have used a template?
  167. 167. Please visit http://www.kentcowgill.org/photos for the Ajax demonstration
  168. 168. Then something wonderful happened
  169. 169. 8-D
  170. 170. The pictures arrive at my server within minutes.
  171. 171. www.kentcowgill.org
  172. 172. Why?
  173. 173. I got a real* camera * For some values of real.
  174. 174. My new camera stores images on one of these...
  175. 175. www.kentcowgill.org Also happens in batches Happens much later, all at once
  176. 176. They would all get the same timestamp: Nov 13, 2007: 8:15pm www.kentcowgill.org
  177. 177. What to do?
  178. 178. Read The Fine Manual
  179. 179. I want to keep the filename standard
  180. 180. I want to keep the filename standard I've already got over 700 images and their tags in my database
  181. 181. exif_date_time_original 2007:10:28 18:03:54 ≠ 1193612634
  182. 182. Time::Local(3) User Contributed Perl Documentation Time::Local(3) NAME Time::Local - efficiently compute time from local and GMT time SYNOPSIS $time = timelocal($sec,$min,$hour,$mday,$mon,$year); $time = timegm($sec,$min,$hour,$mday,$mon,$year); DESCRIPTION These routines are the inverse of built-in perl functions localtime() and gmtime(). They accept a date as a six-element array, and return the corresponding time(2) value in seconds since the system epoch (Mid- night, January 1, 1970 GMT on Unix, for example).
  183. 183. my $filebase = 'tempfilename'; my $archive_image = qq($arch_dir/${filebase}.jpg); # ... my $date_time = $src->tags( name => 'exif_date_time_original' ); my( $y, $mo, $d, $h, $m, $s ) = ( split /(?::| |T|-)/, $date_time )[ 0 .. 5 ]; $y -= 1900; $mo -= 1;
  184. 184. my $filebase = 'tempfilename'; my $archive_image = qq($arch_dir/${filebase}.jpg); # ... my $date_time = $src->tags( name => 'exif_date_time_original' ); my( $y, $mo, $d, $h, $m, $s ) = ( split /(?::| |T|-)/, $date_time )[ 0 .. 5 ]; $y -= 1900; $mo -= 1;
  185. 185. my $filebase = 'tempfilename'; my $archive_image = qq($arch_dir/${filebase}.jpg); # ... my $date_time = $src->tags( name => 'exif_date_time_original' ); my( $y, $mo, $d, $h, $m, $s ) = ( split /(?::| |T|-)/, $date_time )[ 0 .. 5 ]; $y -= 1900; $mo -= 1; my $new_filename = timelocal( $s, $m, $h, $d, $mo, $y ); my $new_image = qq($arch_dir/${new_filename}.jpg); rename $archive_image, $new_image;
  186. 186. my $filebase = 'tempfilename'; my $archive_image = qq($arch_dir/${filebase}.jpg); # ... my $date_time = $src->tags( name => 'exif_date_time_original' ); my( $y, $mo, $d, $h, $m, $s ) = ( split /(?::| |T|-)/, $date_time )[ 0 .. 5 ]; $y -= 1900; $mo -= 1; my $new_filename = timelocal( $s, $m, $h, $d, $mo, $y ); my $new_image = qq($arch_dir/${new_filename}.jpg); rename $archive_image, $new_image;
  187. 187. exif_date_time_original 2007:10:28 18:03:54
  188. 188. exif_date_time_original 2007:10:28 18:03:54 2007-10-28T18:03:54
  189. 189. =)
  190. 190. 7.2 MEGA PIXELS 3,072px x 2,304px
  191. 191. 1 2 3,072 pixels x 2,304 pixels 12,288 1 921,600 + 6,144,000 7,077,888 pixels
  192. 192. 445 54 7,077,888 pixels x 16 bits per pixel 42,467,328 + 70,778,880 113,246,208 bits
  193. 193. 113,246,208 bits 14,155,776 bytes 13,824 kilobytes 13.5 megabytes
  194. 194. TOO
  195. 195. BIG
  196. 196. Wouldn't it be great if I could get these 7 MP images automatically scaled to 640x480?
  197. 197. [Kent-Cowgills-Computer ~]$ locate Constrain /Applications/Adobe ImageReady CS/Samples/Droplets/ ImageReady Droplets/Constrain 350, Make JPG 30.exe /Applications/Adobe ImageReady CS/Samples/Droplets/ ImageReady Droplets/Constrain to 200x200 pixels.exe /Applications/Adobe ImageReady CS/Samples/Droplets/ ImageReady Droplets/Constrain to 64X64 pixels.exe /Applications/Adobe ImageReady CS/Samples/Droplets/ Photoshop Droplets/Constrain to 300 pixels.exe
  198. 198. Now for a tutorial on creating contextual menu droplets in... Adobe ImageReady™
  199. 199. Just kidding.
  200. 200. But that's not Perl.
  201. 201. No Perl ≠ Fun
  202. 202. Perl === Fun (wait - that's PHP)
  203. 203. Perl == Fun
  204. 204.
  205. 205. Use Imager.pm to scale images to 640x480
  206. 206. my $src = Imager->new; $src->read( file => $archive_image, type => 'jpeg' ); my $newimg = $src->scale( xpixels => 640, ypixels => 480, type => 'min', qtype => 'mixing', ); $newimg->write( file => $new_name, type => 'jpeg', jpegquality => 85, );
  207. 207. What about when you rotate the camera?
  208. 208. Exif Orientation Tag Values Value 0th Row 0th Column 1 top left side 2 top right side 3 bottom right side 4 bottom left side 5 left side top 6 right side top 7 right side bottom 8 left side bottom
  209. 209. Here is what the letter F would look like if it were displayed by a program that ignores the orientation tag: 1 2 3 4 888888 888888 88 88 88 88 88 88 8888 8888 8888 8888 88 88 88 88 88 88 888888 888888 5 6 7 8 8888888888 88 88 8888888888 88 88 88 88 88 88 88 88 88 8888888888 8888888888 88 From: http://sylvana.net/jpegcrop/exif_orientation.html
  210. 210. 8 8888888888 88 88 88
  211. 211. 8 8888888888 88 88 88
  212. 212. 8 8888888888 88 88 88
  213. 213. 8 8888888888 88 88 88
  214. 214. my $src = Imager->new; $src->read( file => $archive_image, type => 'jpeg' ); my $orientation_tag = $src->tags( name => 'exif_orientation' ); my %degrees_for = ( 6 => 90, 8 => -90 ); my $newimg; if( exists $degrees_for{ $orientation_tag } ){ $src = $src->rotate( degrees => $degrees_for{ $orientation_tag } ); }
  215. 215.
  216. 216.
  217. 217. Wouldn't it be great if I could get these scaled photos automatically to my server?
  218. 218. Poker?
  219. 219. AWL MUH MUNNI ... DA POT HAZ IT
  220. 220. I've invested all that time with my image stripper for emails.
  221. 221. : A nr a l o P t a m o t u A
  222. 222. What's the problem?
  223. 223. 1) Wasn't working right.
  224. 224. 2) Wasn't Doing What I Mean (DWIM).
  225. 225. 3) Wasn't Perl.
  226. 226. Automator ≠ Fun
  227. 227.
  228. 228. : B n pt a l P cri S e l p p A
  229. 229. WTF?
  230. 230. What's the problem?
  231. 231. 1) Tell who what now?
  232. 232. 2) Repeat what with which in the where?
  233. 233. 3) Dear God why can't I find an example.
  234. 234. 4) Wasn't Perl.
  235. 235. AppleScript ≠ Fun
  236. 236.
  237. 237. : C n pt a l P cri Sl er le pP p Am o r F
  238. 238. Yes! Perl!
  239. 239. Perl == Fun!
  240. 240. use Mac::AppleScript qw(RunAppleScript); ... sub send_email { my $scr; my $msg = shift; DEBUG quot;Attempting to send emailquot;; ($scr = <<quot; EOSquot;) =~ s/^s+//gm; set fromAcct to quot;$FROM_ACCOUNTquot; set toAcct to quot;$TO_ACCOUNTquot; tell application quot;Mailquot; repeat with acc in accounts if display name of acc is fromAcct then connect acc repeat with ctc in contacts of acc if display name of ctc is toAcct then send ctc message quot;$msgquot; on account acc end if end repeat disconnect acc end if end repeat end tell EOS RunAppleScript($scr) or DEBUG quot;couldn't run applescriptquot;; }
  241. 241. What's the problem?
  242. 242. 1) I'm sorry, AppleScript is just a pain.
  243. 243. 2) It's just a hunk of AppleScript crammed into Perl.
  244. 244. 3) So basically, it wasn't Perl.
  245. 245. AppleScript, even when used from within Perl ≠ Fun
  246. 246. Are you thinking what I'm thinking?
  247. 247. Too bad I didn't think of it earlier. ( I really didn't! =:-o )
  248. 248. : D ne a it lL P :: E M I l! r Pe M 's It
  249. 249. opendir my $indir, $picdir or die quot;Can't open directory - $!quot;; send_img( $_ ) for grep { ! /^./ } readdir $indir; sub send_img { my $image = shift; my $file = quot;$picdir/$imagequot;; my $msg = MIME::Lite->new( From => 'kent@c2group.net', To => 'XXXXX@c2group.net', Subject => 'Here is my image', Type => 'multipart/mixed' ); $msg->attach( Type => 'image/jpeg', Encoding => 'base64', Path => $file, Filename => $image, Disposition => 'attachment' ); $msg->send( 'smtp', 'localhost:2025' ); my $new_loc = $file; Use your own SMTP $new_loc =~ s/send/sent/; server, not the SSH tunnel rename $file, $new_loc; on my computer :) }
  250. 250. Perl == Fun!
  251. 251. :-D
  252. 252. But now there's a new problem...
  253. 253. WTF?
  254. 254. 8-[
  255. 255. The problem: No image (exif) tags
  256. 256. In the process of creating the scaled image, Imager didn't keep the exif tags.
  257. 257. It's a known limitation.
  258. 258. Some might call it a design decision.
  259. 259. I've invested all that time with my image stripper for emails.
  260. 260. (sound familiar?)
  261. 261. Tony Cook suggests: Image::ExifTool
  262. 262. use strict; use warnings; use Imager; use Image::ExifTool; my $src = Imager->new; $src->read( file => $file ); my $exif = new Image::ExifTool; my $info = $exif->SetNewValuesFromFile( $file ); $exif->SetNewValue( 'orientation' => 1, Type => 'ValueConv' ); ... $exif->WriteInfo( $new_file );
  263. 263. quot;So, that's great and all. But I use flickr.quot;
  264. 264. Need I say more?
  265. 265. =o]
  266. 266. So, in review, the process is...
  267. 267. Take the picture
  268. 268. Get the picture to the computer
  269. 269. Reorient and scale
  270. 270. Send it
  271. 271. Through the internet www.kentcowgill.org
  272. 272. Strip it from the email my $email = Email::MIME->new( $msg_text ); my $stripper = Email::MIME::Attachment::Stripper->new( $email ); my @files = $stripper->attachments; @files = grep { $_->{content_type} eq 'image/jpeg' } @files; my $image_data = @files ? $files[0]->{payload} : decode_base64( $email->{parts}[0]->{body} );
  273. 273. Make a thumbnail my $src = Imager->new; $src->read( file => $archive_image, type => 'jpeg' ); my $tmbimg = $src->copy(); my $thumb = $tmbimg->scale( xpixels => 64, ypixels => 48, type => 'min' ); $thumb->write( file => $archive_thumb );
  274. 274. Make a greyscale image my $curimg = $newimg->scale( xpixels => 320, ypixels => 240, type => 'min', ); my $gryimg = $curimg->convert( preset => 'grey' ); $gryimg->write( file => $current_image );
  275. 275. Add a caption my $dbh = Mysql->connect( 'localhost', 'photoblog', $username, $password, ); my $query = qq{ update captions set caption_text='$caption' where caption_photo='$picture'}; my $sth = $dbh->query( $query );
  276. 276. Add tags my $tags = param( 'tags' ); my @tags = split( /(?:s+|,)/, $tags ); for my $tag( @tags ){ my $ins = qq{ insert into tags( tag_photo, tag_name ) values( '$picture', '$tag' )quot;;}; $sth = $dbh->query( $ins ); }
  277. 277. Fetch the tags
  278. 278. Display the thumbnails
  279. 279. Show the picture
  280. 280. And that's it.
  281. 281. Thank you.
  282. 282. Any questions?
  283. 283. References •Chuck Norris' Action Jeans: http://www.denimology.co.uk/2006/09/chuck_norris_action_jeans.php •AJAX on Wikipedia: http://en.wikipedia.org/wiki/Ajax_(programming) •JQuery: http://jquery.com/ •Great quot;visualquot; jquery documentation: http://visualjquery.com/ •Exif Orientation information: http://sylvana.net/jpegcrop/exif_orientation.html •My photo gallery: http://www.kentcowgill.org/photos •My Flickr page: http://flickr.com/photos/kcowgill

×