ConvertImageCommand() and Obj-C

The MagickWand interface is a new high-level C API interface to ImageMagick core methods. We discourage the use of the core methods and encourage the use of this API instead. Post MagickWand questions, bug reports, and suggestions to this forum.
Post Reply
kronik
Posts: 4
Joined: 2012-02-07T01:42:28-07:00
Authentication code: 8675308

ConvertImageCommand() and Obj-C

Post by kronik »

I tried to convert cameradeblur sample by myself but stuck on "convert" to "ConvertImageCommand" translation of:

Code: Select all


w1=`convert xc: -format "%[fx:($width%2)==0?$width:($width+1)]" info:`
h1=`convert xc: -format "%[fx:($height%2)==0?$height:($height+1)]" info:`
cx=`convert xc: -format "%[fx:floor(($width+1)/2)]" info:`
cy=`convert xc: -format "%[fx:floor(($height+1)/2)]" info:`
d1=`convert xc: -format "%[fx:max($w1,$h1)]" info:`
fd1=`convert xc: -format "%[fx:$amount*pi/$d1]" info:`
sinang=`convert xc: -format "%[fx:sin($rotation*pi/180)]" info:`
cosang=`convert xc: -format "%[fx:cos($rotation*pi/180)]" info:`
qnoise=`convert xc: -format "%[fx:quantumrange*$noise]" info:`
convert -size ${w1}x1 xc: \
-fx "zz=$fd1*(i-$cx)*$cosang; zz?sin(zz)/(zz):1" \
-scale ${w1}x${h1}\! $tmpF
convert \( $infile -alpha off +fft \) \
\( $tmpF $tmpF -compose multiply -composite -evaluate add $qnoise \) \
\( -clone 0 $tmpF -compose multiply -composite \) \
\( -clone 1 $tmpF -compose multiply -composite \) \
\( -clone 3 -clone 2 +swap -compose divide -composite \) \
\( -clone 4 -clone 2 +swap -compose divide -composite \) \

Please help me to convert this script.

Could anyone explain the meaning of XC and FX keywords too.
el_supremo
Posts: 1015
Joined: 2005-03-21T21:16:57-07:00

Re: ConvertImageCommand() and Obj-C

Post by el_supremo »

xc: is the built-in image function which creates a canvas. If no size is specified, as in this case, then the image will be a single pixel. But the first nine statements aren't really using xc: at all. They are using the fx: feature to calculate values which will be used in the final convert statement. So you can implement these in C very easily without using convert at all as long as you know what the values of the scripting variables such as width, height and amount are.

This first statement sets "w1" to the value of "width" if width is even and to width+1 if width is odd.

Code: Select all

w1=`convert xc: -format "%[fx:($width%2)==0?$width:($width+1)]" info:`
This does the same for h1 and height

Code: Select all

h1=`convert xc: -format "%[fx:($height%2)==0?$height:($height+1)]" info:`
The next two set cx,cy to the midpoint of width and height.

Code: Select all

cx=`convert xc: -format "%[fx:floor(($width+1)/2)]" info:`
cy=`convert xc: -format "%[fx:floor(($height+1)/2)]" info:`
Sets d1 to the larger of w1 and h1

Code: Select all

d1=`convert xc: -format "%[fx:max($w1,$h1)]" info:`
Something to do with rotation fd1 = amount*PI/dl;

Code: Select all

fd1=`convert xc: -format "%[fx:$amount*pi/$d1]" info:`
Now a sin and cos value

Code: Select all

sinang=`convert xc: -format "%[fx:sin($rotation*pi/180)]" info:`
cosang=`convert xc: -format "%[fx:cos($rotation*pi/180)]" info:`
qnoise = quantumrange*noise

Code: Select all

qnoise=`convert xc: -format "%[fx:quantumrange*$noise]" info:`
All these values are then used in the convert command at the end.

Pete
Sorry, my ISP shutdown all personal webspace so my MagickWand Examples in C is offline.
See my message in this topic for a link to a zip of all the files.
kronik
Posts: 4
Joined: 2012-02-07T01:42:28-07:00
Authentication code: 8675308

Re: ConvertImageCommand() and Obj-C

Post by kronik »

Great!

But it's still unclear moments:

1. -size parameter "${w1}x1". If value of w1 is 480 (for example) could this be translated to "480x1"?
2. zz=$fd1*(i-$cx)*$cosang. Should I define "i"?
3. Should I pass string located in parenthesis as one argument ( $infile -alpha off +fft)?

Code: Select all

convert \( $infile -alpha off +fft \) \
\( $tmpF $tmpF -compose multiply -composite -evaluate add $qnoise \) \
\( -clone 0 $tmpF -compose multiply -composite \) \
\( -clone 1 $tmpF -compose multiply -composite \) \
\( -clone 3 -clone 2 +swap -compose divide -composite \) \
\( -clone 4 -clone 2 +swap -compose divide -composite \) \
-delete 0-4 +ift -crop ${width}x${height}+0+0 +repage \
$outfile
el_supremo
Posts: 1015
Joined: 2005-03-21T21:16:57-07:00

Re: ConvertImageCommand() and Obj-C

Post by el_supremo »

1. -size parameter "${w1}x1". If value of w1 is 480 (for example) could this be translated to "480x1"?
Yes.
2. zz=$fd1*(i-$cx)*$cosang. Should I define "i"?
In effect what happens here is that IM executes the -fx argument for each value of i from zero up to the width of the image (Fred - is that right?).
So what it does is similar to this:

Code: Select all

for (i=0;i<width;i++) {
    zz=$fd1*(i-$cx)*$cosang;
    pixel(i,1) = zz ? sin(zz)/(zz) : 1;
}
3. Should I pass string located in parenthesis as one argument ( $infile -alpha off +fft)?
No. If this is executed as a convert command then the parentheses are each a separate argument.

Pete
Sorry, my ISP shutdown all personal webspace so my MagickWand Examples in C is offline.
See my message in this topic for a link to a zip of all the files.
kronik
Posts: 4
Joined: 2012-02-07T01:42:28-07:00
Authentication code: 8675308

Re: ConvertImageCommand() and Obj-C

Post by kronik »

Thanks for your explanation!

Here is my conversion result:

Code: Select all

    MagickWandGenesis();
    magick_wand = NewMagickWand();
    UIImage *img = [UIImage imageNamed:@"test.png"];
    [imageViewButton setImage:img forState:UIControlStateNormal];
    
    NSData * dataObject = UIImagePNGRepresentation(img);//UIImageJPEGRepresentation([imageViewButton imageForState:UIControlStateNormal], 90);
    MagickBooleanType status;
    status = MagickReadImageBlob(magick_wand, [dataObject bytes], [dataObject length]);
    if (status == MagickFalse) {
	ThrowWandException(magick_wand);
    }
	
    int rotation = 0;
    
    int w1 = (int)img.size.width;
    int h1 = (int)img.size.height;
    
    w1 = (w1%2) == 0?w1:w1+1;
    h1 = (h1%2) == 0?h1:h1+1;
    
    int cx = (int)floor((img.size.width + 1) / 2.0);
    int cy = (int)floor((img.size.height + 1) / 2.0);
    int d1 = MAX(w1, h1);
    float fd1 = rotation  * M_PI / d1;
    float sinang = sin(rotation * M_PI / 180);
    float cosang = cos(rotation * M_PI / 180);
    float qnoise = 0.0;
    
    char *input_image = strdup([[[NSBundle mainBundle] pathForResource:@"test" ofType:@"png"] UTF8String]);
    char *output_image = strdup([[[NSBundle mainBundle] pathForResource:@"test_out" ofType:@"png"] UTF8String]);
    char *tmp_img1 = strdup([[[NSBundle mainBundle] pathForResource:@"tmp_img1" ofType:@"png"] UTF8String]);

    char size[32];
    char tmp1[128];
    char tmp2[32];
    char tmp3[1024];
    char tmp4[1024];
    char tmp5[1024];
    char tmp6[1024];
    char tmp7[128];
    
    sprintf(size, "%dx1", w1);
    sprintf(tmp1, "\"zz=%f*(i-%d)*%f; zz?sin(zz)/(zz):1\"", fd1, cx, cosang);
    sprintf(tmp2, "%dx%d!", w1, h1);
    
    char *argv1[] = { "convert", "-size", size, "xc:", "-fx", tmp1, "-scale", tmp2, tmp_img1, NULL };

    //char *argv[] = { "convert", input_image, "-resize", "100x100", output_image, NULL };

    ImageInfo *imageInfo = AcquireImageInfo();
    ExceptionInfo *exceptionInfo = AcquireExceptionInfo();
    
    status = ConvertImageCommand(imageInfo, 9, argv1, NULL, exceptionInfo);
    
    if (status == MagickFalse)
    {
        NSLog(@"Error in first call");
    }
        
    sprintf(tmp3, "(%s -alpha off +fft )", input_image);
    sprintf(tmp4, "( %s %s -compose multiply -composite -evaluate add %f)", tmp_img1, tmp_img1, qnoise);
    sprintf(tmp5, "( -clone 0 %s -compose multiply -composite )", tmp_img1);
    sprintf(tmp6, "( -clone 1 %s -compose multiply -composite )", tmp_img1);
    sprintf(tmp7, "%dx%d+0+0", w1, h1);
    
    char *argv2[] = { "convert", tmp3, tmp4, tmp5, tmp6,
                      "( -clone 3 -clone 2 +swap -compose divide -composite )",
                      "( -clone 4 -clone 2 +swap -compose divide -composite )",
                      "-delete", "0-4", "+ift", "-crop", tmp7, "+repage", output_image, NULL };

    status = ConvertImageCommand(imageInfo, 14, argv2, NULL, exceptionInfo);
    
    if (status == MagickFalse)
    {
        NSLog(@"Error in second convertion");
    }
    
    free(input_image);
    free(tmp_img1);
    free(output_image);
    
    size_t my_size;
    unsigned char * my_image = MagickGetImageBlob(magick_wand, &my_size);
    NSData * data = [[NSData alloc] initWithBytes:my_image length:my_size];
    free(my_image);
    magick_wand = DestroyMagickWand(magick_wand);
    MagickWandTerminus();
    UIImage * image = [[UIImage alloc] initWithData:data];
    [data release];
	
    [imageViewButton setImage:image forState:UIControlStateNormal];
    [image release];
But in both calls of ConvertImageCommand I got status equal to MagickFalse and final image equal to original image.

Please could anybody help me to understand an error?

I've uploaded this project to dropbox
el_supremo
Posts: 1015
Joined: 2005-03-21T21:16:57-07:00

Re: ConvertImageCommand() and Obj-C

Post by el_supremo »

The problem is that tmp3, tmp4, tmp5 and tmp6 contain multiple arguments which must be in individual strings. Even a parenthesis is a separate argument. So where you have

Code: Select all

char *argv2[] = { "convert", tmp3, tmp4, tmp5, tmp6,
It should be:

Code: Select all

char *argv2[] = { "convert", "(", input_image, "-alpha", "off", "+fft", ")", "(", tmp_img1,tmp_img1, "-compose", ........ etc.
Wherever you have a single argument when used on the command line, that must be a separate element of argv2[]. If a command line argument is enclosed in quotes then it will be a single string in the argv2[] strings.

Pete
Sorry, my ISP shutdown all personal webspace so my MagickWand Examples in C is offline.
See my message in this topic for a link to a zip of all the files.
kronik
Posts: 4
Joined: 2012-02-07T01:42:28-07:00
Authentication code: 8675308

Re: ConvertImageCommand() and Obj-C

Post by kronik »

I did as you told me... but nothing changed

Code: Select all

int rotation = 0;
    
    int w1 = (int)img.size.width;
    int h1 = (int)img.size.height;
    
    w1 = (w1%2) == 0?w1:w1+1;
    h1 = (h1%2) == 0?h1:h1+1;
    
    int cx = (int)floor((img.size.width + 1) / 2.0);
    int cy = (int)floor((img.size.height + 1) / 2.0);
    int d1 = MAX(w1, h1);
    float fd1 = rotation  * M_PI / d1;
    float sinang = sin(rotation * M_PI / 180);
    float cosang = cos(rotation * M_PI / 180);
    float qnoise = 0.0;
    
    char *input_image = strdup([[[NSBundle mainBundle] pathForResource:@"test" ofType:@"png"] UTF8String]);
    char *output_image = strdup([[[NSBundle mainBundle] pathForResource:@"test_out" ofType:@"png"] UTF8String]);
    char *tmp_img1 = strdup([[[NSBundle mainBundle] pathForResource:@"tmp_img1" ofType:@"png"] UTF8String]);

    char size[32];
    char tmp1[128];
    char tmp2[32];
    char tmp3[1024];
    char tmp4[1024];
    char tmp5[1024];
    char tmp6[1024];
    char tmp7[128];
    
    sprintf(size, "%dx1", w1);
    sprintf(tmp1, "\"zz=%f*(i-%d)*%f; zz?sin(zz)/(zz):1\"", fd1, cx, cosang);
    sprintf(tmp2, "%dx%d!", w1, h1);
    
    char *argv1[] = { "convert", "-size", size, "xc:", "-fx", tmp1, "-scale", tmp2, tmp_img1, NULL };

    ImageInfo *imageInfo = AcquireImageInfo();
    ExceptionInfo *exceptionInfo = AcquireExceptionInfo();
    
    status = ConvertImageCommand(imageInfo, 9, argv1, NULL, exceptionInfo);
    
    if (status == MagickFalse)
    {
        NSLog(@"Error in first call");
    }
        
    sprintf(tmp3, "(%s -alpha off +fft )", input_image);
    sprintf(tmp4, "%f", qnoise);
    sprintf(tmp5, "( -clone 0 %s -compose multiply -composite )", tmp_img1);
    sprintf(tmp6, "( -clone 1 %s -compose multiply -composite )", tmp_img1);
    sprintf(tmp7, "%dx%d+0+0", w1, h1);
    
    char *argv2[] = { "convert", "(", input_image, "-alpha", "off", "+fft", ")", 
                      "(", tmp_img1, tmp_img1, "-compose", "multiply", "-composite", "-evaluate", "add", tmp4, ")",
                      "(", "-clone", "0", tmp_img1, "-compose", "multiply", "-composite", ")",
                      "(", "-clone", "1", tmp_img1, "-compose", "multiply", "-composite", ")",
                      "(", "-clone", "3", "-clone", "2", "+swap", "-compose", "divide", "-composite", ")",
                      "(", "-clone", "4", "-clone", "2", "+swap", "-compose", "divide", "-composite", ")",
                      "-delete", "0-4", "+ift", "-crop", tmp7, "+repage", output_image, NULL };

    status = ConvertImageCommand(imageInfo, 60, argv2, NULL, exceptionInfo);
So status is MagickFalse after both calls of ConvertImageCommand moreover original and final images still same.

Is there any other suggestions?
User avatar
anthony
Posts: 8883
Joined: 2004-05-31T19:27:03-07:00
Authentication code: 8675308
Location: Brisbane, Australia

Re: ConvertImageCommand() and Obj-C

Post by anthony »

The 'NULL' on the end I is NOT needed. argc passed how many arguments is needed.

My suggestion would be to first try and simplify the result.

Also check the "exception" at the end. You may not be getting a fatal exception.

And finally are you calling MagickCoreGenesis() and MagickCoreTerminus() to initialise the core library?


This works for me... Note the exception handling!

Code: Select all

/*
   Bare minimal test of a direct call to ConvertImageCommand()
   Actually this is basically what the "convert" command does.

   Compile with...
     gcc -I`pwd` -lMagickWand -lMagickCore convert_equiv.c -o convert_equiv

*/
#include <stdio.h>
#include "magick/studio.h"
#include "magick/exception.h"
#include "magick/exception-private.h"
#include "magick/image.h"
#include "wand/MagickWand.h"
#include "wand/convert.h"

int main(int argc, char **argv)
{
  MagickCoreGenesis(argv[0],MagickFalse);

  {
    char *args[] = { "convert", "-size", "100x100", "xc:red", "show:" };
    int args_count = 5;

    ImageInfo *image_info = AcquireImageInfo();
    ExceptionInfo *exception = AcquireExceptionInfo();

    MagickBooleanType status =
       ConvertImageCommand(image_info, args_count, args, NULL, exception);

    if (exception->severity != UndefinedException)
    {
      status=MagickTrue;
      CatchException(exception);
    }

    // This should not be needed as error is reported as an exception
    if (status == MagickFalse)
      fprintf(stderr, "Error in call\n");

    image_info=DestroyImageInfo(image_info);
    exception=DestroyExceptionInfo(exception);
  }
  MagickCoreTerminus();
}
This works.

Now if I replace the argument "-size" with "-sze" and I get an error....
convert_equiv: unrecognized option `-sze' @ error/convert.c/ConvertImageCommand/2732.

The use of a NULL on the end of the array is however good, if you want to automatically count up your arguments...
For example...

Code: Select all

    char *args[] = { "my_program", "-size", "100x100", "xc:red", "rose:",
                     "+append", "info:", NULL };
    int arg_count;
    for(arg_count = 0; args[arg_count] != (char *)NULL; arg_count++);

PS: the -I`pwd` in the compile command is because I compiled on the top level of 'sources' directory (IM compiled but not installed) as I am doing IM development at this time.

PPS: I am at this time writing a replacement ConvertImageCommand() for the IMv7 CLI API. As such I am currently very intimate with this specific section of code right now. New command will read options much like convert, or read options from a script file (using very simplified shell like argument parser).

PPPS: Actually my new organization will also let you make individual option calls much more easily, one or more option on the fly, without requiring you to give a final output file. However The new command will be much more strict on 'do it in order' than "convert" (no IMv5 legacy option handling).
Anthony Thyssen -- Webmaster for ImageMagick Example Pages
https://imagemagick.org/Usage/
Post Reply