📄 gdcmpixelreadconvert.cxx
字号:
// just the color space is YCbCr instead of RGB. This is particularly useful
// for doppler ultrasound where most of the image is grayscale
// (i.e. only populates the Y components) and Cb and Cr are mostly zero,
// except for the few patches of color on the image.
// On such images, RLE achieves a compression ratio that is much better
// than the compression ratio on an equivalent RGB image.
gdcmWarningMacro("--> ConvertYcBcRPlanesToRGBPixels");
uint8_t *localRaw = Raw;
uint8_t *copyRaw = new uint8_t[ RawSize ];
memmove( copyRaw, localRaw, RawSize );
// to see the tricks about YBR_FULL, YBR_FULL_422,
// YBR_PARTIAL_422, YBR_ICT, YBR_RCT have a look at :
// ftp://medical.nema.org/medical/dicom/final/sup61_ft.pdf
// and be *very* affraid
//
int l = XSize * YSize;
int nbFrames = ZSize;
uint8_t *a = copyRaw + 0;
uint8_t *b = copyRaw + l;
uint8_t *c = copyRaw + l+ l;
int32_t R, G, B;
/// We replaced easy to understand but time consuming floating point
/// computations by the 'well known' integer computation counterpart
/// Refer to :
/// http://lestourtereaux.free.fr/papers/data/yuvrgb.pdf
/// for code optimisation.
for ( int i = 0; i < nbFrames; i++ )
{
for ( int j = 0; j < l; j++ )
{
R = 38142 *(*a-16) + 52298 *(*c -128);
G = 38142 *(*a-16) - 26640 *(*c -128) - 12845 *(*b -128);
B = 38142 *(*a-16) + 66093 *(*b -128);
R = (R+16384)>>15;
G = (G+16384)>>15;
B = (B+16384)>>15;
if (R < 0) R = 0;
if (G < 0) G = 0;
if (B < 0) B = 0;
if (R > 255) R = 255;
if (G > 255) G = 255;
if (B > 255) B = 255;
*(localRaw++) = (uint8_t)R;
*(localRaw++) = (uint8_t)G;
*(localRaw++) = (uint8_t)B;
a++;
b++;
c++;
}
}
delete[] copyRaw;
}
/// \brief Deals with the color decoding i.e. handle:
/// - R, G, B planes (as opposed to RGB pixels)
/// - YBR (various) encodings.
/// - LUT[s] (or "PALETTE COLOR").
void PixelReadConvert::ConvertHandleColor()
{
//////////////////////////////////
// Deal with the color decoding i.e. handle:
// - R, G, B planes (as opposed to RGB pixels)
// - YBR (various) encodings.
// - LUT[s] (or "PALETTE COLOR").
//
// The classification in the color decoding schema is based on the blending
// of two Dicom tags values:
// * "Photometric Interpretation" for which we have the cases:
// - [Photo A] MONOCHROME[1|2] pictures,
// - [Photo B] RGB or YBR_FULL_422 (which acts as RGB),
// - [Photo C] YBR_* (with the above exception of YBR_FULL_422)
// - [Photo D] "PALETTE COLOR" which indicates the presence of LUT[s].
// * "Planar Configuration" for which we have the cases:
// - [Planar 0] 0 then Pixels are already RGB
// - [Planar 1] 1 then we have 3 planes : R, G, B,
// - [Planar 2] 2 then we have 1 gray Plane and 3 LUTs
//
// Now in theory, one could expect some coherence when blending the above
// cases. For example we should not encounter files belonging at the
// time to case [Planar 0] and case [Photo D].
// Alas, this was only theory ! Because in practice some odd (read ill
// formated Dicom) files (e.g. gdcmData/US-PAL-8-10x-echo.dcm) we encounter:
// - "Planar Configuration" = 0,
// - "Photometric Interpretation" = "PALETTE COLOR".
// Hence gdcm will use the folowing "heuristic" in order to be tolerant
// towards Dicom-non-conformant files:
// << whatever the "Planar Configuration" value might be, a
// "Photometric Interpretation" set to "PALETTE COLOR" forces
// a LUT intervention >>
//
// Now we are left with the following handling of the cases:
// - [Planar 0] OR [Photo A] no color decoding (since respectively
// Pixels are already RGB and monochrome pictures have no color :),
// - [Planar 1] AND [Photo B] handled with ConvertRGBPlanesToRGBPixels()
// - [Planar 1] AND [Photo C] handled with ConvertYcBcRPlanesToRGBPixels()
// - [Planar 2] OR [Photo D] requires LUT intervention.
gdcmDebugMacro("--> ConvertHandleColor "
<< "Planar Configuration " << PlanarConfiguration );
if ( ! IsRawRGB() )
{
// [Planar 2] OR [Photo D]: LUT intervention done outside
gdcmDebugMacro("--> RawRGB : LUT intervention done outside");
return;
}
if ( PlanarConfiguration == 1 )
{
if ( IsYBRFull )
{
// [Planar 1] AND [Photo C] (remember YBR_FULL_422 acts as RGB)
gdcmDebugMacro("--> YBRFull");
ConvertYcBcRPlanesToRGBPixels();
}
else
{
// [Planar 1] AND [Photo C]
gdcmDebugMacro("--> YBRFull");
ConvertRGBPlanesToRGBPixels();
}
return;
}
// When planarConf is 0, and RLELossless (forbidden by Dicom norm)
// pixels need to be RGB-fyied anyway
if (IsRLELossless)
{
gdcmDebugMacro("--> RLE Lossless");
ConvertRGBPlanesToRGBPixels();
}
// In *normal *case, when planarConf is 0, pixels are already in RGB
}
/// Computes the Pixels Size
void PixelReadConvert::ComputeRawAndRGBSizes()
{
int bitsAllocated = BitsAllocated;
// Number of "Bits Allocated" is fixed to 16 when it's 12, since
// in this case we will expand the image to 16 bits (see
// \ref ReadAndDecompress12BitsTo16Bits() )
if ( BitsAllocated == 12 )
{
bitsAllocated = 16;
}
RawSize = XSize * YSize * ZSize
* ( bitsAllocated / 8 )
* SamplesPerPixel;
if ( HasLUT )
{
RGBSize = 3 * RawSize; // works for 8 and 16 bits per Pixel
}
else
{
RGBSize = RawSize;
}
}
/// Allocates room for RGB Pixels
void PixelReadConvert::AllocateRGB()
{
if ( RGB )
delete [] RGB;
RGB = new uint8_t[RGBSize];
}
/// Allocates room for RAW Pixels
void PixelReadConvert::AllocateRaw()
{
if ( Raw )
delete [] Raw;
Raw = new uint8_t[RawSize];
}
//-----------------------------------------------------------------------------
// Print
/**
* \brief Print self.
* @param indent Indentation string to be prepended during printing.
* @param os Stream to print to.
*/
void PixelReadConvert::Print( std::ostream &os, std::string const &indent )
{
os << indent
<< "--- Pixel information -------------------------"
<< std::endl;
os << indent
<< "Pixel Data: offset " << PixelOffset
<< " x(" << std::hex << PixelOffset << std::dec
<< ") length " << PixelDataLength
<< " x(" << std::hex << PixelDataLength << std::dec
<< ")" << std::endl;
if ( IsRLELossless )
{
if ( RLEInfo )
{
RLEInfo->Print( os, indent );
}
else
{
gdcmWarningMacro("Set as RLE file but NO RLEinfo present.");
}
}
if ( IsJPEG2000 || IsJPEGLossless || IsJPEGLossy || IsJPEGLS )
{
if ( JPEGInfo )
{
JPEGInfo->Print( os, indent );
}
else
{
gdcmWarningMacro("Set as JPEG file but NO JPEGinfo present.");
}
}
}
//-----------------------------------------------------------------------------
} // end namespace gdcm
// Note to developpers :
// Here is a very detailled post from David Clunie, on the troubles caused
// 'non standard' LUT and LUT description
// We shall have to take it into accound in our code.
// Some day ...
/*
Subject: Problem with VOI LUTs in Agfa and Fuji CR and GE DX images, was Re: VOI LUT issues
Date: Sun, 06 Feb 2005 17:13:40 GMT
From: David Clunie <dclunie@dclunie.com>
Reply-To: dclunie@dclunie.com
Newsgroups: comp.protocols.dicom
References: <1107553502.040221.189550@o13g2000cwo.googlegroups.com>
> THE LUT that comes with [my] image claims to be 16-bit, but none of the
> values goes higher than 4095. That being said, though, none of my
> original pixel values goes higher than that, either. I have read
> elsewhere on this group that when that happens you are supposed to
> adjust the LUT. Can someone be more specific? There was a thread from
> 2002 where Marco and David were mentioning doing precisely that.
>
> Thanks
>
> -carlos rodriguez
You have encountered the well known "we know what the standard says but
we are going to ignore it and do what we have been doing for almost
a decade regardless" CR vendor bug. Agfa started this, but they are not
the only vendor doing this now; GE and Fuji may have joined the club.
Sadly, one needs to look at the LUT Data, figure out what the maximum
value actually encoded is, and find the next highest power of 2 (e.g.
212 in this case), to figure out what the range of the data is
supposed to be. I have assumed that if the maximum value in the LUT
data is less than a power of 2 minus 1 (e.g. 0xebc) then the intent
of the vendor was not to use the maximum available grayscale range
of the display (e.g. the maximum is 0xfff in this case). An alternative
would be to scale to the actual maximum rather than a power of two.
Very irritating, and in theory not totally reliable if one really
intended the full 16 bits and only used, say 15, but that is extremely
unlikely since everything would be too dark, and this heuristic
seems to work OK.
There has never been anything in the standard that describes having
to go through these convolutions. Since the only value in the
standard that describes the bit depth of the LUT values is LUT
Descriptor value 3 and that is (usually) always required to be
either 8 or 16, it mystifies me how the creators' of these images
imagine that the receiver is going to divine the range that is intended. Further, the standard is quite explicit that this 3rd
value defines the range of LUT values, but as far as I am aware, all
the vendors are ignoring the standard and indeed sending a third value
of 16 in all cases.
This problem is not confined to CR, and is also seen with DX products.
Typically I have seen:
- Agfa CR, which usually (always ?) sends LUTs, values up to 0x0fff
- Fuji CR, which occasionally send LUTs, values up to 0x03ff
- GE DX, for presentation, which always have LUTs, up to 0x3fff
Swissray, Siemens, Philips, Canon and Kodak never seem to send VOI LUTs
at this point (which is a whole other problem). Note that the presence
or absence of a VOI LUT as opposed to window values may be configurable
on the modality in some cases, and I have just looked at what I happen
to have received from a myriad of sites over whose configuration I have
no control. This may be why the majority of Fuji images have no VOI LUTs,
but a few do (or it may be the Siemens system that these Fuji images went
through that perhaps added it). I do have some test Hologic DX images that
are not from a clinical site that do actually get this right (a value
of 12 for the third value and a max of 0xfff).
Since almost every vendor that I have encountered that encodes LUTs
makes this mistake, perhaps it is time to amend the standard to warn
implementor's of receivers and/or sanction this bad behavior. We have
talked about this in the past in WG 6 but so far everyone has been
reluctant to write into the standard such a comment. Maybe it is time
to try again, since if one is not aware of this problem, one cannot
effectively implement display using VOI LUTs, and there is a vast
installed base to contend with.
I did not check presentation states, in which VOI LUTs could also be
encountered, for the prevalence of this mistake, nor did I look at the
encoding of Modality LUT's, which are unusual. Nor did I check digital
mammography images. I would be interested to hear from anyone who has.
David
PS. The following older thread in this newsgroup discusses this:
"http://groups-beta.google.com/group/comp.protocols.dicom/browse_frm/t hread/6a033444802a35fc/0f0a9a1e35c1468e?q=voi+lut&_done=%2Fgroup%2Fcom p.protocols.dicom%2Fsearch%3Fgroup%3Dcomp.protocols.dicom%26q%3Dvoi+lu t%26qt_g%3D1%26searchnow%3DSearch+this+group%26&_doneTitle=Back+to+Sea rch&&d#0f0a9a1e35c1468e"
PPS. From a historical perspective, the following may be of interest.
In the original standard in 1993, all that was said about this was a
reference to the corresponding such where Modality LUTs are described
that said:
"The third value specifies the number of bits for each entry in the
LUT Data. It shall take the value 8 or 16. The LUT Data shall be stored
in a format equivalent to 8 or 16 bits allocated and high bit equal
1-bits allocated."
Since the high bit hint was not apparently explicit enough, a very
early CP, CP 15 (submitted by Agfa as it happens), replaced this with:
"The third value conveys the range of LUT entry values. It shall take
the value 8 or 16, corresponding with the LUT entry value range of
256 or 65536.
Note: The third value is not required for describing the
LUT data and is only included for informational usage
and for maintaining compatibility with ACRNEMA 2.0.
The LUT Data contains the LUT entry values."
That is how it read in the 1996, 1998 and 1999 editions.
By the 2000 edition, Supplement 33 that introduced presentation states
extensively reworked this entire section and tried to explain this in
different words:
"The output range is from 0 to 2^n-1 where n is the third value of LUT
Descriptor. This range is always unsigned."
and also added a note to spell out what the output range meant in the
VOI LUT section:
"9. The output of the Window Center/Width or VOI LUT transformation
is either implicitly scaled to the full range of the display device
if there is no succeeding transformation defined, or implicitly scaled
to the full input range of the succeeding transformation step (such as
the Presentation LUT), if present. See C.11.6.1."
It still reads this way in the 2004 edition.
Note that LUTs in other applications than the general VOI LUT allow for
values other than 8 or 16 in the third value of LUT descriptor to permit
ranges other than 0 to 255 or 65535.
In addition, the DX Image Module specializes the VOI LUT
attributes as follows, in PS 3.3 section C.8.11.3.1.5 (added in Sup 32):
"The third value specifies the number of bits for each entry in the LUT
Data (analogous to 靊its stored
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -