Open ghukill opened 8 years ago
Hilariously, perhaps you've bumped up against this too!?
That was related to something slightly different. In the description of qualities in the 1.0 version of the API we said that bitonal images had to be "1 bit per pixel". When I joined the editorial team for 1.1 I was pretty sure that wasn't possible with JPEGs, so that post is an artifact of my trying confirm without digging into the JPEG spec :smile:.
I agree that this case needs handling, but I think there might be something up with the color management of your JP2s as well, because: http://libimages.princeton.edu/loris2/AC125%2Fc0008%2F00000006.jp2/full/full/0/bitonal.jpg
FWIW, If you're using a newer version of ImageMagick, you might be OK, but older versions used JasPer, which was a very flawed JP2 implementation, for encoding JP2s. Newer versions use OpenJpeg, which is likely to be compliant with the spec. We use Kakadu for our encoding, and (again, FWIW) you can see our "recipes" here. That example image above was probably encoded with the 20_1_gray
recipe (20_1 = 20:1 compression), and I think the -jp2_space sLUM
param/arg is the key.
Found an approach that seems to work. PIL allows you to check image band extremes with the
im.getextrema()
method.
I can't immediately think of anything better. That seems like it would be a pretty expensive check, but presumably you'd only need to do it here? I'd say go ahead and submit a PR.
Thanks for such a detailed response. I think you're absolutely right that our JP2s could use some color management. We have been making JP2 derivatives upon ingest, and they've looked and worked great, until now. But this is a good opportunity to revisit that process; thanks for the Kakadu "recipes". To be honest, not sure what library we delegated imagemagick to use at the time, it's been awhile now.
As for the check in Loris, maybe it'd make sense to sit on it for a bit? I was wary to even mention it, as I had feared it was specific to our JP2s. Perhaps they rendered just as they should, given the odd JP2s Loris was working with. We'll try some alternative derivates, see how they look, and report back. Thanks again for all your thoughts and insight!
Whichever. I'm not opposed to putting the check in. JP2 is complicated enough that having these kinds of patches is really just practical--there are things like this all over the place.
Here's the rub, I think it would have to be outside the image_request.quality
check here to be effective.
Our JP2s are coming through with im.mode
'RGB', and unfortunately, we don't always have the ability to explicitly request 'bitonal' from the front-end (mixed thumbnails, or some of our digitized books even mix color and bitonal images).
That would mean it would fire on many (if not most) image requests. But I can got ahead and submit a PR, and see how it looks and feels.
Cue truck reverse beeps. I had a pull request ready to go, but thought I should dig in a bit more and see if the .getextrema()
check covered all instances. It didn't. Something about our JP2's, the highest level of tiles in the file did not have the signature ((0,192),(0,192),(0,192))
bands. They would be uniform, in that they generally start at 0, but would terminate at an unknown maximum (e.g. ((0,243),(0,243),(0,243))
or ((0,168),(0,168),(0,168))
, etc.). This would mean checking for repeating bands in .getextrema()
, which improbable for a color image, certainly isn't impossible.
This has convinced me it's really more of a hack for ill-formed JP2s, rather than a prudent check, and the problem is really with the JP2s themselves. But thanks again for thinking through this, much appreciated, and will update you on other channels or a new issue if we discover anything of note.
Just to update this - we've regenerated our JP2s via your kindly shared recipes, and they all render beautifully now, further supporting the idea that fixing the JP2s was the better route than trying to sniff out poorly formed, bitonal JP2s.
Before submitting a pull request, thought submitting an issue might be a better place to start. We had a headscratcher with JP2s, where when originating from bitonal TIFFs, were rendering gray.
Example JP2 rendering gray, where ideally it'd be white: http://digital.library.wayne.edu/loris/fedora:wayne:Afkestie1906b50081986%7CIMAGE_7_JP2/full/full/0/default.jpg
The original bitonal TIFF, rendering through Loris, showing normal tones: http://digital.library.wayne.edu/loris/fedora:wayne:Afkestie1906b50081986%7CIMAGE_7/full/full/0/default.jpg
To demonstrate that it's only JP2s originating from bitonal images, here's one that came from an RGB TIFF that renders normally: http://digital.library.wayne.edu/loris/fedora:wayne:Afkestie1906b50081986%7CIMAGE_6_JP2/full/full/0/default.jpg
The imagemagick convert command we're using used to generate JP2s:
convert {input} {output}[256x256].jp2
Hilariously, perhaps you've bumped up against this too!?
As is the case with the TIFF example above, Loris handles bitonal images just fine if the
im.mode
is '1'. The problem arises when it's not.Found an approach that seems to work. PIL allows you to check image band extremes with the
im.getextrema()
method. All of our originally bitonal images, when notim.mode
'1', have extremes of( (0,192), (0,192), (0,192) )
. Putting in a check for thatim.getextrema()
signature, and if detected, converting toim.mode
'1' with the appropriate dithering seems to do the trick. Images render much closer to a non-bitonal image, and performance doesn't seem to take much of a hit.Here's a bitonal JP2, without
im.mode
'1', rendering correctly with that check in place: http://digital.library.wayne.edu/loris_dev/wayne:Afkestie1906b50081986%7CIMAGE_7_JP2/full/full/0/default.jpgAnd a non bitonal JP2, with the check in place but not firing (because the
im.getextrema()
values are much more diverse than 0's and 192's): http://digital.library.wayne.edu/loris_dev/wayne:Afkestie1906b50081986%7CIMAGE_6_JP2/full/full/0/default.jpgWorth pursuing or issuing a pull request? or maybe better left addressed locally with our Loris instance? One gotcha I can see, however infinitesimally small, would be a non-bitonal image with
im.getextrema()
that is( (0,192), (0,192), (0,192) )
.