0

I'm trying to wrap my head around futures, and have a little sample of scaling a directory of images going...

Can some explain to me how this should work?

import com.sksamuel.scrimage._
import scala.concurrent._
import ExecutionContext.Implicits.global
import java.io._

object Test {

    def main(args: Array[String]) = {
        val dir = new File("/Users/bthibault/Downloads/ff_images/")
        dir.listFiles().foreach(file => AsyncImage(file).map( x => x.scale(0.5) ).onSuccess {
            case image => image.writer(Format.PNG).withMaxCompression.write(file)
        })
    }
}

I'm using the Scrimage package https://github.com/sksamuel/scrimage/ where he gives an example of the Async operation... here is the actual Async file:

https://github.com/sksamuel/scrimage/blob/master/core/src/main/scala/com/sksamuel/scrimage/AsyncImage.scala

Can somebody help me understand what I need to do to get this working? I was playing with the map() function ... my thought was I need to return a real 'image' onSuccess rather than an AsyncImage ....

sksamuel
  • 16,154
  • 8
  • 60
  • 108
bonez
  • 685
  • 1
  • 16
  • 39
  • Do you mean to use something like [`Await.result`](http://www.scala-lang.org/api/current/#scala.concurrent.Await$) to get the `Image` inside the `Future`? – ggovan May 06 '14 at 01:59
  • What is it you are trying to do / ask here. Are you just wanting to know how to asynchronously scale each image inside a folder using the foreach and AsyncImage? – sksamuel May 06 '14 at 12:38
  • You got it... the original example : val dir = new File("/home/sam/images") dir.listFiles().foreach(file => AsyncImage(file).filter(GrayscaleFilter).onSuccess { case image => image.writer(Format.PNG).withMaxCompression.write(file) }) ... trying to replace filter with a scale mechanism, want to how I should get it work and what its doing. I read up about futures, but can't quite grasp whats happening here. – bonez May 06 '14 at 13:37

1 Answers1

2

If you are trying to asynchronously resize all the images in the folder, and are asking for guidance on the correct syntax, then this will work:

object AsyncBatch extends App {

  val dir = new File(args(0)) 
  dir.listFiles.map(file =>
    AsyncImage(file)
      .flatMap(x => x.scale(0.5))
      .onSuccess {
      case image => image.writer(Format.PNG).write(file)
    })
}

Note: The withMaxCompression isn't supported on AsyncWriters. That's an oversight of the library.

Another way you can do this is with for comprehensions:

  val dir = new File(args(0)) 
  for ( file <- dir.listFiles;
        image <- AsyncImage(file);
        scaled <- image.scale(0.5) ) {
    scaled.writer(Format.PNG).write(file)
  }

Which I think is easier to read when you have lots of flatMaps and maps going on.

sksamuel
  • 16,154
  • 8
  • 60
  • 108
  • Thanks this is what I was looking for... apparently you can't call AsyncImage.scaleTo .. my other goal was just use this library as a springboard for jumping into Async scala. So anytime you call anything with a future you need to use map , flatMap, filter.. etc ? – bonez May 06 '14 at 13:41
  • 1
    map, flatMap and filter act on the future to create a new future. Kind of like a pipeline of futures. You do this and then once ready you do that. This is a good reference, but I don't know how beginner friendly it is: http://docs.scala-lang.org/overviews/core/futures.html – sksamuel May 06 '14 at 13:51
  • Your solution, just returned in 1 second without doing anything... whereas the sync version: object CreateImages extends App { val dir = new File("/Users/bthibault/Downloads/ff_images") dir.listFiles.map(file => Image(file).scale(0.5).writer(Format.PNG).withMaxCompression.write(file)) } Seems to work – bonez May 06 '14 at 14:42
  • 1
    Because, I bet, you are letting your program "exit" without waiting for the future to complete? I think you need to post up a general purpose future based question if you're new to the concept. – sksamuel May 06 '14 at 17:11
  • got it. I added thread.sleep at the end just as test and i got it working. I'll keep investigating Futures. – bonez May 06 '14 at 18:19
  • 1
    The problem with a thread sleep is you have to guess how long they will take. If you take your futures, and do Future.sequence(futures), that will give you back a single Future of a list, rather than a List of separate futures. Then you can wait for the future to complete, if you need to, with Await.result(future, 2.minutes) or whatever. You'll need to import import scala.concurrent.duration._ to do the 2.minutes part. – sksamuel May 06 '14 at 21:15