BitmapData#compare ??!? page 2

44 posts

Flag Post

That is why Adobe should never have made this function public. They should have kept it private, and let developers write their own comparison code, and if they one day find that it is a bottleneck, add the more sensible versions instead.

This is more of a tongue in cheek joke – who cares about Adobe, let us use haxe, fellow haxe dev. At least that supports dynamic, and the compare’s return is (slightly) solved.

Edit: Just found out it does return dynamic for the function :)

 
Flag Post

Heh, I’m not worried about language bloat, who cares about libraries and functions that never gets used. It’s more that the flash runtime becomes more complex. Adobe has stopped updating Flash for Android, instead only focusing on AIR (and Flash for Linux has always sucked). One has to wonder if it is because the Flash player is a big fat bloated legacy application that is really hard to work with, and can never be trimmed down due to backwards compatibility reasons.

 
Flag Post

Sad Next died :(

 
Flag Post

OK, this is such a magnificent can of WTF I have to share with the class. So I went and tried to play the compare() game with the Google Chrome logos, the color one and the blue one.

So here are the results of comparing color to blue, and then blue to color, and they are hilarious:

Top left is the original color logo, top right is the original blue logo, bottom left is color.compare(blue) and bottom right is blue.compare(color). At first glance, you probably noticed the extra structure in the compare result that weren’t in the originals. The purple/blue or green/cream mess that corresponds to the green area of the original color logo. It also happens in the bottom of the center (originally) blue dot. What happened there? Who the heck knows!

Did you notice how the black area in the center of the first compare result ‘bleeds’ into the surrounding circle? That’s some funny shit there, isn’t it, especially since it doesn’t occur in the second compare. What could have caused it? Well, what causes anything? Let’s just say a wizard gradient! did it, it’s likely to make as much sense as the behavior of compare().

Also, notice how the center dot in both the color and blue logos is basically blue. Is it the exact same blue? Possibly, probably not, but holy crap comparing one to the other compare() says “black!” while doing the reverse comparison it screams “white!” Does it make any sense? Probably as much as the rest of that function’s behaviors.

And that’s pretty much where I’m going to leave it. The image speaks way louder than I ever could, and it illustrates perfectly what a gigantic piece of crap compare() is. I feel dirty just having used it.

There is none blinder than he who does not wish to see.

Edit: Modified to include Secretmapper’s explanation.

 
Flag Post

Did you notice how the black area in the center of the first compare result ‘bleeds’ into the surrounding circle? That’s some funny shit there, isn’t it, especially since it doesn’t occur in the second compare. What could have caused it? Well, what causes anything? Let’s just say a wizard did it, it’s likely to make as much sense as the behavior of compare().

And that’s pretty much where I’m going to leave it. The image speaks way louder than I ever could, and it illustrates perfectly what gigantic piece of crap compare() is. I feel dirty just having used it.

I commend your eyes for being sharper than a computer’s. You seriously are not trusting what you see over what you get right?

Of course there will we weird things in there, gradients are present! You’re test case is very biased if you’re going to point out “some funny shit there”, just because your eyes can’t see it. To me, this is all expected output.

Do you have any experience with cryptography? There can be some things invisible to the human eye but mean very differently to a computer.

comparing one to the other compare() says “black!” while doing the reverse comparison it screams “white!”

That is VERY logical, don’t you think?

And that’s pretty much where I’m going to leave it. The image speaks way louder than I ever could, and it illustrates perfectly what gigantic piece of crap compare() is. I feel dirty just having used it.

You seemed to have already reached a conclusion here. As I’ve said, if you are too closed-minded to think of any creative use of these, be my guest.

I will try to post my use of compare. Personally, this has taken quite a chunk of my time, so I’m not sure if I could/would, But still, a scholarly discussion is always good every now and then, and I will try to post my results.

 
Flag Post
Originally posted by Secretmapper:

comparing one to the other compare() says “black!” while doing the reverse comparison it screams “white!”

That is VERY logical, don’t you think?

Wow, you… still don’t get it. If you’re basing your similarity assessment of the center dot on counting white pixels (or black pixels, for that matter) one compare tells you the two are identical, the other compare tells you the two are different.

compare() says A = B AND B != A. And it doesn’t bother you. You see nothing wrong with this conclusion. It doesn’t get plainer than this: compare() is an unreliable piece of crap. Its conception is garbage, its design is garbage, its implementation is garbage, and your boneheaded defense of it is garbage.

Either you’re trolling or you’re being willfully stupid. Either way, have fun.

Edit: \/ How can you say I ignored the first part of your post? I even modified my analysis of the compare() output in my previous post to include your explanation!

 
Flag Post

Wow, you… still don’t get it. If you’re basing your similarity assessment of the center dot on counting white pixels (or black pixels, for that matter) one compare tells you the two are identical, the other compare tells you the two are different.

Wow, you ignored the first parts of my posts again.

Look at the image again, or better yet, xxd it.

My bonehead defense is not garbage. Your inability to read- and think, about my posts make you think it is.

 
Flag Post

As I’ve said xxd your image.

PROTIP: It’s not white.

 
Flag Post

This function is like a train wreck that keeps looping on itself (and so is this thread). I just can’t take my eyes off of it!

In this installment of “Why did Adobe let a circus Poodle on shrooms code the Flash player” let’s have fun dissecting the bitmapData.compare() function. You might think I’ve destroyed it thoroughly already, but boy are you in for a treat!

So what did I do this time to that piece of crap function, you ask? I went ahead and reproduced its supposed behavior, as detailed in the Adobe live docs that Bob posted in the OP, and compared that to the actual output of the function. Behold!

So what are you looking at? On the left side, you can see the output of color.compare(blue) using the same color and blue bitmaps as previously posted (top and bottom). On the right side, you see what the output of color.compare(blue) would be if the compare() function was coded according to specs (top) and what it would output if whoever coded it was a lazy ass high school dropout who cared more about his next joint than about coding a piece of software up to a minimal standard of quality worthy of (at least) a college dropout (bottom). In other words, the bottom right picture is my first guess of what could have gone wrong if someone cut corners and ran into a wall. Notice the similarities…

Specifically, here is the code that generated the top-right picture

for (var i:int = 0; i < size; ++i) for (j = 0; j < size; ++j)
{
	pix1 = bmd1.getPixel32(i, j);
	pix2 = bmd2.getPixel32(i, j);
	if (pix1 == pix2) transit2.setPixel32(i, j, 0);
	else if (pix1 % 0x1000000 == pix2 % 0x1000000)
	{
		a = (pix1 % 0x1000000) - (pix2 % 0x1000000);
		if (a < 0) a = 0;
		transit2.setPixel32(i, j, a * 0x1000000 + 0xFFFFFF);
	}
	else
	{
		b = (pix1 % 0x100) - (pix2 % 0x100);
		if (b < 0) b = 0;
		g = ((pix1 / 0x100) % 0x100) - ((pix2 / 0x100) % 0x100);
		if (g < 0) g = 0;
		r = ((pix1 / 0x10000) % 0x100) - ((pix2 / 0x10000) % 0x100);
		if (r < 0) r = 0;
		transit2.setPixel32(i, j, 0xFF000000 + r * 0x10000 + g * 0x100 + b);
	}
}
pt.x = size;
results.bitmapData.copyPixels(transit2, transit2.rect, pt);

and the bottom right picture:

for (var i:int = 0; i < size; ++i) for (j = 0; j < size; ++j)
{
	pix1 = bmd1.getPixel32(i, j);
	pix2 = bmd2.getPixel32(i, j);
	if (pix1 == pix2) transit2.setPixel32(i, j, 0);
	else if (pix1 % 0x1000000 == pix2 % 0x1000000)
	{
		a = (pix1 % 0x1000000) - (pix2 % 0x1000000);
		if (a < 0) a = 0;
		transit2.setPixel32(i, j, a * 0x1000000 + 0xFFFFFF);
	}
	else
	{
		transit2.setPixel32(i, j, 0xFF000000 + (pix1 % 0x1000000 - pix2 % 0x1000000));
	}
}
pt.x = size;
results.bitmapData.copyPixels(transit2, transit2.rect, pt);

The difference is in what is done with the pixel when the two RGB values are different. Proper implementation does a component difference channel by channel, taking care to maintain each component at least at 0. Improper implementation does a simple difference between the two RGB values, caring fuck-all about how horribly incorrect that is. Note that by “proper” I mean “In accordance with the documentation”, and that the term “proper” should not be construed in any way, shape or form as an endorsement of the design decisions that went into this function.

Of course, I cannot examine the inner workings of native functions. Therefore I cannot guarantee that what is coded internally is exactly what I have implemented on the “improper” side. But it doesn’t take a genius to realize that the outputs are too similar for it to be a simple coincidence.

So, tl;dr: compare() is not just a piece of garbage function that was designed by a drunk monkey. It’s a piece of garbage function that was designed and coded by a drunk monkey. And I have proof!

@Secretmapper: By the way to answer your earlier question, no, I do not believe computers. Computers execute. They do not think, they do not judge, they do not use common sense. Try this and tell me if you believe the output.

var i:int = 2147483648;
trace(i, "+ 1 =", (i + 1));
 
Flag Post

Really interesting discussion so far. Ace, does the documentation ever say how the component subtraction happens? You assume that if the result is less than 0, we should simply set it to 0, which actually might give us less information in our result image. So I think a lot of the differences in results may just come from how you handle subtraction.

Got to run, so my code might have bugs in it, but I’ll post it here for the heck of it:

			for (var i:int = 0; i < 10; i++) 
			{
				var c1:uint = Math.random() * 0xffffffff;
				var c2:uint = Math.random() * 0xffffffff;
				trace("\n\nColor 1: 0x" + c1.toString(16) + "   Color 2: 0x" + c2.toString(16));
				var bmd1:BitmapData = new BitmapData(50, 50, true, c1);
				var bmd2:BitmapData = new BitmapData(50, 50, true, c2);
				var diffBmpData:BitmapData = bmd1.compare(bmd2) as BitmapData;
				var r:uint = (((c1 >> 16) & 0xff) - ((c2 >> 16) & 0xff)) & 0xff;
				var g:uint = (((c1 >> 8) & 0xff) - ((c2 >> 8) & 0xff)) & 0xff;
				var b:uint = ((c1 & 0xff) -(c2 & 0xff)) & 0xff;
				trace ("Compare result: 0x" + diffBmpData.getPixel(0, 0).toString(16));
				trace("Whole color subtraction: 0x" + uint(((c1 & 0xffffff) - (c2 & 0xffffff))&0xffffff).toString(16));
				trace("Component subtraction: 0x" + uint((r << 16) | (g << 8) | b).toString(16));
			}

Color 1: 0x24c43b65   Color 2: 0xa9887da7
Compare result: 0x3ebcbc
Whole color subtraction: 0x3bbdbe
Component subtraction: 0x3cbebe


Color 1: 0x9bc2cf9b   Color 2: 0xc48124af
Compare result: 0x41abeb
Whole color subtraction: 0x41aaec
Component subtraction: 0x41abec


Color 1: 0x2d359a49   Color 2: 0x4a0a6007
Compare result: 0x293943
Whole color subtraction: 0x2b3a42
Component subtraction: 0x2b3a42


Color 1: 0x22ba09b   Color 2: 0x8abd0169
Compare result: 0x447d16
Whole color subtraction: 0x6e9f32
Component subtraction: 0x6e9f32


Color 1: 0x5488b85   Color 2: 0xcc7fb44d
Compare result: 0xb4e54c
Whole color subtraction: 0xc8d738
Component subtraction: 0xc9d738


Color 1: 0x3e1928f9   Color 2: 0x57a23f05
Compare result: 0x78ebf5
Whole color subtraction: 0x76e9f4
Component subtraction: 0x77e9f4


Color 1: 0xe1ff409   Color 2: 0x9059f025
Compare result: 0xccfcdb
Whole color subtraction: 0xc603e4
Component subtraction: 0xc604e4


Color 1: 0x8f2e0f05   Color 2: 0x9373bb1b
Compare result: 0xbc53e9
Whole color subtraction: 0xba53ea
Component subtraction: 0xbb54ea


Color 1: 0xdc113a5   Color 2: 0x24920021
Compare result: 0x2f147a
Whole color subtraction: 0x2f1384
Component subtraction: 0x2f1384


Color 1: 0x15f1386b   Color 2: 0x81dfd16d
Compare result: 0x146b00
Whole color subtraction: 0x1166fe
Component subtraction: 0x1267fe
 
Flag Post

I have to check all that, and probably run my own tests to check exactly what color compare() outputs, but here’s one problem: carries.

Lets say RGB1 is 0×800000 and RGB2 is 0×000001. If you do a simple color difference you get 0×7FFFFF, so even though neither pixel has a green component, the result has a green component of 0xFF. That’s bad. And it will happen if you do whole color differences.

So rule #1: Full RGB difference produces garbage. I’ll be back later.

 
Flag Post
Originally posted by Ace_Blue:

I have to check all that, and probably run my own tests to check exactly what color compare() outputs, but here’s one problem: carries.

Lets say RGB1 is 0×800000 and RGB2 is 0×000001. If you do a simple color difference you get 0×7FFFFF, so even though neither pixel has a green component, the result has a green component of 0xFF. That’s bad. And it will happen if you do whole color differences.

So rule #1: Full RGB difference produces garbage. I’ll be back later.

Ran my code with the inputs you suggested and got this:

Color 1: 0xff800000   Color 2: 0xff000001
Compare result: 0x8000ff
Whole color subtraction: 0x7fffff
Component subtraction: 0x8000ff

So that looks like compare is correct. I’m still not sure if what I wrote is 100% correct/logical though, so feel free to rip it apart.

 
Flag Post

I can’t find any error in your code. So this would seem to indicate that compare does subtract by component, which is good, and that it does not floor the differences with 0, which is bad. In this case, one pixel has a blue component of 1, the other a blue component of 0, and the compare result a component of (what else?) 255! Woohoo!

it does also explain the artifacts in my test pictures. Those lines are where the difference suddenly jumps from 0 to 255, turning blue into power pink, or black into red. I’m still puzzled though because if compare() is set simply in this way then a.compare(b) and b.compare(a) should be color negatives of each other, but my test cases aren’t quite there. The black in the center bleeding into the blue ring should be matched on the other picture by white bleeding into the yellow. It’s not there. There must be something else going on as well.

Edit: On any pixel where at most two of the RGB components are exactly the same the difference for those components will be 0 either way, so a.compare(b) and b.compare(a) are not quite color negatives. OK, so that makes sense as well. unbounded channel differences is probably what compare() is doing. Which means it’s not even suitable as a difference filter, and I’m back to the same question: What in the world could anyone ever need this piece of crap for?

 
Flag Post
Hmm?:

Oh My God! A wizard did it!

Note how similar the [0,0] , [0,1], [1,1] images are, yet produces wildly different ([0,2], [1,2],[2,0],[2,1],[2,2])results. The reason you have a "purple/blue or green/cream mess" there is because it *should* have a mess there.

compare() says A = B AND B != A

As I've said, you had ignored my initial post. It does not say that, it says A!=B AND B!=A. The thing is, the center dot is not literally white, it is only visually white! I actually used the exact same colors that was used in chrome (The blue center) for [1,0] and you can see it in [2,0] and [2,1]. Look at how misleading "it" is in [2,0], because it is surrounded by close color values.

The black in the center bleeding into the blue ring should be matched on the other picture by white bleeding into the yellow.

It does bleed, xxd your images, don't just assume.

back to the same question: What in the world could anyone ever need this piece of crap for?

Choo choo train!*

http://stackoverflow.com/questions/12228929/compare-two-bitmaps-in-actionscript-3/12230200#12230200

http://stackoverflow.com/questions/12778639/as3-comparing-bitmapdatas-fastest-method/12779253#12779253

http://stackoverflow.com/questions/2544330/comparing-bitmap-data-in-as3-pixel-for-pixel/2544374#2544374

http://stackoverflow.com/questions/13401773/compare-two-images-and-check-equality/13402911#13402911

http://stackoverflow.com/questions/7319701/as3-possible-to-check-if-mask-is-completely-filled/7326579#7326579

http://stackoverflow.com/questions/7577262/detecting-a-movieclip-has-been-flipped-horizontally-on-the-stage-in-as3/7579176#7579176

http://stackoverflow.com/questions/14942816/comparing-bitmap-is-not-working/14945716#14945716

http://stackoverflow.com/questions/7282484/as3-how-to-check-if-bitmapdata-is-empty/7287700

http://stackoverflow.com/questions/1890985/where-can-i-find-help-with-flash-based-augmented-reality-detecting-hand-position/1893889#1893889

http://stackoverflow.com/questions/5427691/comparing-two-bitmaps-against-each-other-for-match-as3/5429611#5429611

*That's just StackOverflow

 
Flag Post

This thread moved a lot while you were gone buddy, we’ve been over all that already. The artifacting is due to compare() making unbounded differences (so that 0 – 1 = 255, for instance), making the bitmapData it outputs literally worse than useless (because they can’t be used for anything and their production uses CPU time and obstructs memory.)

I’ll check all your links one by one for one that actually uses compare() in such a way that it outputs a bitmapData and that bitmapData is used in a sensible manner. Checking bitmap equality, for instance, doesn’t qualify since the user is only interested in the function returning 0 for a match. The proper function for that would return a Boolean: true for identical bitmapDatas, false in every other case. Not 0 for identical and a garbage object of undermined type for various other cases. Not to mention that for non-identical bitmaps compare() still goes over every single pixel while a proper equality detection should stop at the first difference.

Edit(s):

Link 1: Poster only interested in counting identical pixels. Does not use different pixels in any way. Invalid.

Link 2: Poster extracts identical pixels from an image using compare() and specifically discards all non-identical pixels. Getting closer, not quite there (discarding does not qualify as using).

Link 3: Poster wants to create some sort of mask from a webcam view, and one of the replies mentions using compare() in combination with threshold to detect zones of similarity. It’s a hit! And here’s the verdict: “Using the compare() method returns some very weird results (lots of different colors), then the threshold does remove parts of the bitmap but seems to be quite erratic.” Well, that’s unbounded channel differences for you, compare() is crap at this kind of job because of it. Oh well. Better luck next time.

Link 4: Oh, it’s that one again. I’ve devoted several posts to it already. In the optics of this series let’s just say the OP wants to detect if bitmaps are identical, and the reply is about counting matching pixels. It does nothing at all with unmatched pixels and therefore does not qualify. Next!

Link 5: compare() is only recommended for checking equality, and with a fully transparent bitmap at that. It’s a dud.

Link 6: This one is hilarious. compare() is offered after the original problem has been solved in a quick and reliable manner as a slower alternative that would fail miserably on some images (specifically the ones that are left-right symmetrical.) Worse than useless.

Link 7: OP did not read the docs and is confused that compare() returns a bitmapData object in some cases (OP was expecting 0.) Another illustration of why returning an object of unpredictable type is a horrible idea, but that’s not what we’re after. And since the OP only wants to check bitmap equality we’re done here.

Link 8: compare() as a way to detect fully transparent Bitmaps. This is getting old. Secretmapper, did you just do a search for ‘BitmapData.compare’ on the stackoverflow forums and posted every link that came up without checking any of them for relevance?

Link 9: First mention of compare() is by the OP in the next to last comment, asking if it would be useful for his purpose (it would not). The last comment is again the OP instructing readers to disregard previous comments. I’m happy to oblige.

Link 10: OP is only interested in perfect equality between bitmapData objects. Since that was the last one, this entire link crop is but a series of wide misses, with one narrow one.

Breaking news: compare() still crap function, details at 11.

 
Flag Post

It does seem very useless if the colors are unbounded. Since you can’t tell if one of the color channels has had an “overflow” or if it was simply very high from the start, I have a hard time imagining any sort of use for this function.

 
Flag Post

Secretmapper seems to listen to you a lot more than he listens to me, maybe you can explain that to him.

 
Flag Post

Okay. I get it. I think we are reaching a conclusion now.

I’m going to admit, those links were just googled. I had a hectic day yesterday and I couldn’t check all of them one by one.

But remember how some of those links do feel kind of close? Guess what’s the function the libraries people link them use.

No, it is not BitmapData.compare() It’s an almost identical function with a slight (but quite important) difference: BlendMode.DIFFERENCE

Compares the constituent colors of the display object with the colors of its background, and subtracts the darker of the values of the two constituent colors from the lighter value.

I think this is what you have wanted the design of compare all along to be, is that not right Ace? Now, all you’re going to get is a positive value.

And this is what I had been partially talking about too.

Look at this image. If you move this one pixel to the right, and one pixel down, and use the difference blendmode, you get this:

This is the gist, of the “tracing” I have talked about in the first page. Note how incredibly easy it is.

But that doesn’t even use Compare! And it’s not even an algorithm!

Compare however, can be used for it, along with the use of histogram and threshold. Actually, I would guess the guy above in the link that complained about erratic colors was incapable of smartly using those two things.

The existence of difference in the first place, however makes the use of compare for this kind of things moot. Compare though, can be used for it- as I’ve said, with the combination of other functions, so your argument that is was all useless was not necessarily true.

Here is it. I’ll try my best on this, but if you find any flaws in it, feel free to rip it apart.

Compare()

  • Fast: It is the fastest way to compare two images, except in the worst-case scenario in which your images are vastly different*. But since you are comparing images, we can assume that the images your input does not have incredible variance between them, making it the best choice for these kinds of things.
  • Portable: Potentially speaking you can have one line of code to compare your images. Every other solution requires more code, and really, the next best candidate (difference) is a native function too.
  • The Return Value: Now this is where you might argue with me. However, I stand by my idea that it is useful, though admittedly, the existence of difference had limited it’s use.
    Difference Positioning – If I wanted to know what is the position of the difference in my image, I could have easily done it so by getColorBoundsRect. It is the most elegant solution for it.*
    Threshold Filter – The lack of bounds
    ** can be used if you want to have a threshold filter looking for color values at a specific color range, especially if you have a very intricate pattern. In that case, what you do is ‘bake’ a specific image and compare with your tests. I think that is actually quite a good use of it, and can be expanded for other things where you have an idea of the image used (sprites in a game, image processing)

*In which, mind you, difference is faster, but such a blendmode also produces a by-product that “uses CPU time and obstructs memory”

* * I do not use the colors it throws back to me. So what? It’s still the fastest solution, and the colors could have been quite handy if I wanted to be specific and find only a specific color range.
***If you do not want to let your colors go unbounded, use Difference, but I see the lack of bounding in compare() as a necessary design decision because of the fact that bounding it could turn your colors completely white, signifying a match, and that produces even more problems! I also actually think that one of the real reason there is a lack of bounds there is – speed, I’m guessing that it uses bitwise operation, so logically, overflows are abound.

Okay, that’s it. I do think that my post is good enough, but I’m pretty sure I have not yet exhausted the use for this mildly misunderstood function. As I’ve said, feel free to find any holes in it, so we can discuss if it is actually valid.

 
Flag Post

That’s a pretty nifty tracing trick, and I did not know it. Sadly, compare() cannot be used for tracing outlines in this way, because the differences are unbounded. Here’s a comparison on the ‘color’ Chrome logo from above.

Left is color.compare(color shifted by 2 pixels in +x and +y), right is a difference filter on the same setup in Paint.NET. A difference filter is the perfect tool for the job, all you have to do is apply a threshold filter on top to get your outlines cut out, and a colorTransform to turn them all pure white (or black, or whatever other color you want them to be). with compare() though, the result is such an absolute mess that if you can turn it into a clean outline the amount of processing you’ll have done by then will likely have outweighed any speed gain from using a native function.

This result was eminently predictable, but it’s quite dramatic when you see it firsthand. Look, if you can find a use for the ‘color puke’ that compare creates out of non-identical pixels, more power to you, but I do not believe there is one. You have argued at length that there must be one, but you still have not produced a single practical example. We’re not even at your starting “lots of them” anymore, a single one would do. Just one.