2010-07-17 14 views
9

मैं स्कैला में एक छवि के औसत रंग की गणना करने का प्रयास कर रहा हूं, जहां "औसत" को रेडसम/numpixels, greenSum/numpixels, blueSum/numpixels के रूप में परिभाषित किया गया है।स्कैला - interleaved सरणी की रकम की गणना करने के लिए बेवकूफ तरीका?

यहां कोड है जिसका उपयोग मैं छवि (रास्टर) के आयताकार क्षेत्र में औसत रंग की गणना करने के लिए कर रहा हूं।

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

क्या अलग-अलग इंटरलेव किए गए सरणीओं पर संक्षेप में एक और मूर्खतापूर्ण स्कैला तरीका है? सरणी पर प्रक्षेपण करने का कोई तरीका है जो प्रत्येक चौथे तत्व पर पुनरावृत्त होता है? मुझे स्टैक ओवरफ्लो समुदाय प्रदान करने वाली किसी भी विशेषज्ञता में रूचि है।

उत्तर

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) 

अद्यतन: दोनों 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

+0

बिल्कुल सही जवाब। इस जवाब ने मुझे यह समझने में भी मदद की है कि कैसे फोल्ड लाइफ काम करता है। केस स्टेटमेंट का उपयोग भी इसे बहुत पठनीय बनाता है। ब्रावो – I82Much

+0

इस तथ्य को संभालने का सबसे अच्छा तरीका क्या है कि समूहित मान हमेशा 3 नहीं होता है, लेकिन इसके बजाय चैनलों की संख्या के बराबर है, जो अल्फा मान के आधार पर 3 या 4 हो सकता है? मैं हमेशा 0,1,2 सूचकांक चाहता हूं, लेकिन उस मामले में मुझे कल्पना है कि मैं अब्रे (आर, जी, बी) केस नहीं कर सका क्योंकि मेरे पास हमेशा 3 तत्व सरणी नहीं है। पिक्सल.grouped (numChannels)। फोल्ड लेफ्ट ((0,0,0)) {केस ((आरएसयूएम, जीएसयूएम, बीएसयूएम), रंग: ऐरे [Int]) => (rsum + colors (0), gsum + colors (1), बीएसयूएम + रंग (2))} – I82Much

+0

@ सैंडोर: मुझे नहीं लगता कि आप संचयी परिणाम क्यों चाहते हैं (यानी शुरुआत में केवल पिक्सेल पर रकम)। –

6

यह इस समस्या के लिए पागल ओवरकिल है, लेकिन मैं डेटासेट पर बहुत से विभाजित कटौती करता हूं, और इसके लिए कुछ उपयोगिता कार्यों का निर्माण किया है, "अनुक्रमों पर मिलान" देखें। उनमें से सबसे सामान्य कम है, जो एक संग्रह (वास्तव में एक ट्रैवर्सबल), एक विभाजन समारोह, एक मैपिंग फ़ंक्शन, और एक कमी समारोह लेता है, और विभाजन से कम/मैप किए गए मानों के लिए एक नक्शा उत्पन्न करता है।

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) 
} 
संबंधित मुद्दे