Gimp
view release on metacpan or search on metacpan
examples/image_tile view on Meta::CPAN
# Create a cache of all of the cell samples from the original image for
# this tile only.
for(my $xcell=0;$xcell<$xcells;$xcell++) {
for(my $ycell=0;$ycell<$ycells;$ycell++) {
$ocells[$xcell][$ycell] =
substr($$oimage,
(($x*$xcells + $xcell)*$ytiles*$ycells+
$y*$ycells +
$ycell) * 3, 3);
}
}
my $subimg;
my $weight;
# Loop through all available sub-images and find best fit for this
# tile.
while(($subimg,$weight)=each %wt_cache) {
my $match = 0;
for(my $xcell=0;$xcell<$xcells;$xcell++) {
my $subfile = $subimg;
for(my $ycell=0;$ycell<$ycells;$ycell++) {
# Cell samples are stored as packed 3-byte values
my($o1,$o2,$o3) = unpack 'CCC', $ocells[$xcell][$ycell];
my($n1,$n2,$n3) = unpack 'CCC',
substr($tile_cache{$subfile},($xcell*$ycells+$ycell)*3,3);
# 2 methods of comparing: by RGB and by HSV. HSV seems to
# give a more accurate map, as it stresses the matching of light
# and darkness. We do some weighting of the HSV match so that
# we don't care about hue as much if saturation or value is
# low, and we don't care about saturation as much if value is low
# The net effect is that for a black pixel, you don't care
# what color hue tells you it is, because it's always black
my $c3_delta = abs($o3 - $n3);
my $c2_delta;
my $c1_delta;
if ($DO_HSV) {
# c1 == H, c2 == S, c3 == V
$c2_delta = abs($o2 - $n2) * $o3 / 255;
$c1_delta = hue_dist($o1,$n1)* 2 * ($o3*$o2/(255**2));
} else {
# c1 == R, c2 == G, c3 == B
$c2_delta = abs($o2 - $n2);
$c1_delta = abs($o1 - $n1);
}
# Keep a running score of the differences between samples for this
# sub-image vs. this tile from the orginal
$match += $c1_delta + $c2_delta + $c3_delta;
}
}
# Weight for image duplicates.
$match += $wt_cache{$subimg};
if (!defined($minmatch) || $match < $minmatch) {
$minmatch = $match;
$matchid = $subimg;
}
}
if (!defined($matchid)) {
die("image_tile: No subimages selected!");
}
# Actually insert the selected image.
overlay_image($drawable, $matchid,
$xtilewidth*$x, $ytileheight*$y,
$xtilewidth, $ytileheight);
$wt_cache{$matchid} += $dupweight;
}
# Finish up.
undef $db;
untie %tile_cache;
undef $wdb;
untie %wt_cache;
unlink($wt_file);
unlink($cache_file) if $cleanup;
Gimp::Progress->update(1);
$image->undo_enable;
();
};
# Take IMAGE, XCELLS, YCELLS, TARGET_ASPECT.
# Works destructively on IMAGE, and returns a list of anon-lists which
# contain the color samples for the given IMAGE.
sub get_image_cells {
my ($img, $xcells, $ycells, $target_aspect, $start_complete, $end_complete) = @_;
# print "Target aspect: $target_aspect\n";
my $file = $img->get_filename;
# print "$file: ";
my $width = $img->width;
# print "width: $width ";
my $height = $img->height;
# print "height: $height\n";
my $cells = "\0\0\0" x ($xcells * $ycells);
return () if $width < 1 || $height < 1;
# First crop to fit tiles
match_aspect($img, $target_aspect, $width, $height);
# Now, scale down to xcells by ycells for color sampling
# NOTE: We will re-open this image later if it is chosen.
# This scaling is just to get color samples.
$img->scale($xcells, $ycells);
my $draw = $img->get_active_drawable;
for(my $x=0;$x<$xcells;$x++) {
if (defined($start_complete)) {
Gimp::Progress->update(($start_complete+
($end_complete-$start_complete)*$x/$xcells)/100);
}
for(my $y=0;$y<$ycells;$y++) {
my $color = eval { $draw->pick_color($x, $y, FALSE, TRUE, 1.0) };
next if ($@);
my @c;
if ($DO_HSV) {
@c = rgb2hsv(@$color);
} else {
@c = @$color;
}
substr($cells,($x*$ycells+$y)*3,3) = pack('CCC',@c);
}
}
return \$cells;
}
# Take IMAGE, TARGET_ASPECT, WIDTH (of image), HEIGHT (of image)
# Crops IMAGE to match aspect ratio of TARGET_ASPECT.
sub match_aspect {
my ($img, $target_aspect, $width, $height) = @_;
my $aspect = $width/$height;
if ($aspect < $target_aspect) {
my $oldheight = $height;
$height = int($width / $target_aspect);
# print "Image was $width X $oldheight, cropping to $width X $height\n";
$img->crop($width, $height, 0, int(($oldheight-$height) / 2) );
} elsif ($aspect > $target_aspect) {
my $oldwidth = $width;
$width = int($target_aspect * $height);
# print "Image was $oldwidth X $height, cropping to $width X $height\n";
$img->crop($width, $height, int(($oldwidth-$width) / 2), 0);
}
}
# Take DRAWABLE, INFO, X, Y, WIDTH, HEIGHT
# Opens image referenced by INFO->{name} and scale/crop to fit in rectagnle
# described by X,Y,WIDTH,HEIGHT
sub overlay_image {
my ($draw, $file, $x, $y, $width, $height) = @_;
my $img = Gimp->file_load($file, $file);
my $subwidth = $img->width;
my $subheight = $img->height;
match_aspect($img, $width/$height, $subwidth, $subheight);
$img->scale($width, $height);
$img->get_active_drawable->edit_copy;
my $baseimg = $draw->get_image;
$baseimg->select_rectangle(CHANNEL_OP_REPLACE, $x, $y, $width, $height);
$draw->edit_paste(FALSE)->floating_sel_anchor;
$img->delete;
}
# Take a Red, Green, Blue color value and return Hue, Saturation and Value
# RGB and HSV data should be in the range 0-255 (note Hue is usually
# represented as 0-360, but here is scaled to be 0-255).
sub rgb2hsv {
my $r = shift;
my $g = shift;
my $b = shift;
my($h,$s,$v);
my $min = undef;
my $max = 0;
foreach my $color ($r, $g, $b) {
$min = $color if !defined($min) || $min>$color;
$max = $color if $color > $max;
}
$v = $max;
$s = $max?int(($max-$min)/$max*255+0.5):0;
if ($s == 0) {
$h = 0;
} else {
my $d = $max - $min;
if ($r == $max) {
$h = ($g-$b)/$d;
} elsif ($g == $max) {
$h = 2+($b-$r)/$d;
} else {
$h = 4+($r-$g)/$d;
}
# This:
# $h *= 60;
# $h += 360 if $h < 0;
# $h *= (256/360);
# , simplified is this:
$h= int(($h+($h<0?6:0)) * 128 / 3 + 0.5);
}
return ($h,$s,$v);
}
# Caclulate the "distance" between to HSV hue values in the range 0-255.
sub hue_dist {
my $h1 = shift;
my $h2 = shift;
my $d = abs($h1-$h2);
return($d>128?(256-$d):$d);
}
exit main;
__END__
( run in 0.639 second using v1.01-cache-2.11-cpan-39bf76dae61 )