-1

I'd like to, basically, have a quick way to select a box (region of interest) in an image, and get geometry output in ImageMagick's format. I cannot see an easy way to do it with the default ImageMagick display viewer, so I'm looking for some API (and hopefully examples) to allow me to code my own viewer.

A bit of background: In ImageMagick • View topic - selecting a region of interest from command line (2008) it is said you cannot do it, however, there is display: ImageMagick - Region of Interest (2003?) which explains how to do it (but apparently it refers to an older version).

Anyways, this is how things look is you call display -size 300x500 pattern:checkerboard (pattern:checkerboard is built-in pattern image in imagemagick):

imagemagick display ROI

Once the "ImageMagick" display window is up, click on it; then Command menu shows - from it, choose /"Image Edit"/"Region of Interest..."; then can click and drag on the viewer display window. And you also get the geometry in upper left corner - but you cannot copy/paste it as text (so I've had to retype).

Also, display in command line mode takes up the terminal (linux - Make imagemagick's display exit at terminal, preserving the window (single instance mode) - Super User) - and I cannot see a way to force it to run in "single instance mode", such that I could issue filenames on the command line, and display would load them in the one and the same currently running instance.

Now, I've found Casting spells with ImageMagick - Image manipulation for programmers (2012), which mentions a MagickWand API; after some searching, I found on the imagemagick site:

So, my first thought was a script in Python - but apparently there is only a Perl API, which is fine.

However, what I need to code is basically a command line interface, which will start a display -like window process as a "single instance", and exit the terminal while passing parameters such as file name, -density etc to the window; the window would then react on mouse clicks, allowing selection of a crop geometry box (region of interest) - and finally, render the geometry string in a text box, so you can copy it. But as far as I can see, all the APIs are oriented toward performing the functions of the command-line convert.

So my question is - can any of these API's be used to program a display-like GUI; and do there exist any examples of a similar nature (preferably in a scripting language, but I'll live with C/C++) which can be pointed out?

Many thanks in advance for any answers,
Cheers!

Community
  • 1
  • 1
sdaau
  • 36,975
  • 46
  • 198
  • 278
  • Possibly related: [linux - ImageMagick DisplayImages() for Windows? - Stack Overflow](http://stackoverflow.com/questions/6228050/imagemagick-displayimages-for-windows) – sdaau May 19 '12 at 07:45
  • 1
    There was a guy working on a Windows GUI but have not seen any results as I have a 32bit Windows system and he was developing for 64bit. The thread in the Imagemagik forum is here: http://www.imagemagick.org/discourse-server/viewtopic.php?f=1&t=18219&hilit=gui May or may not be of help - he is the developer of combine ZP. – Bonzo May 19 '12 at 09:09
  • Somewhat related: [Command line SVG and image file viewer in Linux? - Super User](http://superuser.com/a/225465/39752); [A "quick" vector editor (SVG) for Linux (for annotating images?) - Super User](http://superuser.com/questions/352816/a-quick-vector-editor-svg-for-linux-for-annotating-images) – sdaau May 20 '12 at 08:57

1 Answers1

4

Well, this turned out to be a bit of a pain, but I managed to put together a Perl-Tk script with ImageMagick API, that behaves like what I wanted: imgckdis.pl (code also below). Here is a screenshot:

imgckdispl.png

Note that it can pretty much just display an image in hardcoded 400x400 px (although it may extend for bigger images) - there is no menus, no mouse interaction (scrollwheel zoom) - pretty much nothing :) The script only accepts one command-line argument - a file to be opened; but it can also understand ImageMagick specials like "xc:white" (the ImageMagick portion will even automatically render SVG files, as shown on screenshot).

But one thing it is capable of, is working in single instance mode: the first instance started becomes a "master", and draws the Tk window, and locks the respective terminal. Subsequent instances of the script, realizing the master instance is already started, will simply issue a command to the master to load a new image.

This "issuing a command to 'master'" turned out to be not so easy, as the collection of links below shows (as well as the revision notes in the online vesrion). I thought at first, that using interprocess-communication shared variables would allow me to store a "pointer by reference" to the master; and then allow the subsequent instances to call functions on it. Well, it seems that cannot be done - for one, Perl may discourage that - but even if you hop over all those checks, in the end you get a memory address which is not seen as in shared space, and so one cannot retrieve anything from it. Furthermore, the IPC::Shareable Perl package is possibly "guaranteed" only for integers and strings ?!

Nevertheless, the approach that finally worked is, as hinted, to have the "master" poll for changes in changed variables; and non-master instances to simply change this variable when they are called - and this approach seems to work... However, for a "real" application, one would then have to think of organizing quite a few of these shared variables..

Well, maybe one cannot still zoom and reposition the image, and draw a geometry rectangle - but, at least it's an example that can be demonstrated to be working (at least on Ubuntu) :)...

Hope this helps someone,
Cheers!

The code:

#!/usr/bin/perl

# imgckdis.pl
# http://sdaaubckp.svn.sf.net/viewvc/sdaaubckp/single-scripts/imgckdis.pl

use warnings;
use strict;
use Image::Magick; # sudo apt-get install perlmagick # debian/ubuntu
use Tk;
use MIME::Base64;

use Carp;
use Fcntl ':flock';

use Data::Printer;
use Class::Inspector;

use IPC::Shareable;


my $amMaster = 1;
my $file_read;

open my $self, '<', $0 or die "Couldn't open self: $!";
flock $self, LOCK_EX | LOCK_NB or $amMaster = 0;

if ($amMaster == 1) {
  print "We are master single instance as per flock\n";
  IPC::Shareable->clean_up_all;
}

if (!$ARGV[0]) {
  $file_read = "xc:white";
} else {
  $file_read = $ARGV[0];
}
chomp $file_read;


my %options = (
  create    => 1,
  exclusive => 0,
  mode      => 0644,
  destroy   => 0,
);

my $glue1 = 'dat1';
my $glue2 = 'dat2';

my $refcount;
my $reffname;
my $lastreffname;

my $refcount_handle = tie $refcount, 'IPC::Shareable', $glue1 , \%options ;
if ($amMaster == 1) {
  $refcount = undef;
}

my $reffname_handle = tie $reffname, 'IPC::Shareable', $glue2 , \%options ;
if ($amMaster == 1) {
  $reffname = undef;
}

my ($image, $blob, $content, $tkimage, $mw);


if ($amMaster == 1) { # if (not(defined($refcount))) {
  # initialize the assigns
  $lastreffname = "";

  $reffname_handle->shlock(LOCK_SH|LOCK_NB);
  $reffname = $file_read; #
  $reffname_handle->shunlock();

  $refcount_handle->shlock(LOCK_SH|LOCK_NB);
  $refcount = 1; #
  $refcount_handle->shunlock();
}

# mainly from http://objectmix.com/perl/771215-how-display-image-magick-image-tk-canvas.html
sub generateImageContent() {
  #fake a PGM then convert it to gif
  $image = Image::Magick->new(
    size => "400x400",
  );
  $image->Read($file_read); #("xc:white");
  $image->Draw(
    primitive => 'line',
    points => "300,100 300,500",
    stroke => '#600',
  );
  # set it as PGM
  $image->Set(magick=>'pgm');

  #your pgm is loaded here, now change it to gif or whatever
  $image->Set(magick=>'gif');
  $blob = $image->ImageToBlob();

  # Tk wants base64encoded images
  $content = encode_base64( $blob ) or die $!;
}

sub loadImageContent() {
  #fake a PGM then convert it to gif
  $image = Image::Magick->new(
    size => "400x400",
  );
  $image->Read($lastreffname); #("xc:red") for test

  # set it as PGM
  $image->Set(magick=>'pgm');

  #your pgm is loaded here, now change it to gif or whatever
  $image->Set(magick=>'gif');
  $blob = $image->ImageToBlob();

  # Tk wants base64encoded images
  $content = encode_base64( $blob ) or die $!;

  #~ $tkimage->read($content); # expects filename
  $tkimage->put($content); # works!
}


sub CleanupExit() {
  # only one remove() passes - the second fails: "Couldn't remove shared memory segment/semaphore set"
  (tied $refcount)->remove();
  IPC::Shareable->clean_up;
  $mw->destroy();
  print "Exiting appliction!\n";
  exit;
}

sub updateVars() {
  if ( not($reffname eq $lastreffname) ) {
    print "Change: ", $lastreffname, " -> ", $reffname, "\n";
    $lastreffname = $reffname;
    loadImageContent();
  }
}

if ( not($amMaster == 1) ) {
  # simply set the shared variable to cmdarg variable
  # (master's updateVars should take care of update)
  $reffname_handle->shlock(LOCK_SH|LOCK_NB);
  $reffname = $file_read;
  $reffname_handle->shunlock();

  # and exit now - we don't want a second instance
  print "Main instance of this script is already running\n";
  croak "Loading new file: $file_read";
}


$mw = MainWindow->new();
$mw->protocol(WM_DELETE_WINDOW => sub { CleanupExit(); } );

generateImageContent();
$tkimage = $mw->Photo(-data => $content);

$mw->Label(-image => $tkimage)->pack(-expand => 1, -fill => 'both');
$mw->Button(-text => 'Quit', -command => sub { CleanupExit(); } )->pack;

# polling function for sharable - 100 ms
$mw->repeat(100, \&updateVars);


MainLoop;



__END__

Relevant links:

Community
  • 1
  • 1
sdaau
  • 36,975
  • 46
  • 198
  • 278