#!/usr/local/bin/perl -w # Rehist: alter an input image to match an input histogram, write out # altered image. use Image::Magick; use strict; $| = 1; # unbuffered output plz kthx my $err; my $sourceimage = shift; my $targethistogram = shift or die "Usage: rehist.pl original_image target_histogram\n"; # read in original image, target histogram print "Reading source image..."; my $src = Image::Magick->new; $err = $src->Read($sourceimage); warn "$err" if "$err"; print "done.\n"; print "Reading target histogram..."; my $tar = Image::Magick->new; $err = $tar->Read($targethistogram); warn "$err" if "$err"; print "done.\n"; my %index; my @histogram; my $w = $src->Get('width'); my $h = $src->Get('height'); # create histogram of source image print "Creating histogram of source..."; my @srcpix = $src->GetPixels(map=>"I", width=>"$w", height=>"$h"); for(my $i=0; $i < @srcpix; $i++) { my $p = int($srcpix[$i]*255); # because normalize=>'false' is throwing errors. $histogram[$p]++; # add to histogram push(@{$index{$p}}, $i); # add to index of pixels by value; } print "done.\n"; # read histogram from target histogram print "Reading target histogram..."; my @tarhistogram; my $tarhtotal; for(my $i = 0; $i < 256; $i++) { my @column = $tar->GetPixels(map=>"I", width=>1, height=>100, x=>$i, y=>0); $tarhistogram[$i] = 99; foreach my $n (@column) { $tarhistogram[$i] -= $n; } $tarhtotal += $tarhistogram[$i]; # total pixels in histogram } print "done.\n"; # normalize src histogram data print "Normalizing source histogram data..."; my $norm = 0; for(my $c=0; $c < 256; $c++) { if(defined($histogram[$c])) { if($histogram[$c] > $norm) { $norm = $histogram[$c];} } else { $histogram[$c] = 0; } } for(my $c=0; $c < @histogram; $c++) { # outlier: all black image, $norm is 0, div by zero! Dodge that. if($norm == 0) { last; } $histogram[$c] = 100 * $histogram[$c] / $norm; } print "done.\n"; #print "Total of " . scalar(@histogram) . " values recorded.\n"; # write out source histogram print "Writing source histogram image..."; my $srchist = Image::Magick->new; $srchist->Set(size=>'256x100'); $srchist->ReadImage('xc:white'); for(my $x=0; $x < 256; $x++) { my $height = $histogram[$x]; for(my $y = 0; $y < $height; $y++) { $srchist->Set("pixel[$x," . (99 - $y) . "]"=>'black'); } } my $shistout = $sourceimage; $shistout =~ s!^(.+)(\.\w{3})$!h_$1.png!; $err = $srchist->Write("$shistout"); warn "$err" if "$err"; print "done.\n"; # diff between size of histogram and size of image, in pixelcount my $scale = $h * $w / $tarhtotal; # create flat sorted array of pixels from original image my @indsort; foreach my $k (sort {$a <=> $b} keys %index) { push(@indsort, @{$index{$k}}); } # push portions of the flat array into a new index according to the # target histogram's frequencies my $remainder = 0; my %targetindex; for(my $i = 0; $i < 256; $i++) { my $lenraw = $tarhistogram[$i] * $scale + $remainder; my $len = int($lenraw); $remainder = $lenraw - $len; my @chunk = splice(@indsort, 0, $len); push(@{$targetindex{$i}}, @chunk); } print "Printing final histogram-corrected image..."; my $final = Image::Magick->new; $final->Set(size=>$w . "x" . $h); $final->ReadImage('xc:red'); foreach my $lev (sort {$a <=> $b} keys %targetindex) { my @arr = @{$targetindex{$lev}}; my $hex = sprintf("#%2.2x%2.2x%2.2x", $lev, $lev, $lev); foreach my $r (@arr) { my $ix = $r % $w; my $iy = int($r / $w); $final->Set("pixel[$ix,$iy]"=>"$hex"); } } my $finalout = $sourceimage; $finalout =~ s!^(.+)(\.\w{3})$!out_$1.png!; $err = $final->Write("$finalout"); warn "$err" if "$err"; print "done.\n";