-
Notifications
You must be signed in to change notification settings - Fork 488
Implement Huffman Tables directory and reader #231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Created class HuffmanTablesDirectory - Created class HuffmanTablesDescriptor - Created class JpegDhtReader - Added method SequentialReader.available() to see if there's more to read. - Did some minor tweaks to JpegSegmentType
@drewnoakes The explanation about "typical"/"optimized" is in Javadoc on the |
My mistake. Very thorough, thanks. |
I'll merge this now. @kwhopper can provide any feedback at his convenience. |
Cheers 👍 |
} | ||
|
||
@Nullable | ||
public String getNumerOfTablesDescription() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method name typo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, seems so 😞
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in d691939.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There seems to be a test usage for that method as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, force pushed a fix to my fix in fb0b62d.
I can't intelligently comment without reading up on the spec, but I'm fine if you're fine. The code seems reasonable. Has it been run through the images repo? |
I haven't run it through the repo, it's a huge download. |
I ran it through the images repo. Seems good. Lots of diffs like this: diff --git a/jpg/ImageTestSuite/metadata/316be81dfdeeb942e904feb3a77f4f83.jpg.txt b/jpg/ImageTestSuite/metadata/316be81dfdeeb942e904feb3a77f4f83.jpg.txt
index 4820961..ce5bd52 100644
--- a/jpg/ImageTestSuite/metadata/316be81dfdeeb942e904feb3a77f4f83.jpg.txt
+++ b/jpg/ImageTestSuite/metadata/316be81dfdeeb942e904feb3a77f4f83.jpg.txt
@@ -17,12 +17,15 @@ TYPE: JPEG
[JFIF - 0x000c] Thumbnail Width Pixels = 0
[JFIF - 0x000d] Thumbnail Height Pixels = 0
+[Huffman - 0x0001] Number of Tables = 2 Huffman tables
+
[File - 0x0001] File Name = 316be81dfdeeb942e904feb3a77f4f83.jpg
[File - 0x0002] File Size = 49977 bytes
[File - 0x0003] File Modified Date = <omitted for regression testing as checkout dependent>
- JPEG
- JFIF
+- Huffman
- File |
The diff snippet you included made me wonder if I made a bug, only 2 Huffman tables sounded strange. I downloaded JPEGsnoop and I think I've found the reason as well. I'm not sure if this should be changed or not. This problem will only apply to progressive JPEGs and the only alternative is to scan through the whole file. Is collecting the remaining Here is the JPEGsnoop report for the image: JPEGsnoop 1.7.5 by Calvin Hass
http://www.impulseadventure.com/photo/
-------------------------------------
Filename: [testObjects\316be81dfdeeb942e904feb3a77f4f83.jpg]
Filesize: [49977] Bytes
Start Offset: 0x00000000
*** Marker: SOI (xFFD8) ***
OFFSET: 0x00000000
*** Marker: APP0 (xFFE0) ***
OFFSET: 0x00000002
Length = 16
Identifier = [JFIF]
version = [1.1]
density = 229 x 229 DPI (dots per inch)
thumbnail = 0 x 0
*** Marker: DQT (xFFDB) ***
Define a Quantization Table.
OFFSET: 0x00000014
Table length = 131
----
Precision=16 bits
Destination ID=0 (Luminance)
DQT, Row #0: 53 37 33 53 80 133 170 203
DQT, Row #1: 40 40 47 63 87 193 200 183
DQT, Row #2: 47 43 53 80 133 190 230 186
DQT, Row #3: 47 57 73 97 170 290 266 206
DQT, Row #4: 60 73 123 186 226 363 343 256
DQT, Row #5: 80 117 183 213 270 346 376 306
DQT, Row #6: 163 213 260 290 343 403 400 336
DQT, Row #7: 240 306 316 326 373 333 343 330
Approx quality factor = 15.01 (scaling=333.00 variance=1.25)
*** Marker: DQT (xFFDB) ***
Define a Quantization Table.
OFFSET: 0x00000099
Table length = 131
----
Precision=16 bits
Destination ID=1 (Chrominance)
DQT, Row #0: 57 60 80 157 330 330 330 330
DQT, Row #1: 60 70 87 220 330 330 330 330
DQT, Row #2: 80 87 186 330 330 330 330 330
DQT, Row #3: 157 220 330 330 330 330 330 330
DQT, Row #4: 330 330 330 330 330 330 330 330
DQT, Row #5: 330 330 330 330 330 330 330 330
DQT, Row #6: 330 330 330 330 330 330 330 330
DQT, Row #7: 330 330 330 330 330 330 330 330
Approx quality factor = 15.00 (scaling=333.41 variance=0.14)
*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) ***
OFFSET: 0x0000011E
Frame header length = 17
Precision = 8
Number of Lines = 1071
Samples per Line = 1443
Image Size = 1443 x 1071
Raw Image Orientation = Landscape
Number of Img components = 3
Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y)
Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb)
Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr)
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00000131
Huffman table length = 25
----
Destination ID = 0
Class = 0 (DC / Lossless Table)
Codes of length 01 bits (001 total): 00
Codes of length 02 bits (001 total): 01
Codes of length 03 bits (001 total): 02
Codes of length 04 bits (001 total): 03
Codes of length 05 bits (001 total): 04
Codes of length 06 bits (001 total): 05
Codes of length 07 bits (000 total):
Codes of length 08 bits (000 total):
Codes of length 09 bits (000 total):
Codes of length 10 bits (000 total):
Codes of length 11 bits (000 total):
Codes of length 12 bits (000 total):
Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total):
Total number of codes: 006
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x0000014C
Huffman table length = 22
----
Destination ID = 1
Class = 0 (DC / Lossless Table)
Codes of length 01 bits (001 total): 00
Codes of length 02 bits (001 total): 01
Codes of length 03 bits (001 total): 02
Codes of length 04 bits (000 total):
Codes of length 05 bits (000 total):
Codes of length 06 bits (000 total):
Codes of length 07 bits (000 total):
Codes of length 08 bits (000 total):
Codes of length 09 bits (000 total):
Codes of length 10 bits (000 total):
Codes of length 11 bits (000 total):
Codes of length 12 bits (000 total):
Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total):
Total number of codes: 003
*** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x00000164
Scan header length = 12
Number of img components = 3
Component[1]: selector=0x01, table=0(DC),0(AC)
Component[2]: selector=0x02, table=1(DC),0(AC)
Component[3]: selector=0x03, table=1(DC),0(AC)
Spectral selection = 0 .. 0
Successive approximation = 0x01
NOTE: Scan parsing doesn't support this SOF mode.
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x000028E0
Huffman table length = 38
----
Destination ID = 0
Class = 1 (AC Table)
Codes of length 01 bits (000 total):
Codes of length 02 bits (002 total): 00 01
Codes of length 03 bits (001 total): 11
Codes of length 04 bits (004 total): 02 10 20 30
Codes of length 05 bits (003 total): 12 31 40
Codes of length 06 bits (000 total):
Codes of length 07 bits (003 total): 21 41 50
Codes of length 08 bits (001 total): 60
Codes of length 09 bits (001 total): 03
Codes of length 10 bits (000 total):
Codes of length 11 bits (003 total): 22 32 42
Codes of length 12 bits (001 total): 13
Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total):
Total number of codes: 019
*** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x00002908
Scan header length = 8
Number of img components = 1
Component[1]: selector=0x01, table=0(DC),0(AC)
Spectral selection = 1 .. 5
Successive approximation = 0x02
NOTE: Scan parsing doesn't support this SOF mode.
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00003D97
Huffman table length = 29
----
Destination ID = 1
Class = 1 (AC Table)
Codes of length 01 bits (001 total): 01
Codes of length 02 bits (000 total):
Codes of length 03 bits (003 total): 00 10 11
Codes of length 04 bits (001 total): 20
Codes of length 05 bits (000 total):
Codes of length 06 bits (002 total): 30 60
Codes of length 07 bits (003 total): 80 90 C0
Codes of length 08 bits (000 total):
Codes of length 09 bits (000 total):
Codes of length 10 bits (000 total):
Codes of length 11 bits (000 total):
Codes of length 12 bits (000 total):
Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total):
Total number of codes: 010
*** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x00003DB6
Scan header length = 8
Number of img components = 1
Component[1]: selector=0x03, table=0(DC),1(AC)
Spectral selection = 1 .. 63
Successive approximation = 0x01
NOTE: Scan parsing doesn't support this SOF mode.
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00003DE1
Huffman table length = 28
----
Destination ID = 1
Class = 1 (AC Table)
Codes of length 01 bits (001 total): 01
Codes of length 02 bits (000 total):
Codes of length 03 bits (002 total): 10 11
Codes of length 04 bits (003 total): 00 20 60
Codes of length 05 bits (000 total):
Codes of length 06 bits (003 total): 80 90 C0
Codes of length 07 bits (000 total):
Codes of length 08 bits (000 total):
Codes of length 09 bits (000 total):
Codes of length 10 bits (000 total):
Codes of length 11 bits (000 total):
Codes of length 12 bits (000 total):
Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total):
Total number of codes: 009
*** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x00003DFF
Scan header length = 8
Number of img components = 1
Component[1]: selector=0x02, table=0(DC),1(AC)
Spectral selection = 1 .. 63
Successive approximation = 0x01
NOTE: Scan parsing doesn't support this SOF mode.
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00003E21
Huffman table length = 23
----
Destination ID = 0
Class = 1 (AC Table)
Codes of length 01 bits (001 total): 21
Codes of length 02 bits (001 total): D0
Codes of length 03 bits (001 total): A0
Codes of length 04 bits (001 total): B0
Codes of length 05 bits (000 total):
Codes of length 06 bits (000 total):
Codes of length 07 bits (000 total):
Codes of length 08 bits (000 total):
Codes of length 09 bits (000 total):
Codes of length 10 bits (000 total):
Codes of length 11 bits (000 total):
Codes of length 12 bits (000 total):
Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total):
Total number of codes: 004
*** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x00003E3A
Scan header length = 8
Number of img components = 1
Component[1]: selector=0x01, table=0(DC),0(AC)
Spectral selection = 6 .. 63
Successive approximation = 0x02
NOTE: Scan parsing doesn't support this SOF mode.
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00003E4C
Huffman table length = 36
----
Destination ID = 0
Class = 1 (AC Table)
Codes of length 01 bits (000 total):
Codes of length 02 bits (002 total): 00 01
Codes of length 03 bits (002 total): 10 11
Codes of length 04 bits (002 total): 21 31
Codes of length 05 bits (003 total): 20 30 41
Codes of length 06 bits (000 total):
Codes of length 07 bits (003 total): 40 50 51
Codes of length 08 bits (001 total): 61
Codes of length 09 bits (001 total): 60
Codes of length 10 bits (001 total): 71
Codes of length 11 bits (001 total): 81
Codes of length 12 bits (001 total): B1
Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total):
Total number of codes: 017
*** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x00003E72
Scan header length = 8
Number of img components = 1
Component[1]: selector=0x01, table=0(DC),0(AC)
Spectral selection = 1 .. 63
Successive approximation = 0x21
NOTE: Scan parsing doesn't support this SOF mode.
*** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x00006325
Scan header length = 12
Number of img components = 3
Component[1]: selector=0x01, table=0(DC),0(AC)
Component[2]: selector=0x02, table=0(DC),0(AC)
Component[3]: selector=0x03, table=0(DC),0(AC)
Spectral selection = 0 .. 0
Successive approximation = 0x10
NOTE: Scan parsing doesn't support this SOF mode.
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00007512
Huffman table length = 30
----
Destination ID = 1
Class = 1 (AC Table)
Codes of length 01 bits (000 total):
Codes of length 02 bits (003 total): 00 01 11
Codes of length 03 bits (000 total):
Codes of length 04 bits (003 total): 10 21 60
Codes of length 05 bits (001 total): 31
Codes of length 06 bits (000 total):
Codes of length 07 bits (003 total): 20 41 80
Codes of length 08 bits (001 total): C0
Codes of length 09 bits (000 total):
Codes of length 10 bits (000 total):
Codes of length 11 bits (000 total):
Codes of length 12 bits (000 total):
Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total):
Total number of codes: 011
*** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x00007532
Scan header length = 8
Number of img components = 1
Component[1]: selector=0x03, table=0(DC),1(AC)
Spectral selection = 1 .. 63
Successive approximation = 0x10
NOTE: Scan parsing doesn't support this SOF mode.
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x000075CE
Huffman table length = 30
----
Destination ID = 1
Class = 1 (AC Table)
Codes of length 01 bits (000 total):
Codes of length 02 bits (003 total): 00 01 11
Codes of length 03 bits (001 total): 10
Codes of length 04 bits (000 total):
Codes of length 05 bits (003 total): 21 31 60
Codes of length 06 bits (001 total): 41
Codes of length 07 bits (000 total):
Codes of length 08 bits (003 total): 20 80 C0
Codes of length 09 bits (000 total):
Codes of length 10 bits (000 total):
Codes of length 11 bits (000 total):
Codes of length 12 bits (000 total):
Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total):
Total number of codes: 011
*** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x000075EE
Scan header length = 8
Number of img components = 1
Component[1]: selector=0x02, table=0(DC),1(AC)
Spectral selection = 1 .. 63
Successive approximation = 0x10
NOTE: Scan parsing doesn't support this SOF mode.
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00007676
Huffman table length = 43
----
Destination ID = 0
Class = 1 (AC Table)
Codes of length 01 bits (001 total): 01
Codes of length 02 bits (001 total): 00
Codes of length 03 bits (001 total): 11
Codes of length 04 bits (000 total):
Codes of length 05 bits (002 total): 21 31
Codes of length 06 bits (002 total): 10 41
Codes of length 07 bits (002 total): 20 51
Codes of length 08 bits (002 total): 30 61
Codes of length 09 bits (003 total): 40 50 71
Codes of length 10 bits (000 total):
Codes of length 11 bits (003 total): 60 81 91
Codes of length 12 bits (001 total): A1
Codes of length 13 bits (001 total): B1
Codes of length 14 bits (000 total):
Codes of length 15 bits (002 total): C1 D1
Codes of length 16 bits (003 total): E1 F0 F1
Total number of codes: 024
*** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x000076A3
Scan header length = 8
Number of img components = 1
Component[1]: selector=0x01, table=0(DC),0(AC)
Spectral selection = 1 .. 63
Successive approximation = 0x10
NOTE: Scan parsing doesn't support this SOF mode.
*** Marker: EOI (End of Image) (xFFD9) ***
OFFSET: 0x0000C337
*** Searching Compression Signatures ***
Signature: 0105A3D95D2D36DE9351313E30D8E945
Signature (Rotated): 013C3A43642D2E8325A76C3818B3C324
File Offset: 0 bytes
Chroma subsampling: 2x2
EXIF Make/Model: NONE
EXIF Makernotes: NONE
EXIF Software: NONE
Searching Compression Signatures: (3347 built-in, 0 user(*) )
EXIF.Make / Software EXIF.Model Quality Subsamp Match?
------------------------- ----------------------------------- ---------------- --------------
SW :[IJG Library ] [015 ]
The following IJG-based editors also match this signature:
SW :[GIMP ] [015 ]
SW :[IrfanView ] [015 ]
SW :[idImager ] [015 ]
SW :[FastStone Image Viewer ] [015 ]
SW :[NeatImage ] [015 ]
SW :[Paint.NET ] [015 ]
SW :[Photomatix ] [015 ]
SW :[XnView ] [015 ]
Based on the analysis of compression characteristics and EXIF metadata:
ASSESSMENT: Class 1 - Image is processed/edited
This may be a new software editor for the database.
If this file is processed, and editor doesn't appear in list above,
PLEASE ADD TO DATABASE with [Tools->Add Camera to DB]
|
In the .NET project I wrote a newer JPEG reader that shouldn't suffer this problem. It's currently used only in a PowerShell Cmdlet, but with some more review and testing would end up in the current |
Small update/follow up to #231
I need to check if the Huffman table used in JPEGs are the "typical" Huffman tables defined in the JPEG standard. Some devices can only display such JPEGs.
I've implemented a small directory and a reader for DHT segments. I was unsure exactly how to "translate" the data to metadata-extractor's tag-based structure, as there are no tags here in reality. A JPEG can have many Huffman tables, although the normal is 4. There can be multiple Huffman tables in one DHT segments, but some implementations write one segment per table.
At first I created one
Directory
per DHT segment, but that ended up with many Huffman directories with very little information inside for some JPEGs - so I changed it so that there's always just one Huffman directory added to a givenMetadata
instance and additional tables found are added to this. The tables themselves are stored in theHuffmanTableDirectory
instance. They are small, just a few bytes and can be retrieved.ImageIO
allows you to specify the Huffman tables to use when writing JPEGs, so this could potentially be usedful. This means that the only TAG I've been able to put at theDirectory
level is the number of Huffman tables.An alternative would be to create one
HuffmanTableDirectory
per Huffman table, and store the tables values and properties as tags. That would mean a lot ofHuffmanTableDirectories
though, so I figured it was cleaner to hide them away under oneDirectory
.Sadly I discovered
ImageIO
's Huffman table class after writing this implementation, so theImageIO
class isn't used, but they are easy to convert. I've noticed that many things implemented in metadata-extractor exists "in parallel" inImageIO
likeIIOMetadata
for example, so it might be good to be "independent" in that sense. Also, TwelveMonkeys implements their owncom.twelvemonkeys.imageio.metadata.Directory
(andcom.twelvemonkeys.imageio.plugins.jpeg.HuffmanTable
) instead of usingIIOMetadata
to make the confusion complete.I've also added
available()
toSequentialReader
to make it easier to know if there's more data to read. There's no problem to do this usingEOFException
, but I try to avoid using exceptions as "control logic".I also did some very minor changes to
JpegSegmentType
(mostly comments).Any feedback, views or thoughts are welcome.
edit: I forgot the most important part: The "typical" Huffman tables from the JPEG standard are hardcoded, so that both each separate
HuffmanTable
andHuffmanTablesDirectory
hasisTypical()
andisOptimized()
methods. This makes it easy to check what kind of Huffman tables a given JPEG has.isTypical
andisOptimized
are opposites, even though these aren't the most intuitive terms, they come from the JPEG standard itself. The standard basicly presents two scenarios for creating Huffman tables - either by using those embedded in the standard referred to as "typical" or to actually calculate tables suitable to that particular image aka "optimized". The "typical" tables are generated from a statistical average of many images and are there to aid "weak CPU" encoding devices. The trade-off is that the files get a little bit bigger, but this is usually around 5% or so and is often considered "worth it" for cameras etc.What was never intended by the standard was that some JPEG decoders should also rely on these tables, but the fact is that some implementations do this. To top the crazyness off, both the DCF (Design rule for Camera File system) standard and the DLNA (Digital Living Network Alliance) standard have managed to manifest this flawed implementation in their standards as requirements - meaning that JPEGs can only use these "typical" tables to be compliant.