2010-07-17 3 views
9

"평균"은 redSum/numpixels, greenSum/numpixels, blueSum/numpixels로 정의되는 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) 
} 

다른 인터리브 어레이에 대해 더 자세히 설명하는 스칼라 방식이 있습니까? 모든 네 번째 요소를 반복하는 배열을 통해 투영을 얻는 방법은 무엇입니까? Stack Overflow 커뮤니티가 제공 할 수있는 전문 기술에 관심이 있습니다.

답변

10

pixels.grouped(3)은 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) 

UPDATE : 모두 3 개, 4 채널을 처리하려면, 기본적으로 내가 "0 개 이상의 요소"를 의미 여기

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

+0

완벽한 대답을. 이 답변은 foldLeft가 어떻게 작동하는지 더 잘 이해할 수있게 도와주었습니다. case 문을 사용하면 매우 쉽게 읽을 수 있습니다. Bravo – I82Much

+0

그룹화 된 값이 항상 3이 아닌 대신 알파 값에 따라 3 또는 4 인 채널 수와 동일하다는 사실을 처리하는 가장 좋은 방법은 무엇입니까? 항상 0,1,2 인덱스를 원하지만이 경우에는 항상 3 요소 배열을 가지고 있지 않기 때문에 더 이상 Array (r, g, b)를 수행 할 수 없다고 상상해보십시오. pixels.grouped (numChannels) .foldLeft ((0,0,0)) {case ((rsum, gsum, bsum), colors : Array [Int]) => (rsum + colors (0), gsum + colors (1), bsum + colors (2))} – I82Much

+0

@Sandor : 왜 누적 결과가 필요한지 알지 못합니다. –

6

이것은이 문제에 대한 정신없는 과잉이다. 그러나 나는 데이터 세트보다 많은 부분 축소를 수행하고 그것을위한 몇 가지 유틸리티 함수를 구현했다. 가장 일반적으로 사용되는 것은 reduceBy입니다. 컬렉션 (실제로는 Traversable), 파티션 함수, 매핑 함수 및 축소 함수를 사용하고 파티션에서 축소/매핑 된 값으로 매핑을 생성합니다.

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) 

높은 순서 프로그래밍의 굉장한 능력하기 전에 묵비권된다 감안할.

2

당신이 제공 한 해결책이 관용적 인 해결책이라고 생각하기 때문에 이것은 아주 좋은 질문입니다! 명령형 모델은이 문제에 실제로 부합합니다. 나는 잘 읽는 간단한 기능적 해결책을 찾으려고 노력했지만 그것을 할 수 없었다.

나는 pixels.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) 
} 
관련 문제