Frage

Can someone please share sample code of imgscalr using AsyncScalr for resizing code? I am trying to use imgscalr (Scalr class) for image processing. It is a good and easy to use library, however, gives OutOfMemoryException too often. I am hoping using using AsyncScalr will solve my problem for low loads.

War es hilfreich?

Lösung

If you are familiar with the Java Concurrent libs, using the AsyncScalr class is really straight forward; if you are not familiar with the new concurrent libs, then the gist is this:

  1. Call an API method that does some work at an unknown point in the future; the method call returns a Future that wraps the actual work.
  2. The original API call actually queues up the work internally; if it isn't busy, it will likely do the work right away, but if it is busy and the queue is huge, it might take a while for the work to complete (in this case "the work" is the scaling of the image).
  3. The calling code (your code) that wants the result, can keep doing work until Future.isDone() returns true to indicate the work was completed OR the calling code can just block until the operation is done by calling: Future.get() -- this method returns the result of the work, in this case, a BufferedImage that represents the scaled result.

Code literally ends up looking like this:

// Block until result is done
BufferedImage result = AsyncScalr.resize(origImage, 125).get();

The difference between this code and using the Scalr class directly is that in a multi-threaded systems, if you call Scalr.resize() (or any of the image ops) from ALL your threads, every single one of those threads is going to start an expensive image operation, flooding your CPU with concurrent work and slowing the system to a crawl (choking out other processes running on it like DB or web server).

Using the AsyncScalr class you can safely call AsyncScalr.resize (or any of the other operations) from any number of threads and never worry about flooding the host system with work; AsyncScalr.THREAD_COUNT determines how many simultaneous jobs can occur at one time; you typically want to set this to the number of cores on the host machine or less-than the number of cores if the host machine is also hosting other important services like a database or web server (to ensure you don't choke out the other processes when scaling gets busy).

You can set this thread value on the command line at startup time for your app with the "imgscalr.async.threadCount" system property; by default it is "2", but you could set it to "1" if you were worried about the system being too low on memory.

Alternatively if you have work your thread can do while you wait for the result, you can do something like this to really make good use of asynchronous programming:

// Queue up the scaling operation (or any other op)
Future<BufferedImage> result = AsyncScalr.resize(origImage, 125);

/*
 * You can do other work here that doesn't need 'result', like making
 * DB calls, cleaning up temp files or anything else you might need to
 * do.
 */

// Now we are all done and need the resulting image, so we wait for it.
BufferedImage scaledImage = result.get();

// Do something with the image...

If you had a significant amount of other work you could do while waiting for the image to be scaled, you can simply loop on result.isDone() and keep working until the scaling operation is done; but if you only have a discrete/specific amount of work to do, no need to loop on isDone, just do the work then call Future.get() to get the result (or block until it is ready).

Hope that helps!

Andere Tipps

Here's a utility method that schedules the resizing of an image. The nice thing about this is that it returns a ListenableFuture, allowing you to attach a callback that gets executed once the image is resized.

/**
 * Schedules the asynchronous resizing of an image.
 * <p>
 * Uses all available processors to do so.
 * 
 * @param pathToImage
 *            the path to the image we want to resize
 * @param quality
 *            the quality we want the output image to have. One of {@link Method}.
 * @param desiredSize
 *            the resulting image will not have a bigger height or width than this
 * @return
 *         a {@link ListenableFuture} of the resulting image. You can add a callback to it using {@link Futures#addCallback(ListenableFuture, FutureCallback)}
 * @throws IOException
 *             if the image at {@code pathToImage} couldn't be read
 */
public static ListenableFuture<BufferedImage> resize(String pathToImage, Method quality, int desiredSize) throws IOException {

    // Configure AsyncScalr to use all available processors for resizing the images
    String nrOfProcessors = String.valueOf(Runtime.getRuntime().availableProcessors());
    System.setProperty(AsyncScalr.THREAD_COUNT_PROPERTY_NAME, nrOfProcessors);

    BufferedImage image = ImageIO.read(new File(pathToImage));
    Future<BufferedImage> ordinaryFuture = AsyncScalr.resize(image, quality, desiredSize);
    ListenableFuture<BufferedImage> futureImage = JdkFutureAdapters.listenInPoolThread(ordinaryFuture);
    image.flush();
    return futureImage;
}

And this is how you use resize:

ListenableFuture<BufferedImage> futureImage = resize("/path/to/img.png", Method.SPEED, 250);

Futures.addCallback(futureImage, new FutureCallback<BufferedImage>() {
  @Override
  public void onSuccess(BufferedImage result) {
          System.out.println("Your resized image is ready :-)");
  }
  @Override
  public void onFailure(Throwable t) {
          System.out.println("Couldn't resize image :-(");
  }
});

ListenableFuture and FutureCallback are both defined in the Guava library.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top