hybrid binarizer without holes

2,761 views
Skip to first unread message

Steven Parkes

unread,
Oct 9, 2011, 1:05:45 PM10/9/11
to zx...@googlegroups.com
I've been thinking about ways to reduce hybrid binarizer's (HB) penchant for punching holes in large black areas and was wondering if anybody had feedback.

HB estimates black level on a block-level basis in order to handle shadows, etc. This works very well for blocks that have a fair amount of dynamic range in them. In blocks that don't (i.e., less than 10% of max for a byte), it presumes the area is white. (There's a "hack" (I think it's mine?) such that if the block is entirely 0 == black, it will in that one case, know that it's black. That makes some synthetic codes work that otherwise would fail.))

This works vey well in most cases. One condition that it fails fairly spectacularly for is qr codes (which have large black areas (the finder patterns)) in cases where the sampling rate (i.e., image resolution) is much greater than the feature/module size.

In that case, while estimating the black point of a block that falls entirely inside a finder, the low-contrast path is tickled and the area is assumed to be white. This punches a whole in the finder in the binarized result and the qr code detector fails.

The motivation for this for me is decoding images taken with a phone camera. Those are much higher resolution than the images I decode from the live preview and thus get holed. It's possible to decimate the pictures and decode that and that often works. But it limits the images that will decode since a lot of information is thrown away. Its more or less a random guess at what the feature-size to resolution ratio is, which can be anything. It works if people are taking pictures as they would from the live video feed, but since the resolution of the camera is greater, I'd like to be able to allow a larger variety of capture conditions.

It's admittdely not the highest priority for me, but it is an interesting puzzle that I wanted to see if I could come up with a simple (elegant?) solution for.

So I've been thinking about a way to tweak the algorithm. The overall idea is that in addition to the current block's dynamic range, we can look at more global information from the image to weaken the assumption that low dynamic range means white. This should be a very local change … it shouldn't sacrifice the accuracy of HB in other ways. And it won't cover every case, but it'd great if it could cover very unambiguous cases.

So I tweaked things to record not only the block mins and maxes, but the global mins and maxes for the image. Then, in the low contrast blocks, I look at how close the block min is to the global min and block max is to the global max. In cases where the block min is much closer to the global min (black_diff) than the block max is to the global max (white_diff), I use the max+1 as the black point, otherwise I use the current estimate, which is twice the minimum.

I ran through the blackbox tests with this. If I use the degenerate case of black_diff < white_diff, it works poorly for a lot of images because it misclassifies things until the "real" dynamic range of the image is known since it's only using the dynamic range of the portion of the image already processed, which will often be low for images with a simple white-ishbackground.

So I tried using multiples, e.g., 8*black_diff < white_diff. This makes the algorithm more conservative in the sense of preferring white false positives over black. I tried a number of multiplication factors. In my test set, once the factor goes above around 12, the holes come back. If the ratio is too low, more "black"ish artifacts (like the thumb in one of the tests) get blacked and things fail.

But that provides a pretty wide, relatively insensitive range. I'm using 8 for now.

There's also the issue of what the black point estimate should be. If it's less than the max, holes appear which cause failures. I don't want to assume the local maximum is the global maximum (to handle shadows) so I don't want to use it. Setting at it between max+1 and 2*max seems to work okay so I'm erring on the conservative side with max+1.

This causes only minor changes in the blackbox tests. I think it adds 8 but loses 12. The weird thing is that in the new decode fails (a couple of data matrix tests), the binarized images are virtually indistinguishable visually. I still want to look into exactly whats causing those no-decodes.

So I just thought I'd see what people think. It feels like a cheap and fairly clean improvement ...

Sean Owen

unread,
Oct 9, 2011, 1:31:21 PM10/9/11
to zx...@googlegroups.com
I think the overall answer is that this approach, indeed, only works on relatively "low resolution" images, where holes don't appear. It doesn't work otherwise. But, most camera preview images are easily sufficiently low resolution for this to be a great approach.

One answer is to not use this on higher resolution images. If you've identified even a small improvement, that helps high-res cases while not really spoiling low-res cases, that's a great change. The logic makes sense to me. It sounds like you're seeing more failures than successes with this sort of change though, because the test images are intended to represent low-res camera preview images.

I think it's valid and useful to include this as some kind of alternate mode, or even alternate implementation; until it makes a lot more of the 'real' images better, probably best not to make it a default.

Steven Parkes

unread,
Oct 9, 2011, 1:59:36 PM10/9/11
to zx...@googlegroups.com
Yeah, I want to track down the additional failures. I haven't had a chance yet. When I do, I'll follow up.

It'd be easy enough to make it an option, as you mention, and simple to turn on and off knowing a priori the size of the image being decoded.

But I still like the puzzle of why it is failing some images. It's not obvious to me why it should, so I want to explore/understand those first.

I posted to do a sanity check on the approach, see if there was some egregious flaw I was missing. Thanks for the comments. If anybody else has any ...


Daniel Switkin

unread,
Oct 11, 2011, 4:10:26 PM10/11/11
to Steven Parkes, zx...@googlegroups.com
I just spent some time rereading the algorithm and trying to remember the subtleties of it. Before I get into your proposed fix Steven, I want to mention that Sean's change in CL 1500 seems wrong to me:


By setting the average to 1, that will lower the threshold, which will make more pixels white. Isn't that the opposite of what this change tried to do?

Daniel

Steven Parkes

unread,
Oct 11, 2011, 4:20:38 PM10/11/11
to Daniel Switkin, zx...@googlegroups.com
> I want to mention that Sean's change in CL 1500 seems wrong to me

Little subtle, but it doesn't lower it, it makes it 1. Since the max is 0, the min is 0 and twice the min is 0 which makes it white since the threshold is strictly <. I came across the same thing (see issue 469): my patch had been to use <= instead of < but the effect in this case is the same.

Sean Owen

unread,
Oct 11, 2011, 4:31:33 PM10/11/11
to zx...@googlegroups.com, Daniel Switkin
It wasn't my idea, but Steven's interpretation is right. It only matters when the region is all pure black (max=min=0), and makes sure it parses as pure black by making the threshold 1.

Here's a different rule that makes more "sense" to me but works less well. This makes the region white when it's low-contrast but on average seems absolutely brighter than 50%, and black otherwise.

          if (max + min > 0xFF) {
            average = min;
          } else {
            average = max + 1;
          }

Steven Parkes

unread,
Oct 11, 2011, 4:36:43 PM10/11/11
to zx...@googlegroups.com
> Here's a different rule that makes more "sense" to me but works less well

Yeah. A lot of the simple rules fail on images that have any shading (=== overall reduced dynamic range) and, in particular, variable shading. Those are the cases where HB's approach really shines, these corner cases notwithstanding.

Steven Parkes

unread,
Oct 11, 2011, 5:07:12 PM10/11/11
to Daniel Switkin, zx...@googlegroups.com

On Oct 11, 2011, at 1:10 PM, Daniel Switkin wrote:

> Before I get into your proposed fix Steven

Working on another idea that, if it works, will work a lot better ...

Steven Parkes

unread,
Oct 11, 2011, 8:16:24 PM10/11/11
to zx...@googlegroups.com
To lead off: net blackbox tests: +10 (+16, -6)

So I think there's a fairly simple and reliable mechanism for doing this.

I propose changing

int average;
if (max - min > 24) {
average = sum >> 6;
} else {
// When min == max == 0, let average be 1 so all is black
average = max == 0 ? 1 : min >> 1;
}
blackPoints[y][x] = average;

to

int average = -1;
if (max - min > 24) {
average = sum >> 6;
} else {
if (y > 0 && x > 0) {
int bp = (blackPoints[y-1][x] + 2*blackPoints[y][x-1] + blackPoints[y-1][x-1])/4;
if (min < bp) {
average = bp;
}
}
if (average == -1) {
average = min >> 1;
}
}
blackPoints[y][x] = average;


[As to the average = -1, I don't know if others have a different preferred style for not duplicating the min >> 1 which I don't like …]

The high level interpretation is that given we're looking for dark on light symbology, if we're in a low dynamic range area that should be dark, it was surrounded by a light region at some point and good black-point estimates were made at the transition. So in those cases, we use the average of those previous estimates.

We actually look at the black points of the block up, left, and up-left. Since I didn't want to do a real divide, I give the left block double weight.

I tried different conditionals, e,g., min < bp, max < bp, and (min+max)/2 < bp. min worked best in tests. It reduces false-whites without creating false-blacks. The rationale for this would be that the bp estimate at the transition could be on the low side if the average was dominated by dark pixels. It'd be nice to be able to update or reestimate the bp in these blocks rather than use the surrounding average but I haven't come up with any local way of doing that. While we have an estimate of what the local black value is, we don't have an estimate for the local white value.

The -6 blackbox tests are in the datamatrix tests. And that's a net -6, there are some new successes and some new failures.

I spent a fair amount of time tracking one error. It was on a previous version of the code that now passes but I think it demonstrates what's going on.

Image (datamatrix-2/03.jpg): http://dl.dropbox.com/u/11615635/zxing/image.jpg

Original binarized image: http://dl.dropbox.com/u/11615635/zxing/orig.png
And the new one: http://dl.dropbox.com/u/11615635/zxing/new.png

They look very similar. In fact, the key difference between decode and not in this case is a single pixel in the upper left hand corner:

Original zoomed: http://dl.dropbox.com/u/11615635/zxing/orig-zoomed.png
New zoomed: http://dl.dropbox.com/u/11615635/zxing/new-zoomed.png

I tracked this as far as getting a one pixel difference in the surrounding white rectangle estimate and that was enough to throw off the decode.

I believe the other new datamatrix failures are of the same class. I eyeballed the binarizer outputs and they all had tiny changes that were not very significant.

Steven Parkes

unread,
Oct 11, 2011, 9:36:17 PM10/11/11
to zx...@googlegroups.com
And a case these changes help but don't fix is http://dl.dropbox.com/u/11615635/zxing/desktop.png

This image was originally 2k x 2.5k (taken with an iPhone 4).

It's an image of the IOS simulator but essentially it's the same for any high resolution image taken from an LCD screen: camera resolutions and focus have gotten so good, they can resolve the individual LCD pixels. Our eyes low-pass filter this stuff but the camera doesn't.

I don't have any ideas for an elegant solution that will commonly work with this. It's relatively easy to low pass filter the image by just decimating it which also has the benefit of reducing the computation required. But if the picture is of something further away, low pass filtering will blur things and inhibit the ability to resolve smaller codes.

So I'm back to trying a decimated decode with a fall-back to a full-resolution decode.

Message has been deleted
Message has been deleted

Daniel Switkin

unread,
Oct 12, 2011, 12:46:23 PM10/12/11
to Steven Parkes, zx...@googlegroups.com
On Tue, Oct 11, 2011 at 4:20 PM, Steven Parkes <smpa...@smparkes.net> wrote:
> I want to mention that Sean's change in CL 1500 seems wrong to me

Little subtle, but it doesn't lower it, it makes it 1. Since the max is 0, the min is 0 and twice the min is 0 which makes it white since the threshold is strictly <. I came across the same thing (see issue 469): my patch had been to use <= instead of < but the effect in this case is the same.

Yeah, I get it. I prefer testing pixel <= threshold too since it's clearer: a completely black pixel should always come out black.

Whatever we change here is going to need a boatload of comments.

Daniel

Daniel Switkin

unread,
Oct 12, 2011, 12:59:47 PM10/12/11
to Steven Parkes, zx...@googlegroups.com
On Tue, Oct 11, 2011 at 8:16 PM, Steven Parkes <smpa...@smparkes.net> wrote:
       int average = -1;
       if (max - min > 24) {
         average = sum >> 6;
       } else {
         if (y > 0 && x > 0) {
           int bp = (blackPoints[y-1][x] + 2*blackPoints[y][x-1] + blackPoints[y-1][x-1])/4;
           if (min < bp) {
             average = bp;
           }
         }
         if (average == -1) {
           average = min >> 1;
         }
       }
       blackPoints[y][x] = average;

I'm not sure the contents of the (y > 0 && x > 0) block are what you want.

For example, if min is 240 and max is 255, but bp is 248, the average will be 248. That's exactly what I was trying to avoid, i.e. picking a threshold in between a small contrast range which causes noise to be interpreted as real black and white data.

I think you want to pick a value outside the min..max range. It should be less than min for bright values, and greater than max for dark values. The trick is knowing what's bright and what's dark without using a global value. Perhaps your idea of neighboring blackPoints could be used for that.

Thoughts? I'd rather talk this out a bit more before committing.

Daniel

Steven Parkes

unread,
Oct 12, 2011, 1:35:57 PM10/12/11
to Daniel Switkin, zx...@googlegroups.com
> I'm not sure the contents of the (y > 0 && x > 0) block are what you want.

I think we're on the same page, but that's just a boundary condition to force back to the existing code if there aren't any previous estimates. The code actually checks the previous bp estimates of the blocks at left [x-1,y], up [x,y-1], and up-left [x-1,y-1]

> Perhaps your idea of neighboring blackPoints could be used for that.

That's what this code does, right? It just drops back to the existing mechanism where that's not possible.

int bp = (blackPoints[y-1][x] + 2*blackPoints[y][x-1] + blackPoints[y-1][x-1])/4;
if (min < bp) {
average = bp;
}

It's looking at the average of previous black points as the local estimate of the bp. If the current min is below the bp, we assume the "inherited" estimated bp is as good as we have. The intention/intuition is that the bp used within a large black area is the bp estimated at the boundary of the block, when it went from light to dark.

The conditional (min < bp) is supposed to protect against the (presumably rare) case where the bp block boundaries line up exactly with a transition from dark to light.

Daniel Switkin

unread,
Oct 12, 2011, 4:21:07 PM10/12/11
to Steven Parkes, zx...@googlegroups.com
To be clear the x and y checks themselves are fine, I'm talking about the code within the braces. We fall back to min >> 1 which only makes sense for bright areas. It would be nice if very dark low contrast areas came out dark, not just for pixel == 0.

Steven Parkes

unread,
Oct 13, 2011, 1:26:00 PM10/13/11
to Daniel Switkin, zx...@googlegroups.com
> I'm talking about the code within the braces. We fall back to min >> 1 which only makes sense for bright areas. It would be nice if very dark low contrast areas came out dark, not just for pixel == 0.

Yeah, that's the key here. But there's something I'm not following because that's exactly the behavior that I'm proposing we change, though only in a subset of the cases.

Here's the whole new code

int average = -1;
if (max - min > 24) {
average = sum >> 6;
} else {

if (y > 0 && x > 0) {
int bp = (blackPoints[y-1][x] + 2*blackPoints[y][x-1] + blackPoints[y-1][x-1]) >> 2;


if (min < bp) {
average = bp;
}
}

if (average == -1) {
average = min >> 1;
}
}
blackPoints[y][x] = average;

This doesn't have the max == 0 condition anymore because it doesn't need it.

Here's what I think is supposed to happen. (I know some of this is obvious and repetitive, but there's something missing in this discussion and I don't know what it is.)

We have three cases here, generally speaking. Blocks that are a mix of light and dark pixels, blocks that are light only, and blocks there are dark only. When estimated the blackpoint (bp) we don't know a priori which case we have.

The mixed case is detected by a max-min difference greater that 24. In this case, the bp estimate is the average of all the pixels in the block. This seems like a simple and effective solution. The more abstract idea would be, I think, to assume you had a bimodal distribution of pixels and to use some kind of classifier like k-means to estimate the means and then use the midpoint between them. That would seem to work better than the current average for blocks that have far from an equal number of light and dark pixels. But the current average seems to work well enough to not warrant the extra complexity (even if I knew how to do it … I really only understand classification algorithms at the abstract level.) Moreover, I think we're more sensitive to false-black (signal) than to false-white (background) so I'd guess there are cases where something that was actually "more correct" by some metric would result in worse decoding.

So that leaves the all-light and all-dark blocks. The problem in both cases is we have no estimate of the dynamic range, no idea "how high is up". 128 is dark in one image and light in another. In fact, 128 could be dark in one part of an image and light in another of the same image. HB does a good job of handling many cases like this by not assuming a global dynamic range.

The legacy heuristic is to assume that all non-mixed blocks are white. That still doesn't provide any indication of the level of dark so the estimate is made at half the min in the block. This is an effective estimate in many cases but will fail in a some cases, e.g., where dark is 128 and light is 255. But it seems to work well enough and nothing better has been proposed.

So the final case is all dark blocks. The legacy heuristic assumes these to be light and sets the bp estimate at half the min which causes them generally to be binarized as light.

That was the impetus for the "max == 0" hack, so that synthetic black and non-black (black == 0, non-black != 0) images with a resolution such that the finder size was greater than the HB block size would decode. Without that condition, HB would create light regions inside the finder patterns and the finder pattern finder would fail.

But that's really just a hack on the "assume it's light" assumption. So what I'm preposing is not always assuming non-mixed blocks are light.

Then the questions are (assuming you maintain the current bp estimate of min/2 if it's a light block):

1) how do you know if it's a light or dark block
2) what do you use for the bp in a dark block

And we want to do this without assuming any global information like the global min and max for the image. That kind of assumption doesn't hold for images with varying local dynamic range, e.g., images with shadows.

The key to (1) is to assume that all-dark areas are surrounded by areas that are not all-dark, i.e., are all-light or mixed. SInce we're doing dark on light symbology, this should always be the case, at least for blocks we care about. We also assume we're making bp estimates top left to bottom right so if we are in a black region (that we care about) we have valid estimates at the boundary from light to dark that were based on either mixed or all light blocks. So to choose between all-light or all-dark, we just compare the values in the block with the average of the neighboring bp estimates. These estimates should always be reasonably accurate (as good as any of our estimates).

Actually, that skips a step, which is what about dark blocks that are surrounded by dark blocks? That leads to (2), what should the bp estimate be for a block that we decide is all dark? We have no idea what the dynamic range is, how high is high, since there are no light values in the block. But we do have the previous black points … so we just use the average of those values.

So the summary is the bp is estimated as

- the average the pixels for mixed blocks
- half the min value in the block for light blocks
- the average of the three previous bps for dark blocks

- mixed blocks are blocks with max - min > 24
- light blocks are blocks that are not mixed and either no previous bp estimates exist or where min >= the previous bp estimates
- dark blocks are blocks that are not mixed and min < the previous bp estimates

This only changes the algorithm in cases where black regions are surrounded by light regions but those are the only ones we care about … at least those are the only ones I'm addressing in this. If an algorithm existed that could subsume this (as this subsumes the max == 0 hack), that'd be great but I don't know of one.

The (min < bp) part still feels a bit arbitrary. I tried (max < bp) and (max + min)/2 < bp and they both performed more poorly.

Sean Owen

unread,
Oct 14, 2011, 4:24:42 AM10/14/11
to zx...@googlegroups.com, Steven Parkes
Indeed, that makes good sense. That's what my change does, essentially. For low-contrast regions, it comes out white when it's generally bright, and black when it's generally dark. The funny thing is it doesn't work as well. Not much worse, but clearly worse.

The current rule is dumber, but more effective. The rule is that low-contrast regions are always white. (Well, except the 0 luminance special case.) Seems undesirable, but somehow just blanking everything to the same thing in low-contrast areas is better. 
Likewise, the "min >> 1" piece seems unnecessarily low. Setting to "min - 1" should be just as fine as it produces the same result. But "min >> 1" is better. It's because it influences the threshold for nearby blocks. It pulls down the average notably, and therefore also encourages nearby blocks to be white. Again -- seems less than intelligent. But it is actually helpful. The reason is almost surely that it chops out noise.

Steven's rule leaves most of this effect in place and tries to be more selective about what is left black, and it does work better -- not miles better, but clearly better.

Now, clearly this section is just a handful of hackery. It would be nice to have a full understanding of what's working well and craft something more elegant. But this is a small change, it's better, it makes some sense. I can't find any comparable variant that does better. I'd support putting this in for sure -- I've toyed with it enough to think we're not missing something obviously better and easy.

Steven Parkes

unread,
Oct 14, 2011, 8:43:44 AM10/14/11
to zx...@googlegroups.com
> The reason is almost surely that it chops out noise.

Yup. Some ad hoc low pass filtering. I think this combines with the fact that at least some of the decoders are more sensitive to false-dark (signal) than to false light (background) so the impact of errors is asymmetric.

Daniel Switkin

unread,
Oct 14, 2011, 12:22:50 PM10/14/11
to Steven Parkes, zx...@googlegroups.com
Thanks for typing all that Steven. The code you posted can still produce an average of zero, so I believe we need to test for pixel <= threshold to use it. Have you tried that?

The min < bp check is probably what bugs me the most. Here's a few variations I brainstormed but haven't tested:

// Trust your neighbors completely
int average;
if (max - min > 24) {
  average = sum >> 6;
} else {
  if (y > 0 && x > 0) {
    average = getBlackPointFromNeighbors(x, y);
  } else {
    average = min >> 1;
  }
}
blackPoints[y][x] = average;

// Blend your average with your neighbors
int average = sum >> 6;
if (max - min <= 24) {
 if (y > 0 && x > 0) {
  int bp = getBlackPointFromNeighbors(x, y);
  average = (average + bp) >> 1;
 } else {
  average = min >> 1;
 }
}
blackPoints[y][x] = average;

// Another form of blending with neighbors
int average;
if (max - min > 24) {
  average = sum >> 6;
} else {
  if (y > 0 && x > 0) {
    int bp = getBlackPointFromNeighbors(x, y);
    if (max < bp) {
      average = (max + bp) >> 1;
    } else if (min > bp) {
      average = (min + bp) >> 1;
    } else {
      average = bp;
    }
  } else {
    average = min >> 1;
  }
}
blackPoints[y][x] = average;

If you have a chance, I'm curious to see how these perform in the blackbox tests against what you've come up with. In the past I've used CommandLineRunner --dump_black_point to visually inspect the output.

Daniel

Steven Parkes

unread,
Oct 14, 2011, 12:42:01 PM10/14/11
to Daniel Switkin, zx...@googlegroups.com
> The code you posted can still produce an average of zero, so I believe we need to test for pixel <= threshold to use it.

It can for blocks not surrounded by something light or mixed. Do you think it can for blocks that are?

I have no problem putting it back in but left it out since I didn't think it could matter. But maybe I missed something.

> Have you tried that?

Can't remember. I'll recheck.

> The min < bp check is probably what bugs me the most.

Me, too.

> Here's a few variations I brainstormed but haven't tested

I'll test 'em.

Steven Parkes

unread,
Oct 14, 2011, 1:45:55 PM10/14/11
to Daniel Switkin, zx...@googlegroups.com

On Oct 14, 2011, at 9:22 AM, Daniel Switkin wrote:

> I believe we need to test for pixel <= threshold

and my earlier comment

> I have no problem putting it back in but left it out since I didn't think it could matter.

I actually meant the (max == 0) version when I said left out. Felt like a hack. I like the "<= threshold" version.

I wouldn't think it would make any difference, but against my current patch, it's a net blackbox win of +8.


Daniel Switkin

unread,
Oct 14, 2011, 2:32:41 PM10/14/11
to Steven Parkes, zx...@googlegroups.com
I say go for pixel <= threshold, and please add a comment about pixel == 0 values.

Steven Parkes

unread,
Oct 14, 2011, 2:39:38 PM10/14/11
to Daniel Switkin, zx...@googlegroups.com

>> Here's a few variations I brainstormed but haven't tested
>
> I'll test 'em.

Delta blackbox test passes.

Baseline is my current patch with the <= threshold added back in.

Trust neighbors: -24
Blend with neighbors: -127
Conditional blend with neighbors: -118

Daniel Switkin

unread,
Oct 14, 2011, 3:00:07 PM10/14/11
to Steven Parkes, zx...@googlegroups.com
Interesting, thanks for trying.

How many net images does your technique gain against the trunk, including the <= threshold change?

Also, do we have any megapixel or larger images to test with where the QR is quite large, to visually check that we're filling in the blacks correctly?

Sean Owen

unread,
Oct 14, 2011, 3:09:59 PM10/14/11
to zx...@googlegroups.com, Steven Parkes
By my count, the net score is +14:

-2 Aztec

-8 Datamatrix

+6 PDF417

+18 QR code


I did verify that it works correctly on a 3000x3000 image with 100x100 modules.

I'd like to commit this with some comments to Java.

Sean Owen

unread,
Oct 14, 2011, 3:17:22 PM10/14/11
to zx...@googlegroups.com, Steven Parkes
Make that +19 -- missed +5 RSS14.

Daniel Switkin

unread,
Oct 14, 2011, 3:43:55 PM10/14/11
to zx...@googlegroups.com, Steven Parkes
Cool, thanks Sean. Yes, lots and lots of comments.

Steven Parkes

unread,
Oct 14, 2011, 3:53:25 PM10/14/11
to Daniel Switkin, zx...@googlegroups.com
There are multiple slightly interacting things here that it's hard to keep straight.

Compared to my patch:

Straight legacy (max == 0 ? 1, < threshold): -14
Legacy with max == 0 ? 1 and <= threshold: -16
Legacy with <= threshold w/o max == 0 ? 1: -10

- As Sean mentioned, these haven't been including the rss tests since they aren't in the current build.xml.
- These don't include a new qr test that I've added.

As both Sean I have mentioned, these aren't monotonic. There are some new failures as well as the new successes. Even across the cases above these vary. I did look at one of the new datamatrix failures and it came down to being sensitive to a single pixel change on the corner of the code.


Steven Parkes

unread,
Oct 14, 2011, 3:55:20 PM10/14/11
to zx...@googlegroups.com
> I'd like to commit this with some comments to Java.

Please give me a chance to respond … I'm still trying to catch up.

Steven Parkes

unread,
Oct 14, 2011, 4:57:42 PM10/14/11
to zx...@googlegroups.com

On Oct 14, 2011, at 12:09 PM, Sean Owen wrote:

> I'd like to commit this with some comments to Java.

I really don't want to make a deal about this …

… but I guess I should let you know that this kind of discourages me, from the point of view of trying to be a valued member of the project/community.

I'm a committer, so I can commit my own code … unless I misunderstood what becoming a committer meant and it was really meant only for the C++ port. If that's the case, I really did misunderstand.

If that's not the case … I can tell you after having slaved on this for a couple of days, to have someone else commit it just makes me feel bad. I spent a lot of time looking at the issue and writing up the rationale, I have both Java and C++ versions of the patch queued to commit, etc.

I don't begrudge that at all … that's what it means to me to be part of an open source project. But I've figured I'm on an equal footing with other committers. I'm used to the attitude of committers being you commit what you write and/or sponsor from non-committer contributors. (I probably inherited this attitude from my work with Apache Lucene, which I think has an admirable approach to community.)

If that's not the case, I can live with it, but my approach to the project may end up changing some. I may not spend as much time trying to explain things, to create consensus. It's a lot of effort.

This extends over to the C++ port. I try pretty hard to keep it in sync with the Java port, at least the parts that are currently up to date. That can be a lonely challenge. On the flip side, I propose my changes in the Java code base even though I'm not currently using it. I figure it's just the right thing to do, to get agreement in general and then implement in both code bases.

From my point of view, I'd prefer the commit of this to be one commit, to both code bases since I know it's the same. To have someone else commit part of it makes extra work for me to go back and make sure they are the same. (I try to keep them as identical as possible, down to the variable names, to minimize future support effort).

Reminds me that I worry about how much the C++ port is considered a poor stepchild. I don't expect people contributing to the Java port to make equivalent changes to the C++ code base but there are lots of things that Java contributors can do or not do that can have a significant impact on how difficult it is to keep the C++ code up to date. I don't really know what to expect in terms of people's behavior, whether out of respect or otherwise, they make an effort to make things as easy as possible for people trying to maintain other ports, or whether that just isn't a consideration and the chips just fall where they may for the non-Java ports.

Sean Owen

unread,
Oct 14, 2011, 5:04:12 PM10/14/11
to zx...@googlegroups.com
Oh sorry, was just offering to do the legwork to update tests, comments and commit. You are more than welcome to do so. I had in mind you were working on the C++ version, and I've done the Java part locally.
I suppose I'm used to being the one who would have to commit as I have the access.

Sean Owen

unread,
Oct 14, 2011, 5:05:42 PM10/14/11
to zx...@googlegroups.com
(the slightly bad news about the Java port is that it's about to be overhauled for Java 6. This will probably make it a bit harder to re-port as it uses more language features and JDK classes than before.)

Steven Parkes

unread,
Oct 14, 2011, 8:15:47 PM10/14/11
to zx...@googlegroups.com

On Oct 14, 2011, at 2:05 PM, Sean Owen wrote:

> (the slightly bad news about the Java port is that it's about to be overhauled for Java 6. This will probably make it a bit harder to re-port as it uses more language features and JDK classes than before.)

Good to know.

Guess I was always swimming against the current. Guess it's as good a time as any to throw in the towel.

I'll check in the thresholding stuff but I can't reach parity between the Java and C++ after r1697.

Daniel Switkin

unread,
Oct 19, 2011, 11:23:33 AM10/19/11
to Steven Parkes, zx...@googlegroups.com
Steven, in case there was any doubt, let me say I really appreciate all you've contributed to the project and hope you continue to do so. Of course you can commit to the Java, C++, or any other trees you like.

Daniel

Steven Parkes

unread,
Oct 19, 2011, 11:39:12 AM10/19/11
to Daniel Switkin, zx...@googlegroups.com
> Steven, in case there was any doubt, let me say I really appreciate all you've contributed to the project and hope you continue to do so. Of course you can commit to the Java, C++, or any other trees you like.

:)

I realized one way to make the C++ and Java tests stably comparable is to implement a luminance source with a known algorithm, which could be used just for the tests. That way "real" code gets the built-in mechanism which may be faster but we can still compare things to a known reference where performance isn't critical but repeatability is. In my C++ testing, I use ImageMagick to reduce RGB to grayscale but it's only somewhat by luck that it happens to use the same conversion function that the Java code used to use (and even that required a small tweak to the Java code).

Steven Parkes

unread,
Jun 26, 2012, 10:39:18 AM6/26/12
to pmenaria, zx...@googlegroups.com

On Jun 26, 2012, at 2:15 AM, pmenaria wrote:

> Hi, I want to increase the scanning distance of the qr code but want to keep the qr code size same. Can you suggest me something Pls.

Not sure why this is attached to the old binarizer thread.

The key to decoding is detection of the code and sufficient resolution to identify the qr modules. You can try bumping the resolution of the capture but how much this will help is unknown.
Reply all
Reply to author
Forward
0 new messages