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




