[AS3]位图的色阶控制-- 伽玛校正(Gamma Correction)

2008-11-02 15:49:54

位图调节色阶时,在内部使用的是曲线校正中的伽玛校正。 伽玛校正为调整亮度的方法,它只有一个参数即γ(Gamma)。伽玛校正的变换公式为: [img]attachments/month_0811/0200811215476.gif[/img] x&#39;为变换后的亮度。xmax为x的最大值。 γ = 1 x&#39; = x (没有变化) γ = 0.5 x&#39; = x2(变暗) γ = 2 x&#39; = √x(变亮) 调整色阶的三个滑条的意思 Plotoshop的调整色阶里有黑,灰,白三个滑条,黑为阴影,白为高亮。从0到黑均为黑色;从白到255均为白色;从黑到白为以灰为中值的伽玛校正。(xb为黑,xg为灰,xw为白) [img]attachments/month_0811/22008112154745.gif[/img] Actionscript 3.0的实现 实现曲线、伽玛校正等可以使用BitmapData.paletteMap。 三个滑条的值分别为xb、xg、xw (0≤xb≤xg≤xw≤255)。Xmax = xb - xw。当x = xg - xb时,因为想x&#39; / xmax = 0.5,所以 [img]attachments/month_0811/u2008112154811.gif[/img] 基本实现代码: [code] var gamma:Number = Math.log((xg - xb) / (xw - xb)) / Math.log(0.5); var mapR:Array = [], mapG:Array = [], mapB:Array = []; for(var i:int = 0; i < 256; i++) { mapB[i] = i < xb ? 0 : i > xw ? 0xff : 255 * Math.pow((i - xb) / (xw - xb), 1 / gamma); mapG[i] = mapB[i] << 8; mapR[i] = mapB[i] << 16; } image.paletteMap(bmdOrigin, bmd.rect, new Point(), mapR, mapG, mapB);[/code] 完整的实现代码: [code] package bitmap { import flash.display.*; import flash.filters.*; import flash.geom.*; import flash.events.Event; [SWF(width=&#34;256&#34;, height=&#34;410&#34;)] public class Histogram3 extends Sprite { [Embed(source=&#34;023.jpg&#34;)] private var SampleImage:Class; private var dragging:Sprite; private var h2pos:Number = 0.5; private var h1:Sprite; private var h2:Sprite; private var h3:Sprite; public function Histogram3() { stage.scaleMode = &#34;noScale&#34;; var bmd:BitmapData = Bitmap(addChild(new SampleImage())).bitmapData; var s:Sprite = new Sprite(); addChild(s).y = bmd.height + 10; cr&#101;ateHistogram(bmd, s); s = new Sprite(); addChild(s).y = bmd.height + 140; cr&#101;ateHistogram(bmd, s); addChild(cr&#101;ateSlider()).y = bmd.height + 115; var bmdOrigin:BitmapData = bmd.clone(); addEventListener( Event.ENTER_FRAME, function(e:*):void { if(dragging) { var gamma:Number = Math.log((h2.x - h1.x) / (h3.x - h1.x)) / Math.log(0.5); var mapR:Array = [], mapG:Array = [], mapB:Array = []; for(var i:int = 0; i < 0x100; i++) { mapB[i] = i < h1.x ? 0 : i > h3.x ? 0xff : 255 * Math.pow((i - h1.x) / (h3.x - h1.x), 1 / gamma); mapG[i] = mapB[i] << 8; mapR[i] = mapB[i] << 16; } bmd.paletteMap(bmdOrigin, bmd.rect, new Point(), mapR, mapG, mapB); s.graphics.clear(); cr&#101;ateHistogram(bmd, s); } } ); } // 生成色阶 private function cr&#101;ateHistogram(bmd:BitmapData, s:Sprite):void { // 灰度化 var cmf:ColorMatrixFilter = new ColorMatrixFilter( [1 / 3, 1 / 3, 1 / 3, 0, 0, 1 / 3, 1 / 3, 1 / 3, 0, 0, 1 / 3, 1 / 3, 1 / 3, 0, 0] ); var bmd2:BitmapData = bmd.clone(); bmd2.applyFilter(bmd2, bmd2.rect, new Point(), cmf); // 用threshold来得到颜色分布 var values:Array = []; for(var i:int = 0; i < 0x100; i++) { values[i] = bmd2.threshold(bmd2, bmd2.rect, new Point(), &#34;==&#34;, i + (i << 8) + (i << 16), 0, 0xffffff, false); } bmd2.dispose(); // 画色阶 var max:int = bmd.width * bmd.height / 50; s.graphics.lineStyle(1); for(i = 0; i < 0x100; i++) { s.graphics.moveTo(i, 100); s.graphics.lineTo(i, Math.max(0, 100 - values[i] / max * 100)); } } // 生成滑条 private function cr&#101;ateSlider():Sprite { // 画滑条可移动的范围 var slider:Sprite = new Sprite(); slider.graphics.beginFill(0xffffff); slider.graphics.drawRect(0, 0, 256, 10); slider.graphics.endFill(); slider.graphics.lineStyle(1, 0); slider.graphics.lineTo(255, 0); slider.buttonMode = true; slider.useHandCursor = true; // 画滑条 h1 = Sprite(slider.addChild(cr&#101;ateButton(0x000000))); h1.x = 0; h2 = Sprite(slider.addChild(cr&#101;ateButton(0x999999))); h2.x = 128; h3 = Sprite(slider.addChild(cr&#101;ateButton(0xffffff))); h3.x = 255; // mouseDown slider.addEventListener(&#34;mouseDown&#34;, function(e:*):void { var localX:Number = slider.globalToLocal(new Point(mouseX, mouseY)).x; // 滑动滑条 var d1:Number = Math.abs(localX - h1.x); var d2:Number = Math.abs(localX - h2.x); var d3:Number = Math.abs(localX - h3.x); var max:Number = Math.min(d1, d2, d3); dragging = (max == d1 ? h1 : max == d2 ? h2 : h3); // 验证滑条是否超出范围 var bounds:Rectangle = getDraggableBounds(dragging); dragging.x = Math.max(Math.min(localX, bounds.right), bounds.x); up&#100;ateH2(null); dragging.startDrag(false, bounds); }); // mouseMove stage.addEventListener(&#34;mouseMove&#34;, up&#100;ateH2); // mouseUp stage.addEventListener(&#34;mouseUp&#34;, function(e:*):void { if(dragging) { dragging.stopDrag(); dragging = null; } }); return slider; } // 生成滑条 private function cr&#101;ateButton(color:int):Sprite { var s:Sprite = new Sprite(); s.graphics.lineStyle(1, 0); s.graphics.beginFill(color); s.graphics.lineTo(5, 8.6); s.graphics.lineTo(-5, 8.6); s.graphics.endFill(); return s; } // 计算滑条的移动范围 private function getDraggableBounds(s:Sprite):Rectangle { if(s == h1) return new Rectangle(0, 0, h3.x - 4, 0); if(s == h2) return new Rectangle(h1.x + 2, 0, h3.x - h1.x - 4, 0); if(s == h3) return new Rectangle(h1.x + 4, 0, 255 - h1.x - 4, 0); return null; } // 计算灰滑条的移动 private function up&#100;ateH2(e:*):void { if(dragging &amp;&amp; dragging != h2) { h2.x = (h3.x - h1.x) * h2pos + h1.x; h2.x = Math.max(Math.min(h2.x, h3.x - 2), h1.x + 2); } else if(dragging == h2){ h2pos = (h2.x - h1.x) / (h3.x - h1.x); } } } } [/code] 示例文件: [swf]attachments/month_0811/x2008112154940.swf[/swf]