A BlurFilter-nek azt a jó tulajdonságát fogjuk kihasználni hogy nem változtatja meg túlságosan (jobb estben egyáltalán) a képen elhelyezkedő pixelek összértékét. A legegyszerűbb blur algoritmus egy pixel új értékét a körülötte lévők átlagolásával számítja ki így kapunk egy elmosódott képet. Ha sikerülne egy kép tartalmának pixeleit feketére, a hátteret fehérre változtatni majd egy elmosó algoritmust használva az összes pixelt közel egy átlag szürkeségi szintre hoznunk akkor egy pixel értékéből meg tudnánk mondani hogy az összes fekete pixel hány százalékban fordul elő a fehér képen.
Szerencsére a BlurFilter hasonló módon működik kellően gyors is és a BitmapData treshold metódusa is a segítségünkre van.

Az elképzelést az alábbi alkalmazás valósítja meg:

var picture:Bitmap
var maskData:BitmapData
var tresholdMap:BitmapData
var blurMap:BitmapData

init()

function init():void
{
	picture = new Bitmap(new DogPic(0,0))
	blurMap = new BitmapData(200, 200, false, 0xFFFFFF)
	tresholdMap = new BitmapData(200, 200, false, 0xFFFFFF)

	sampleArea.sampleCont.addChild(new Bitmap(blurMap))
	tresholdArea.tresholdCont.addChild(new Bitmap(tresholdMap))
	imageArea.addChild(picture)

	imageCombo.addEventListener(Event.CHANGE, onComboChange)

	calcArea()
	countPixels()
}

function countPixels():void
{
	var s:uint = getTimer()
	var pixels:ByteArray = tresholdMap.getPixels(tresholdMap.rect)
	pixels.position = 0
	var count:uint
	var ln:uint = pixels.length
	var all:uint = ln/4
	while(pixels.position < ln){
		var pxe:uint = pixels.readUnsignedInt()
		if(pxe < 0xFFFFFFFF){
			count++
		}
	}
	var time:uint = getTimer()-s
	var percent:Number = (count/all)*100

	output1Field.text = percent.toFixed(1) +"% | "+time+" ms"
}

function calcArea():void
{
	var matr:Matrix = new Matrix()
	matr.scale(tresholdMap.width/picture.width, tresholdMap.height/picture.height)

	tresholdMap.draw(picture.bitmapData, matr)
	tresholdMap.threshold(tresholdMap, tresholdMap.rect, new Point(), "<", 0xFFFEFEFE)

	blurMap.draw(tresholdMap)

	var s:uint = getTimer()
	blurMap.applyFilter(blurMap, blurMap.rect, new Point(), new BlurFilter(200,200,3))

	var sSize:uint = 50
	var pxValue:uint
	var sampleNum:uint
	for(var x:int=sSize; x<blurMap.width; x+=sSize){
		for(var y:int=sSize; y<blurMap.height; y+=sSize){
			pxValue += blurMap.getPixel(x, y) & 0x0000FF
			sampleNum++
		}
	}
	pxValue /= sampleNum
	var time:uint = getTimer()-s

	output2Field.text = (100-(pxValue/255)*100).toFixed(1) +"% | "+time+" ms"
}

function onComboChange(event:Event):void
{
	var className:String = imageCombo.selectedItem.data
	var clazz:Class = getDefinitionByName(className) as Class
	picture.bitmapData = new clazz(0,0)

	calcArea()
	countPixels()
}

A lényegi átalakításokat a calcArea függvény végzi ahol először is célszerű lekicsinyíteni a képet amin dolgozunk, mivel minél nagyobb, annál időigényesebb akármilyen manipuláció és a becsléshez egy 200*200 as BitmapData is bőven elegendő. A tresholdMap nevű BitmapData fogja a szűrt pixeleket feketén a többit fehéren tartalmazni ehhez elég egy megfelelő küszöb színt beállítani a threshold metódusnak. A mi esetünkben egy a fehérnél egy kicsivel sötétebb árnyalat (0xFFFEFEFE), amelyik szín ennél sötétebb azt feketére állítjuk.
A következő lépésnél a blurMap-ra másolt fekete-fehér képen végrehajtjuk az elmosást miután 9 helyről mintát veszünk amiket a piros pontok jelölnek. Ezeknek a pontoknak a színét átlagoljuk mivel a blur nem végez tökéletes elmosást. Szürkeárnyalatos kép lévén a piros, zöld, kék komponensek intenzitásai egyformák ezért elég csak egy komponenst figyelni. Az átlag 0-255 közé esik amit tekinthetünk a képen elhelyezkedő fekete pixelek arányának is így (+/-) 3%-os pontossággal meg tudjuk becsülni az elfoglalt területet.

A következő alkalmazás egy BitmapData maszkot használ ami egy kaparós sorsjegy felületet szimulál. Egy hasonló elven működő hirdetésnek vagy játéknak szüksége lehet gyorsan tudni hogy körülbelül hány százaléka lett már a takarásnak eltüntetve, erre is alkalmazható a fent leírt módszer. Másik megoldás lehetne a pixelek megszámolása ami pontosabb eredményt adna de nem tudnánk hogy honnan lett eltüntetve a maszk. A blur segítéségével és a mintavételezési pontok fontos helyekre rakásával a kívánt terület környékéről kapunk csak információkat.

var picture:Bitmap
var brush:BitmapData
var maskData:BitmapData
var blurMap:BitmapData
var maskBitmap:Bitmap
var checkTimer:Timer

init()

function init():void
{
	picture = new Bitmap(new BigPic(0,0))
	brush =  new CoinBrush(0,0)
	blurMap = new BitmapData(200, 200, false, 0xFFFFFF)
	sampleArea.sampleCont.addChild(new Bitmap(blurMap))

	checkTimer = new Timer(250)

	imageArea.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownArea)
	stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUpArea)
	checkTimer.addEventListener(TimerEvent.TIMER, onCheckTimer)

	setupPic()
}

function setupPic():void
{
	maskData = new BitmapData(picture.width, picture.height, true, 0)
	maskBitmap = new Bitmap(maskData)

	picture.cacheAsBitmap = true
	maskBitmap.cacheAsBitmap = true
	picture.mask = maskBitmap

	imageArea.addChild(picture)
	imageArea.addChild(maskBitmap)
}

function drawBrush(x:Number, y:Number):void
{
	maskData.draw(brush, new Matrix(1,0,0,1, x-brush.width/2, y-brush.height/2), null, "darken")
}

function checkPixels():void
{
	var s:uint = getTimer()
	blurMap.lock()
	blurMap.fillRect(blurMap.rect, 0xFFFFFF)
	var matr:Matrix = new Matrix()
	matr.scale(blurMap.width/maskData.width, blurMap.height/maskData.height)

	blurMap.draw(maskData, matr)

	blurMap.applyFilter(blurMap, blurMap.rect, new Point(), new BlurFilter(150,150,3))

	var sSize:uint = 50
	var pxValue:uint
	var sampleNum:uint
	for(var x:int=sSize; x<blurMap.width; x+=sSize){
		for(var y:int=sSize; y<blurMap.height; y+=sSize){
			pxValue += blurMap.getPixel(x, y) & 0x0000FF
			sampleNum++
		}
	}
	pxValue /= sampleNum
	blurMap.unlock()

	var time:uint = getTimer()-s
	var percent:Number = (pxValue/255)*100
	pixelValue.text = "szín:"+pxValue +" | "+percent.toFixed(1)+"%\n"
	pixelValue.appendText(time+" ms")
}

function moveCoin():void
{
	coinImage.x = stage.mouseX
	coinImage.y = stage.mouseY
}

function onCheckTimer(event:Event):void
{
	checkPixels()
}

function onMouseDownArea(event:MouseEvent):void
{
	drawBrush(imageArea.mouseX, imageArea.mouseY)
	checkPixels()
	moveCoin()
	checkTimer.start()

	Mouse.hide()
	coinImage.visible = true

	stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMoveArea)
}

function onMouseUpArea(event:Event):void
{
	stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMoveArea)
	checkTimer.stop()
	checkPixels()

	coinImage.visible = false
	Mouse.show()
}

function onMouseMoveArea(event:MouseEvent):void
{
	drawBrush(imageArea.mouseX, imageArea.mouseY)
	moveCoin()
}

Ebben a mintában már egy másik megközelítést használunk miszerint nem a képre hanem a kép maszkjára vagyunk kíváncsiak. A maszk is egy BitmapData amire minden egér mozdulatnál egy fekete törlő hatást keltő képet rajzolunk ami a maszkot fogja majd alkotni. Ennek a maszknak számoljuk az elfoglalt területét így meg tudjuk határozni mennyire van eltakarva a kép.

A források Adobe Flash CS3-ban készültek és innen letölthetőek:
PixelPercentCalc.zip