我需要将两个BitmapData
个对象混合在一起。
我正在使用flash.*
库和AS3编译目标在Haxe上写作。
我已经调查了HxSL和PixelBender,似乎没有人有一个按位XOR运算符,也没有任何其他可用于创建XOR的按位运算符(但是我错过了一些明显的东西吗?我&#39 ; d接受任何答案,它只使用HxSL或PixelBlender中可用的整数/浮点运算符和函数来进行按位异或。)
我找不到Flash中的预定义过滤器或着色器似乎都无法对两个图像进行异或(但是,我是否遗漏了一些明显的东西?可以使用其他过滤器的组合来完成XOR。)< / p>
我找不到像XOR drawmode那样把东西画到其他东西上(但这并不意味着它不存在!如果它存在的话也会有效!)
目前我能找到的唯一方法是在图像上逐像素化循环,但即使在快速机器上,每个图像也需要几秒钟,而不是过滤器,我用于其他图像处理操作,快了大约一百倍。
是否有任何更快的方法?
答案 0 :(得分:3)
更多地使用它我发现在循环中删除条件和额外的Vector访问速度可以在我的机器上加速大约100ms。
这是之前的XOR循环:
// Original Vector XOR code:
for (var i: int = 0; i < len; i++) {
// XOR.
result[i] = vec1[i] ^ vec2[i];
if (ignoreAlpha) {
// Force alpha of FF so we can see the result.
result[i] |= 0xFF000000;
}
}
以下是Vector解决方案的更新XOR循环:
if (ignoreAlpha) {
// Force alpha of FF so we can see the result.
alphaMask = 0xFF000000;
}
// Fewer Vector accessors makes it quicker:
for (var i: int = 0; i < len; i++) {
// XOR.
result[i] = alphaMask | (vec1[i] ^ vec2[i]);
}
以下是我在Flash中对两个图像进行异或测试的解决方案。
我发现PixelBender解决方案比直接使用ActionScript要快6到10个 。
我不知道是不是因为我的算法很慢,或者只是在PixelBender中尝试伪造按位操作的极限。
<强>结果:强>
明显的赢家是使用BitmapData.getVector()
,然后对两个像素数据流进行异或。
这是我在PixelBender中实现按位XOR的方式,基于维基百科上给出的公式:http://en.wikipedia.org/wiki/Bitwise_operation#Mathematical_equivalents
以下是最终PBK的要点:https://gist.github.com/Coridyn/67a0ff75afaa0163f673
在我的机器上运行两个3200x1400图像的XOR时,这需要 6500-6700ms 。
我首先将公式转换为JavaScript以检查它是否正确:
// Do it for each RGBA channel.
// Each channel is assumed to be 8bits.
function XOR(x, y){
var result = 0;
var bitCount = 8; // log2(x) + 1
for (var n = 0; n < bitCount; n++) {
var pow2 = pow(2, n);
var x1 = mod(floor(x / pow2), 2);
var y1 = mod(floor(y / pow2), 2);
var z1 = mod(x1 + y1, 2);
result += pow2 * z1;
}
console.log('XOR(%s, %s) = %s', x, y, result);
console.log('%s ^ %s = %s', x, y, (x ^ y));
return result;
}
// Split out these functions so it's
// easier to convert to PixelBender.
function mod(x, y){
return x % y;
}
function pow(x, y){
return Math.pow(x, y);
}
function floor(x){
return Math.floor(x);
}
确认它是正确的:
// Test the manual XOR is correct.
XOR(255, 85); // 170
XOR(170, 85); // 255
XOR(170, 170); // 0
然后我通过使用一系列宏展开循环将JavaScript转换为PixelBender:
// Bitwise algorithm was adapted from the "mathematical equivalents" formula on Wikipedia:
// http://en.wikipedia.org/wiki/Bitwise_operation#Mathematical_equivalents
// Macro for 2^n (it needs to be done a lot).
#define POW2(n) pow(2.0, n)
// Slight optimisation for the zeroth case - 2^0 = 1 is redundant so remove it.
#define XOR_i_0(x, y) ( mod( mod(floor(x), 2.0) + mod(floor(y), 2.0), 2.0 ) )
// Calculations for a given "iteration".
#define XOR_i(x, y, i) ( POW2(i) * ( mod( mod(floor(x / POW2(i)), 2.0) + mod(floor(y / POW2(i)), 2.0), 2.0 ) ) )
// Flash doesn't support loops.
// Unroll the loop by defining macros that call the next macro in the sequence.
// Adapted from: http://www.simppa.fi/blog/category/pixelbender/
// http://www.simppa.fi/source/LoopMacros2.pbk
#define XOR_0(x, y) XOR_i_0(x, y)
#define XOR_1(x, y) XOR_i(x, y, 1.0) + XOR_0(x, y)
#define XOR_2(x, y) XOR_i(x, y, 2.0) + XOR_1(x, y)
#define XOR_3(x, y) XOR_i(x, y, 3.0) + XOR_2(x, y)
#define XOR_4(x, y) XOR_i(x, y, 4.0) + XOR_3(x, y)
#define XOR_5(x, y) XOR_i(x, y, 5.0) + XOR_4(x, y)
#define XOR_6(x, y) XOR_i(x, y, 6.0) + XOR_5(x, y)
#define XOR_7(x, y) XOR_i(x, y, 7.0) + XOR_6(x, y)
// Entry point for XOR function.
// This will calculate the XOR the current pixels.
#define XOR(x, y) XOR_7(x, y)
// PixelBender uses floats from 0.0 to 1.0 to represent 0 to 255
// but the bitwise operations above work on ints.
// These macros convert between float and int values.
#define FLOAT_TO_INT(x) float(x) * 255.0
#define INT_TO_FLOAT(x) float(x) / 255.0
evaluatePixel
函数中当前像素的每个通道的XOR:
void evaluatePixel()
{
// Acquire the pixel values from both images at the current location.
float4 frontPixel = sampleNearest(inputImage, outCoord());
float4 backPixel = sampleNearest(diffImage, outCoord());
// Set up the output variable - RGBA.
pixel4 result = pixel4(0.0, 0.0, 0.0, 1.0);
// XOR each channel.
result.r = INT_TO_FLOAT ( XOR(FLOAT_TO_INT(frontPixel.r), FLOAT_TO_INT(backPixel.r)) );
result.g = INT_TO_FLOAT ( XOR(FLOAT_TO_INT(frontPixel.g), FLOAT_TO_INT(backPixel.g)) );
result.b = INT_TO_FLOAT ( XOR(FLOAT_TO_INT(frontPixel.b), FLOAT_TO_INT(backPixel.b)) );
// Return the result for this pixel.
dst = result;
}
我发现最快的解决方案是从两个图像中提取Vector
像素并在ActionScript中执行XOR。
对于相同的两个3200x1400,这需要 480-500ms 。
package diff
{
import flash.display.Bitmap;
import flash.display.DisplayObject;
import flash.display.IBitmapDrawable;
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.utils.ByteArray;
/**
* @author Coridyn
*/
public class BitDiff
{
/**
* Perform a binary diff between two images.
*
* Return the result as a Vector of uints (as used by BitmapData).
*
* @param image1
* @param image2
* @param ignoreAlpha
* @return
*/
public static function diffImages(image1: DisplayObject,
image2: DisplayObject,
ignoreAlpha: Boolean = true): Vector.<uint> {
// For simplicity get the smallest common width and height of the two images
// to perform the XOR.
var w: Number = Math.min(image1.width, image2.width);
var h: Number = Math.min(image1.height, image2.height);
var rect: Rectangle = new Rectangle(0, 0, w, h);
var vec1: Vector.<uint> = BitDiff.getVector(image1, rect);
var vec2: Vector.<uint> = BitDiff.getVector(image2, rect);
var resultVec: Vector.<uint> = BitDiff.diffVectors(vec1, vec2, ignoreAlpha);
return resultVec;
}
/**
* Extract a portion of an image as a Vector of uints.
*
* @param drawable
* @param rect
* @return
*/
public static function getVector(drawable: DisplayObject, rect: Rectangle): Vector.<uint> {
var data: BitmapData = BitDiff.getBitmapData(drawable);
var vec: Vector.<uint> = data.getVector(rect);
data.dispose();
return vec;
}
/**
* Perform a binary diff between two streams of pixel data.
*
* If `ignoreAlpha` is false then will not normalise the
* alpha to make sure the pixels are opaque.
*
* @param vec1
* @param vec2
* @param ignoreAlpha
* @return
*/
public static function diffVectors(vec1: Vector.<uint>,
vec2: Vector.<uint>,
ignoreAlpha: Boolean): Vector.<uint> {
var larger: Vector.<uint> = vec1;
if (vec1.length < vec2.length) {
larger = vec2;
}
var len: Number = Math.min(vec1.length, vec2.length),
result: Vector.<uint> = new Vector.<uint>(len, true);
var alphaMask = 0;
if (ignoreAlpha) {
// Force alpha of FF so we can see the result.
alphaMask = 0xFF000000;
}
// Assume same length.
for (var i: int = 0; i < len; i++) {
// XOR.
result[i] = alphaMask | (vec1[i] ^ vec2[i]);
}
if (vec1.length != vec2.length) {
// Splice the remaining items.
result = result.concat(larger.slice(len));
}
return result;
}
}
}
您目前使用BitmapData.getPixel32()
循环使用BitmapData的方法提供了类似于 1200ms 的速度:
for (var y: int = 0; y < h; y++) {
for (var x: int = 0; x < w; x++) {
sourcePixel = bd1.getPixel32(x, y);
resultPixel = sourcePixel ^ bd2.getPixel(x, y);
result.setPixel32(x, y, resultPixel);
}
}
我最后的测试是尝试迭代两个ByteArray
像素数据(非常类似于上面的Vector
解决方案)。此实施还需要 1200ms :
/**
* Extract a portion of an image as a Vector of uints.
*
* @param drawable
* @param rect
* @return
*/
public static function getByteArray(drawable: DisplayObject, rect: Rectangle): ByteArray {
var data: BitmapData = BitDiff.getBitmapData(drawable);
var pixels: ByteArray = data.getPixels(rect);
data.dispose();
return pixels;
}
/**
* Perform a binary diff between two streams of pixel data.
*
* If `ignoreAlpha` is false then will not normalise the
* alpha to make sure the pixels are opaque.
*
* @param ba1
* @param ba2
* @param ignoreAlpha
* @return
*/
public static function diffByteArrays(ba1: ByteArray,
ba2: ByteArray,
ignoreAlpha: Boolean): ByteArray {
// Reset position to start of array.
ba1.position = 0;
ba2.position = 0;
var larger: ByteArray = ba1;
if (ba1.bytesAvailable < ba2.bytesAvailable) {
larger = ba2;
}
var len: Number = Math.min(ba1.length / 4, ba2.length / 4),
result: ByteArray = new ByteArray();
// Assume same length.
var resultPixel:uint;
for (var i: uint = 0; i < len; i++) {
// XOR.
resultPixel = ba1.readUnsignedInt() ^ ba2.readUnsignedInt();
if (ignoreAlpha) {
// Force alpha of FF so we can see the result.
resultPixel |= 0xFF000000;
}
result.writeUnsignedInt(resultPixel);
}
// Seek back to the start.
result.position = 0;
return result;
}
答案 1 :(得分:1)
根据您想要实现的目标,有一些可能的选项(例如,每个通道的XOR或者只是非黑色的任何像素?)。
有BitmapData.compare() method可以为您提供有关这两个位图的大量信息。在比较之前,您可以BitmapData.threshold()输入数据。
另一种选择是使用draw method和BlendMode.DIFFERENCE blend mode将两个图片绘制到同一个BitmapData
实例中。这将显示两个图像之间的差异(相当于Photoshop中的差异混合模式)。
如果您需要检查是否有任何像素是非黑色的,那么您可以先尝试运行BitmapData.threshold然后使用差异混合模式绘制结果,如上图所示。
您是在进行图像处理还是其他类似的像素点击检测?
首先,我要查看BitmapData并查看可以使用的内容。