FixJAIBugJPEGLS.cxx
/*=========================================================================
Program: GDCM (Grassroots DICOM). A DICOM library
Copyright (c) 2006-2011 Mathieu Malaterre
All rights reserved.
See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "gdcmReader.h"
#include "gdcmWriter.h"
#include "gdcmImageReader.h"
#include <fstream>
#include "gdcm_charls.h"
/*
* This small example should show how one can handle the famous JAI-JPEGLS bug
* It will take in as invalid DICOM/JAI-JPEG-LS and write out as Explicit Little
* Endian. One can use `gdcmconv --jpegls` to recompress properly
*
* References:
* http://charls.codeplex.com/discussions/230307?ProjectName=charls
* http://charls.codeplex.com/workitem/7297
* http://www.dcm4che.org/jira/browse/DCM-442
* http://www.dcm4che.org/jira/browse/DCMEE-1144
* http://java.net/jira/browse/JAI_IMAGEIO_CORE-183
*
* Explanation of the issue:
*
* Seems, the error is in the calculation of the default values for thresholds T1,
* T2, T3, in particular min(MAXVAL, 4095) is not applied in
*
* FACTOR = (min(MAXVAL, 4095) + 128)/256
*
* as specified in http://www.itu.int/rec/T-REC-T.87-199806-I/en .
*
*/
int main(int argc, char *argv[])
{
if( argc < 3 )
{
std::cerr << argv[0] << " input.dcm output.dcm" << std::endl;
return 1;
}
const char *filename = argv[1];
const char *outfilename = argv[2];
gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( "FixJAIBugJPEGLS" );
gdcm::ImageReader reader;
reader.SetFileName( filename );
if( !reader.Read() )
{
return 1;
}
gdcm::Image &image = reader.GetImage();
//unsigned long len = image.GetBufferLength();
const gdcm::DataElement & in =
reader.GetFile().GetDataSet().GetDataElement( gdcm::Tag(0x7fe0,0x0010) );
const gdcm::SequenceOfFragments *sf = in.GetSequenceOfFragments();
if( !sf )
{
std::cerr << "No pixel data (or not encapsulated)" << std::endl;
return 1;
}
const unsigned int *dims = image.GetDimensions();
if ( sf->GetNumberOfFragments() != dims[2] )
{
std::cerr << "Unsupported" << std::endl;
return 1;
}
// unsigned long totalLen = sf->ComputeByteLength();
std::vector<unsigned char> rgbyteOutall;
for(unsigned int i = 0; i < sf->GetNumberOfFragments(); ++i)
{
const gdcm::Fragment &frag = sf->GetFragment(i);
if( frag.IsEmpty() ) return 1;
const gdcm::ByteValue *bv = frag.GetByteValue();
if( !bv ) return 1;
unsigned long totalLen = bv->GetLength();
std::vector<char> vbuffer;
vbuffer.resize( totalLen );
char *buffer = vbuffer.data();
bv->GetBuffer(buffer, totalLen);
const unsigned char* pbyteCompressed0 = (const unsigned char*)buffer;
while( totalLen > 0 && pbyteCompressed0[totalLen-1] != 0xd9 )
{
totalLen--;
}
JlsParameters metadata;
char errorMsg[256+1]={'\0'};
if (JpegLsReadHeader(buffer, totalLen, &metadata, errorMsg) != charls::ApiResult::OK)
{
std::cerr << "Can't parse jpegls: " << errorMsg << std::endl;
return 1;
}
std::cout << metadata.width << std::endl;
std::cout << metadata.height << std::endl;
std::cout << metadata.bitsPerSample << std::endl;
gdcm::PixelFormat const & pf = image.GetPixelFormat();
std::cout << pf << std::endl;
// http://charls.codeplex.com/discussions/230307?ProjectName=charls
unsigned char marker_lse_13[] = {
0xFF, 0xF8, 0x00, 0x0D,
0x01,
0x1F, 0xFF,
0x00, 0x22, // T1 = 34
0x00, 0x83, // T2 = 131
0x02, 0x24, // T3 = 548
0x00, 0x40
};
unsigned char marker_lse_14[] = {
0xFF, 0xF8, 0x00, 0x0D,
0x01,
0x3F, 0xFF,
0x00, 0x42, // T1 = 66
0x01, 0x03, // T2 = 259
0x04, 0x44, // T3 = 1092
0x00, 0x40
};
unsigned char marker_lse_15[] = {
0xFF, 0xF8, 0x00, 0x0D,
0x01,
0x7F, 0xFF,
0x00, 0x82, // T1 = 130
0x02, 0x03, // T2 = 515
0x08, 0x84, // T3 = 2180
0x00, 0x40
};
unsigned char marker_lse_16[] = {
0xFF, 0xF8, 0x00, 0x0D,
0x01,
0xFF, 0xFF,
0x01, 0x02, // T1 = 258
0x04, 0x03, // T2 = 1027
0x11, 0x04, // T3 = 4356
0x00, 0x40
};
const unsigned char *marker_lse = nullptr;
switch( metadata.bitsPerSample )
{
case 13:
marker_lse = marker_lse_13;
break;
case 14:
marker_lse = marker_lse_14;
break;
case 15:
marker_lse = marker_lse_15;
break;
case 16:
marker_lse = marker_lse_16;
break;
}
if( !marker_lse )
{
std::cerr << "Can't handle: " << metadata.bitsPerSample << std::endl;
return 1;
}
// FIXME: One should recompute the value for 0x0F
vbuffer.insert (vbuffer.begin() + 0x0F, marker_lse, marker_lse+15);
#if 0
std::ofstream of( "/tmp/d.jls", std::ios::binary );
of.write( &vbuffer[0], vbuffer.size() );
of.close();
#endif
const char *pbyteCompressed = vbuffer.data();
size_t cbyteCompressed = vbuffer.size(); // updated length
JlsParameters params;
JpegLsReadHeader(pbyteCompressed, cbyteCompressed, &params, nullptr);
std::vector<unsigned char> rgbyteOut;
//rgbyteOut.resize( image.GetBufferLength() );
rgbyteOut.resize(params.height *params.width * ((params.bitsPerSample + 7)
/ 8) * params.components);
CharlsApiResultType result =
JpegLsDecode(rgbyteOut.data(), rgbyteOut.size(), pbyteCompressed, cbyteCompressed, &params, errorMsg );
if (result != charls::ApiResult::OK)
{
std::cerr << "Could not patch JAI-JPEGLS: " << errorMsg << std::endl;
return 1;
}
rgbyteOutall.insert( rgbyteOutall.end(), rgbyteOut.begin(), rgbyteOut.end() );
}
gdcm::DataElement pixeldata( gdcm::Tag(0x7fe0,0x0010) );
pixeldata.SetVR( gdcm::VR::OW );
pixeldata.SetByteValue( (char*)rgbyteOutall.data(), (uint32_t)rgbyteOutall.size() );
// Add the pixel data element
reader.GetFile().GetDataSet().Replace( pixeldata );
reader.GetFile().GetHeader().SetDataSetTransferSyntax(
gdcm::TransferSyntax::ExplicitVRLittleEndian);
gdcm::Writer writer;
writer.SetFileName( outfilename );
writer.SetFile( reader.GetFile() );
writer.Write();
std::cout << "Success !" << std::endl;
return 0;
}
gdcm::Bitmap::GetPixelFormat
const PixelFormat & GetPixelFormat() const
Get/Set PixelFormat.
Definition gdcmBitmap.h:107
gdcm::Bitmap::GetDimensions
const unsigned int * GetDimensions() const
Return the dimension of the pixel data, first dimension (x), then 2nd (y), then 3rd (z)....
gdcm::ByteValue
Class to represent binary value (array of bytes).
Definition gdcmByteValue.h:35
gdcm::ByteValue::GetBuffer
bool GetBuffer(char *buffer, unsigned long length) const
gdcm::ByteValue::GetLength
VL GetLength() const override
Definition gdcmByteValue.h:77
gdcm::DataElement
Class to represent a Data Element either Implicit or Explicit.
Definition gdcmDataElement.h:59
gdcm::DataElement::GetByteValue
const ByteValue * GetByteValue() const
Definition gdcmDataElement.h:133
gdcm::DataElement::IsEmpty
bool IsEmpty() const
Check if Data Element is empty.
Definition gdcmDataElement.h:106
gdcm::DataElement::GetSequenceOfFragments
const SequenceOfFragments * GetSequenceOfFragments() const
gdcm::DataElement::SetByteValue
void SetByteValue(const char *array, VL length)
Definition gdcmDataElement.h:126
gdcm::DataElement::SetVR
void SetVR(VR const &vr)
Definition gdcmDataElement.h:88
gdcm::DataSet::GetDataElement
const DataElement & GetDataElement(const Tag &t) const
Definition gdcmDataSet.h:188
gdcm::DataSet::Replace
void Replace(const DataElement &de)
Replace a dataelement with another one.
Definition gdcmDataSet.h:150
gdcm::FileMetaInformation::SetDataSetTransferSyntax
void SetDataSetTransferSyntax(const TransferSyntax &ts)
gdcm::FileMetaInformation::SetSourceApplicationEntityTitle
static void SetSourceApplicationEntityTitle(const char *title)
gdcm::File::GetDataSet
const DataSet & GetDataSet() const
Get Data Set.
Definition gdcmFile.h:57
gdcm::File::GetHeader
const FileMetaInformation & GetHeader() const
Get File Meta Information.
Definition gdcmFile.h:48
gdcm::Fragment
Class to represent a Fragment.
Definition gdcmFragment.h:44
gdcm::ImageReader
ImageReader.
Definition gdcmImageReader.h:34
gdcm::ImageReader::GetImage
const Image & GetImage() const
Return the read image.
gdcm::ImageReader::Read
bool Read() override
gdcm::Image
Image.
Definition gdcmImage.h:47
gdcm::PixelFormat
PixelFormat.
Definition gdcmPixelFormat.h:46
gdcm::Reader::GetFile
const File & GetFile() const
Set/Get File.
Definition gdcmReader.h:72
gdcm::Reader::SetFileName
void SetFileName(const char *filename_native)
gdcm::SequenceOfFragments
Class to represent a Sequence Of Fragments.
Definition gdcmSequenceOfFragments.h:32
gdcm::SequenceOfFragments::GetFragment
const Fragment & GetFragment(SizeType num) const
gdcm::SequenceOfFragments::GetNumberOfFragments
SizeType GetNumberOfFragments() const
gdcm::Tag
Class to represent a DICOM Data Element (Attribute) Tag (Group, Element).
Definition gdcmTag.h:39
gdcm::TransferSyntax::ExplicitVRLittleEndian
@ ExplicitVRLittleEndian
Definition gdcmTransferSyntax.h:64
gdcm::VR::OW
@ OW
Definition gdcmVR.h:77
gdcm::Writer
Writer ala DOM (Document Object Model).
Definition gdcmWriter.h:49
gdcm::Writer::Write
virtual bool Write()
Main function to tell the writer to write.
gdcm::Writer::SetFile
void SetFile(const File &f)
Set/Get the DICOM file (DataSet + Header).
Definition gdcmWriter.h:66
gdcm::Writer::SetFileName
void SetFileName(const char *filename_native)
Set the filename of DICOM file to write:
gdcmImageReader.h
gdcmReader.h
gdcmWriter.h