Scala - идиоматический способ подсчета сумм чередуемого массива? - PullRequest
9 голосов
/ 17 июля 2010

Я пытаюсь вычислить средний цвет изображения в Scala, где «среднее» определяется как redSum / Numpixels, GreenSum / Numpixels, BlueSum / Numpixels.используется для вычисления среднего цвета в прямоугольной области изображения (Растр).

// 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 для различных чередующихся массивов?Какой-нибудь способ получить проекцию на массив, который повторяется по каждому 4-му элементу?Меня интересует любой опыт, который может предоставить сообщество Stack Overflow.

Ответы [ 3 ]

10 голосов
/ 17 июля 2010

pixels.grouped(3) вернет Iterator[Array[Int]] массивов из 3 элементов. Так

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)}

_* здесь в основном означает «0 или более элементов». См. «Соответствие последовательностям» в http://programming -scala.labs.oreilly.com / ch03.html

6 голосов
/ 17 июля 2010

Это безумное излишество для этой проблемы, но я сделал много секционированных сокращений для наборов данных и создал для этого некоторые служебные функции.Наиболее общим из них является reduBy, который принимает коллекцию (фактически Traversable), функцию разделения, функцию отображения и функцию сокращения и создает карту из разделов в уменьшенные / отображенные значения.1003 * Учитывая, что тяжелая техника, ваша проблема становится

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)

Отключитесь перед удивительной силой программирования более высокого порядка.

2 голосов
/ 18 июля 2010

Это отличный вопрос, так как я думаю, что решение, которое вы предоставили, является идиоматическим решением! Императивная модель действительно соответствует этой проблеме. Я пытался найти простое функциональное решение, которое хорошо читается, но я не смог этого сделать.

Я думаю, что тот, у которого pix.grouped (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)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...