2

I have some existing code in C# that I'm trying to move to an open source scripting language. The general idea is to read in a PNG image, and perform a "custom inversion" color transformation on it.

I want to:

  • simply invert all greyscale (ie R=G=B) pixels (using convert +negate works great for this!)
  • negate the luminance, only, of all non-greyscale pixels (ie, luminance of 1 becomes 0, .2 becomes .8)

Iterating through the pixels and using Graphics::ColorObject to convert the RGB to HSL may be a solution, but the script I wrote to do that is extremely slow, whereas in my C# code (which I would use if I was able to run this in a .NET environment) it only takes a second or two to convert an image.

I'm guessing this is possible with one or more successive convert commands (since convert +negateaccomplishes #1 easily) but am not sure how to do #2.

This is my extremely slow perl code, that I actually haven't been able to determine if it works because it takes so long. I am NOT much of a perl programmer, so I'm sure this can be optimized several times over.


  #!/usr/local/bin/perl
  use Image::Magick;
  use Graphics::ColorObject;
 my $image;

$image = new Image::Magick;

$image->Read($ARGV[0]);

my $width = $image->Get('width');
my $height = $image->Get('height');
my $w = 0;
my $h = 0;

for($w=0; $w GetPixel(x=>$w,y=>$h);
                #print "checking $w $h: $r $g $b\n";
                if($r == $g && $g == $b) {
                        # grey scale - invert
                        $r = 1 - $r;
                        $g = 1 - $g;
                        $b = 1 - $b;
                } else {
                        # convert only luminance
                        $color = Graphics::ColorObject->new_RGB([$r, $g, $b]);
                        ($h, $s, $l) = @{ $color->as_HSL() };
                        $l = 1 - $l;
                        $newcolor = Graphics::ColorObject->new_HSL([$h, $s, $l]);
                        ($r, $g, $b) = @{ $newcolor->as_RGB() };
                }
                $image->SetPixel(x=>$w,y=>$h,color=>[$r, $g, $b]);
        }
}

$image->Write($ARGV[0] + "_i");
General Grievance
  • 4,555
  • 31
  • 31
  • 45
tkrajcar
  • 1,702
  • 1
  • 17
  • 34
  • Show your slow code. Stackers could be interested but do not want to start from scratch. – daxim Mar 28 '11 at 18:02
  • Have you tried doing doing the conversion "in process" (i.e. inside the Perl script via API calls to ImageMagick). This might speed things up a bit since you don't have to fork separate instances of the "convert" executable. However, I suspect I/O is your biggest bottleneck. If Perl supports memory mapped I/O (likely) then you could mem-map the file and run the ImageMagick algorithm over the raw bytes, leaving it up to the OS to determine how to efficiently load the data. – metasim Mar 28 '11 at 20:00
  • PS: I suspect conversion to HSL will be required, but you can do a simple filter and do it on non-greyscale pixels. – metasim Mar 28 '11 at 20:05

1 Answers1

2

I expect this is what you are looking for using convert (done with ImageMagick on Ubuntu)

~$ convert rose: -colorspace HSL -channel Luminance -negate rose_out.pnm
~$ display rose: &  display rose_out.pnm &

Result is (original left)

rose / rose_negated_lum

renick
  • 3,873
  • 2
  • 31
  • 40