代码之家  ›  专栏  ›  技术社区  ›  I82Much

Scala—计算交错数组和的惯用方法?

  •  9
  • I82Much  · 技术社区  · 14 年前

    我正在尝试计算Scala中图像的平均颜色,其中“平均”定义为红/像素、绿/像素、蓝/像素。

    这是我用来计算图像矩形区域(光栅)平均颜色的代码。

    // A raster is an abstraction of a piece of an image and the underlying
    // pixel data.
    // For instance, we can get a raster than is of the upper left twenty
    // pixel square of an image
    def calculateColorFromRaster(raster:Raster): Color = {
      var redSum = 0
      var greenSum = 0
      var blueSum = 0
    
      val minX = raster.getMinX()
      val minY = raster.getMinY()
    
      val height = raster.getHeight()
      val width = raster.getWidth()
      val numPixels = height * width
    
      val numChannels = raster.getNumBands() 
    
      val pixelBuffer = new Array[Int](width*height*numChannels)
      val pixels = raster.getPixels(minX,minY,width,height,pixelBuffer)
    
      // pixelBuffer now filled with r1,g1,b1,r2,g2,b2,...
      // If there's an alpha channel, it will be r1,g1,b1,a1,r2,... but we skip the alpha
      for (i <- 0 until numPixels) {
        val redOffset = numChannels * i
        val red = pixels(redOffset)
        val green = pixels(redOffset+1)
        val blue = pixels(redOffset+2)
    
        redSum+=red
        greenSum+=green
        blueSum+=blue
      }
      new Color(redSum / numPixels, greenSum / numPixels, blueSum / numPixels)
    }
    

    有没有更惯用的Scala方法来总结不同的交错数组?有什么方法可以得到数组上每四个元素迭代一次的投影?我对Stack Overflow社区能提供的任何专业知识都感兴趣。

    3 回复  |  直到 14 年前
        1
  •  10
  •   Alexey Romanov    14 年前

    pixels.grouped(3) 将返回一个 Iterator[Array[Int]] 三元素数组的集合。所以呢

    val pixelRGBs = pixels.grouped(3)
    
    val (redSum, greenSum, blueSum) = 
      pixelRGBs.foldLeft((0, 0, 0)) {case ((rSum, gSum, bSum), Array(r, g, b)) => (rSum + r, gSum + g, bSum + b)}
    
    new Color(redSum / numPixels, greenSum / numPixels, blueSum / numPixels)
    

    更新:为了处理3和4频道,我会写

    pixels.grouped(numChannels).foldLeft((0, 0, 0)) {case ((rSum, gSum, bSum), Array(r, g, b, _*)) => (rSum + r, gSum + g, bSum + b)}
    

    _* http://programming-scala.labs.oreilly.com/ch03.html

        2
  •  6
  •   Dave Griffith    14 年前

      def reduceBy[A, B, C](t: Traversable[A], f: A => B, g: A => C, reducer: (C, C) => C): Map[B, C] = {
        def reduceInto(map: Map[B, C], key: B, value: C): Map[B, C] =
          if (map.contains(key)) {
            map + (key -> reducer(map(key), value))
          }
          else {
            map + (key -> value)
          }
        t.foldLeft(Map.empty[B, C])((m, x) => reduceInto(m, f(x), g(x)))
      }
    

    考虑到那台重型机器,你的问题就变成了

    val sumByColor:Map[Int, Int] = reduceBy(1 until numPixels, (i => i%numChannels), (i=>pixel(i)), (_+_))
    return Color(sumByColor(0)/numPixels, sumByColor(1)/numPixels, sumByColor(2)/numPixels)
    

        3
  •  2
  •   BenMorel Manish Pradhan    11 年前

    这是一个很好的问题,因为我认为您提供的解决方案是惯用的解决方案!命令式模型非常适合这个问题。我试图找到一个简单的功能解决方案,读起来很好,但我做不到。

    我觉得有像素的(3)很好,但我不确定它是否比你的好。

    import java.awt.image.Raster
    import java.awt.Color
    
    def calculateColorFromRaster(raster:Raster): Color = {
      val minX = raster.getMinX()
      val minY = raster.getMinY()
    
      val height = raster.getHeight()
      val width = raster.getWidth()
      val numPixels = height * width
    
      val numChannels = raster.getNumBands()
    
      val pixelBuffer = new Array[Int](width*height*numChannels)
      val pixels = raster.getPixels(minX,minY,width,height,pixelBuffer)
    
      // pixelBuffer now filled with r1,g1,b1,r2,g2,b2,...
      // If there's an alpha channel, it will be r1,g1,b1,a1,r2,... but we skip the alpha
    
      // This case class is only used to sum the pixels, a real waste of CPU!
      case class MyPixelSum(r: Int, g: Int, b: Int){
        def +(sum: MyPixelSum) = MyPixelSum(sum.r +r, sum.g + g, sum.b + b)
      }
    
      val pixSumSeq= 0 until numPixels map((i: Int) => {
        val redOffset = numChannels * i
        MyPixelSum(pixels(redOffset), pixels(redOffset+1),pixels(redOffset+2))
      })
      val s = pixSumSeq.reduceLeft(_ + _)
    
      new Color(s.r / numPixels, s.g / numPixels, s.b / numPixels)
    }