performing 40k composite operations

Questions and postings pertaining to the usage of ImageMagick regardless of the interface. This includes the command-line utilities, as well as the C and C++ APIs. Usage questions are like "How do I use ImageMagick to create drop shadows?".
Post Reply
nessence

performing 40k composite operations

Post by nessence »

I have the following rmagick code, which I need to go faster (actual questions and benchmarks below):

Code: Select all

gc = Magick::Draw.new
plot_count = 0
while plot_count < points.size
  gc.composite(points[plot_count][0], points[plot_count][1], 0, 0, mydot, Magick::PlusCompositeOp)
  plot_count += 1
end
gc.draw(base)
My goal is to get 40k (points.size) composite operations in under a second.

The canvas ("base") is "256x256 DirectClass 8-bit", and "mydot" is "4x4 4x4+0+0 DirectClass 8-bit". Both are generated by ImageMagick.

Is there anyway to capture the composite affect for a given source, so it can be repeatedly applied to different pixel locations?
Is there a way I should prep the canvas and source so there are no interstitial operations?

Now for the weird part...

Currently, for the same 25k points, on the same blank canvas, the time to render is random, from 5 to 30 seconds. My suspicion is some kind of heap operation is causing a slowdown. My other thought is this could be OSX or video card driver if imagemagick is using any OSX operations for this. My assumption is if I get things fast enough on OSX, it will be faster on a server -- but that remains to be seen -- a server won't have the GPU my desktop has.

Is there anyway to ramp up imagemagick for an operation like this?

I'm hoping someone with experience can shed some light on how to optimize any part of this operation.
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: performing 40k composite operations

Post by magick »

For speed, you need to access the image pixels directly. You can further speed up the operation by adding OpenMP to your loop so the operations can be performed in parallel.
nessence

Re: performing 40k composite operations

Post by nessence »

magick wrote:For speed, you need to access the image pixels directly. You can further speed up the operation by adding OpenMP to your loop so the operations can be performed in parallel.
I googled openmp but most of the links are regarding deployment and not functionality. I'm using RMagick.

How do I add OpenMP to loop operations?
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: performing 40k composite operations

Post by magick »

For maximum speed you need direct access to the pixels. Does RMagick provide direct access to the pixels? We did not write it so we don't know. OpenMP is not available for Ruby but Ruby may permit threading.
nessence

Re: performing 40k composite operations

Post by nessence »

magick wrote:For maximum speed you need direct access to the pixels. Does RMagick provide direct access to the pixels? We did not write it so we don't know. OpenMP is not available for Ruby but Ruby may permit threading.
Yes, RMagick provides direct access to pixels.

First, I must note, I haven't had time to dig into the source for RMagick and ImageMagick's composite() and draw(). What I do know is that draw() is one call from Ruby so, even with threads, I couldn't parallelize it within my application (I would have to modify RMagick). I will do some homework and find out how draw() is implemented but I do know that rmagick uses ImageMagick APIs and doesn't generate CLI commands...

I could parallelize the 25k composite() (draw) calls but if ImageMagick has no builtin mechanism to do this asynchronously, it would be slow, as it would be relegated to my own Ruby locking code (Ruby is slow).

If I were to thread composite() calls, does ImageMagick Draw/composite provide a mechanism to asynchronously queue (or multi-thread) it's operations?

Unfortunately draw() is still the slowest method. Is OpenMP the only way ImageMagick can perform parallel operations?

I have imagemagick compiled with openmp and when set MAGICK_THREAD_LIMIT over 1, it seems ignored when running "identify -list resource" command. Is this by design?
nessence

Re: performing 40k composite operations

Post by nessence »

I relinquished to using direct pixel access instead of compositing.

This doesn't make sense to me though. I basically created a loop in Ruby which performs the same action as the Plus operation. No threading, no special libraries, and I even had to add window/clip operations for when the composite exceeded image boundaries.

For the same dataset, my Ruby code is 2x faster. (1-2sec for 25k points, 2-4sec for 50k points)

It looks like RMagick saves to an MPR(?) temp "cache" file between every operation. I'm not sure why it has to do this for every operation, rather than simply drawing on the pixel memory; destructive image (non-draw) composites actually generates additional interstitial files.

Is this a proper implementation?

If so, does (or will) IM provide an abstract canvas API so sequential Draw (or destructive image) operations are performed on the same memory address (direct pixels) rather than having to write a file between every operation?

(Feel free to move thread to developers...)
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: performing 40k composite operations

Post by magick »

We don't know enough about RMagick internals to determine where any bottleneck might be. For maximum speed we'd use the MagickCore API, something like this:

Code: Select all

  mybox_pixels=GetVirtualPixels(box,0,0,4,4,exception);
  #pragma omp parallel for schedule(dynamic,4)
  for (i=0; i < # points; i++)
  {  PixelPacket *pixels;
    pixels=GetAuthenticPixels(canvas,x[i],y[i],4,4,exception);
    if (pixels)
      memcpy(pixels,mybox_pixels,16*sizeof(*pixels));
    SyncAuthenticPixels(canvas,exception);
  }
That's a copy operation. For plus, you would have to sequentially add each pixel (e.g. pixels[j].red+=mybox_pixels[j].red).
Post Reply