Dear DICOM/WSI community,
AFAIK, Aperio/Leica scanners can produce SVS files where the compression schemes are declared as follows:
I tried using com.pixelmed.convert.TIFFToDicom to convert this into a DICOM Whole Slide Image (WSI). The tool produces a DICOM file utilizing the JPEG 2000 Transfer Syntax along with a Photometric Interpretation of YBR_ICT.
I have been re-reading DICOM PS3.5 §8.2.4 (JPEG 2000 Image Compression), but I fail to see how YBR_ICT could apply here. I would greatly appreciate it if someone could clarify whether YBR_ICT actually applies to the J2K codestream found within these Aperio SVS files.
Here is what I see on my side:
$ tiffdump
openslide.cs.cmu.edu/download/openslide-testdata/Aperio/JP2K-33003-1.svs | grep Tile | head -5
TileWidth (322) SHORT (3) 1<256>
TileLength (323) SHORT (3) 1<256>
TileOffsets (324) LONG (4) 4209<16 782918 1498738 2144038 2856649 3717870 4522504 5254867 6126701 7106199 8089728 9092193 10233942 11401633 12529818 13598031 14730020 15788367 16744281 17694061 18711546 19676671 20580385 21515662 ...>
TileByteCounts (325) LONG (4) 4209<10965 8983 7184 8512 10641 8979 7802 9329 10401 8921 7776 10293 10250 9229 8545 11299 10733 9329 9534 11329 10077 9011 10144 11258 ...>
TileWidth (322) SHORT (3) 1<256>
Extracting the first tile is straightforward:
However, examining this codestream (1) & (2) does not yield what I expected for an irreversible color transform. For a true YBR_ICT stream, I would expect to see the multiple-component transformation flag active:
$ jpegdump < ybr_ict.j2k
[...]
MultipleComponentTransformation = 0x1 (Part 1 Annex G2 transformation of components 0,1,2)
Furthermore, based on the output of opj_dump, it appears that first.j2k is using some form of chroma subsampling (where dx and dy values vary across components). For a standard YBR_ICT transformation, I would instead expect a uniform 4:4:4 sampling grid where all components match:
$ opj_dump -i /tmp/o.j2k
[...]
numcomps=3
component 0 {
dx=1, dy=1
Could anyone clarify if PixelMed's assignment of YBR_ICT is correct for these specific Aperio J2K (0x80eb) streams, or if they should be interpreted differently?
Thank you for your time and insights,
(1)
$ jpegdump < first.j2k
Offset 0x0000 Marker 0xff4f SOC Start of codestream
Offset 0x0002 Marker 0xff51 SIZ Image and tile size length variable 0x2f
Offset 0x0033 Marker 0xff52 COD Coding style default length variable 0x0c
JPEG_COD_Parameters:
Scod = 0x0
Precinct size PPx = 15 and PPy = 15
SOPMarkerSegments = not used
EPHPMarkerSegments = not used
ProgressionOrder = 0x0 (Layer-resolution level-component-position progression)
NumberOfLayers = 1
MultipleComponentTransformation = 0x0 (None)
NumberOfDecompositionLevels = 5
CodeBlockWidth = 0x4
CodeBlockHeight = 0x4
CodeBlockStyle = 0x0
No selective arithmetic coding bypass
No reset of context probabilities on coding pass boundaries
No termination on each coding pass
No vertically causal context
No predictable termination
No segmentation symbols are used
WaveletTransformation = 0x0 (9-7 irreversible filter)
Offset 0x0041 Marker 0xff5c QCD Quantization default length variable 0x23
Offset 0x0066 Marker 0xff64 COM Comment (JPEG 2000) length variable 0x08
Offset 0x0070 Marker 0xff90 SOT Start of tile-part length variable 0x0a
Offset 0x007c Marker 0xff93 SOD Start of data
Offset 0x2ad3 Marker 0xffd9 EOI End of Image (JPEG 2000 EOC End of codestream)
End of file
(2)
$ opj_dump -i first.j2k
[INFO] Start to read j2k main header (0).
[INFO] Main header has been correctly decoded.
Image info {
x0=0, y0=0
x1=256, y1=256
numcomps=3
component 0 {
dx=1, dy=1
prec=8
sgnd=0
}
component 1 {
dx=2, dy=1
prec=8
sgnd=0
}
component 2 {
dx=2, dy=1
prec=8
sgnd=0
}
}
Codestream info from main header: {
tx0=0, ty0=0
tdx=256, tdy=256
tw=1, th=1
default tile {
csty=0
prg=0
numlayers=1
mct=0
comp 0 {
csty=0
numresolutions=6
cblkw=2^6
cblkh=2^6
cblksty=0
qmfbid=0
preccintsize (w,h)=(15,15) (15,15) (15,15) (15,15) (15,15) (15,15)
qntsty=2
numgbits=1
stepsizes (m,e)=(460,11) (464,10) (464,10) (434,9) (478,9) (478,9) (459,8) (528,8) (528,8) (544,7) (651,7) (651,7) (738,6) (616,6) (616,6) (542,5)
roishift=0
}
comp 1 {
csty=0
numresolutions=6
cblkw=2^6
cblkh=2^6
cblksty=0
qmfbid=0
preccintsize (w,h)=(15,15) (15,15) (15,15) (15,15) (15,15) (15,15)
qntsty=2
numgbits=1
stepsizes (m,e)=(460,11) (464,10) (464,10) (434,9) (478,9) (478,9) (459,8) (528,8) (528,8) (544,7) (651,7) (651,7) (738,6) (616,6) (616,6) (542,5)
roishift=0
}
comp 2 {
csty=0
numresolutions=6
cblkw=2^6
cblkh=2^6
cblksty=0
qmfbid=0
preccintsize (w,h)=(15,15) (15,15) (15,15) (15,15) (15,15) (15,15)
qntsty=2
numgbits=1
stepsizes (m,e)=(460,11) (464,10) (464,10) (434,9) (478,9) (478,9) (459,8) (528,8) (528,8) (544,7) (651,7) (651,7) (738,6) (616,6) (616,6) (542,5)
roishift=0
}
}
}
Codestream index from main header: {
Main header start position=0
Main header end position=112
Marker list: {
type=0xff4f, pos=0, len=2
type=0xff51, pos=2, len=49
type=0xff52, pos=51, len=14
type=0xff5c, pos=65, len=37
type=0xff64, pos=102, len=10
}
}