Skip to content

Commit 3f1c5cc

Browse files
Added progression order verification test
1 parent f2e035c commit 3f1c5cc

2 files changed

Lines changed: 98 additions & 122 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ DCMTK-HTJ2K provides HTJ2K codec support for [DCMTK](https://github.com/DCMTK/dc
1717
- **HTJ2K Encoding**: Compress DICOM images using HTJ2K lossless and lossy compression.
1818
- **HTJ2K Decoding**: Decompress HTJ2K-encoded DICOM images.
1919
- **DCMTK Integration**: Seamless integration with DCMTK codec framework.
20-
- **Configurable Parameters**: Support for codeblock dimensions, fragment sizes, and encoding options.
20+
- **Configurable Parameters**: Support for codeblock dimensions, progression order, number of decompositions, fragment sizes, and encoding options.
2121

2222
## Dependencies
2323

tests/test.cc

Lines changed: 97 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <gtest/gtest.h>
66

7+
#include <fstream>
78
#include <vector>
89

910
#include "dcmtk/dcmdata/dcdeftag.h"
@@ -23,6 +24,83 @@ struct TestLogConfig {
2324

2425
static TestLogConfig g_testLogConfig;
2526

27+
void PopulateDatasetWithRequiredAttributes(
28+
DcmDataset *dataset, Uint16 rows, Uint16 cols, Uint16 bitsAllocated,
29+
Uint16 samplesPerPixel, OFString const &photometricInterpretation,
30+
Uint16 pixelRepresentation) {
31+
// Required image attributes
32+
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_Rows, rows).good());
33+
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_Columns, cols).good());
34+
ASSERT_TRUE(
35+
dataset->putAndInsertUint16(DCM_SamplesPerPixel, samplesPerPixel).good());
36+
ASSERT_TRUE(dataset
37+
->putAndInsertString(DCM_PhotometricInterpretation,
38+
photometricInterpretation.c_str())
39+
.good());
40+
ASSERT_TRUE(
41+
dataset->putAndInsertUint16(DCM_BitsAllocated, bitsAllocated).good());
42+
ASSERT_TRUE(
43+
dataset->putAndInsertUint16(DCM_BitsStored, bitsAllocated).good());
44+
ASSERT_TRUE(
45+
dataset->putAndInsertUint16(DCM_HighBit, bitsAllocated - 1).good());
46+
ASSERT_TRUE(
47+
dataset->putAndInsertUint16(DCM_PixelRepresentation, pixelRepresentation)
48+
.good());
49+
50+
// Minimal required UIDs
51+
char studyUid[100];
52+
char seriesUid[100];
53+
char instanceUid[100];
54+
dcmGenerateUniqueIdentifier(studyUid, SITE_STUDY_UID_ROOT);
55+
dcmGenerateUniqueIdentifier(seriesUid, SITE_SERIES_UID_ROOT);
56+
dcmGenerateUniqueIdentifier(instanceUid, SITE_INSTANCE_UID_ROOT);
57+
58+
ASSERT_TRUE(dataset
59+
->putAndInsertString(DCM_SOPClassUID,
60+
UID_SecondaryCaptureImageStorage)
61+
.good());
62+
ASSERT_TRUE(
63+
dataset->putAndInsertString(DCM_StudyInstanceUID, studyUid).good());
64+
ASSERT_TRUE(
65+
dataset->putAndInsertString(DCM_SeriesInstanceUID, seriesUid).good());
66+
ASSERT_TRUE(
67+
dataset->putAndInsertString(DCM_SOPInstanceUID, instanceUid).good());
68+
}
69+
70+
void VerifyProgressionOrder(OFTempFile const &tmpFile,
71+
unsigned char const expectedProgressionOrder) {
72+
// Open temp file in a buffer
73+
std::ifstream input(tmpFile.getFilename(), std::ios::binary);
74+
// Scan for JPEG 2000 COD marker (0xFF52)
75+
bool foundCodMarker = false;
76+
char byte1, byte2;
77+
if (input.get(byte1)) {
78+
while (input.get(byte2)) {
79+
if (static_cast<unsigned char>(byte1) == 0xFF &&
80+
static_cast<unsigned char>(byte2) == 0x52) {
81+
foundCodMarker = true;
82+
break;
83+
}
84+
byte1 = byte2;
85+
}
86+
}
87+
ASSERT_TRUE(foundCodMarker);
88+
// Get COD marker length (2 bytes after COD marker)
89+
char codLength[2];
90+
// COD segment length
91+
ASSERT_TRUE(input.read(codLength, 2));
92+
// COD coding style default (1 byte after COD marker)
93+
char codingStyleDefault;
94+
ASSERT_TRUE(input.read(&codingStyleDefault, 1));
95+
// COD progression order (1 byte after coding style default)
96+
char progressionOrder;
97+
ASSERT_TRUE(input.read(&progressionOrder, 1));
98+
// Verify progression order
99+
ASSERT_TRUE(progressionOrder == expectedProgressionOrder);
100+
101+
input.close();
102+
}
103+
26104
TEST(BasicTest, FrameworkWorks) {
27105
EXPECT_EQ(1 + 1, 2);
28106
EXPECT_TRUE(true);
@@ -59,36 +137,9 @@ TEST(CodecTest, BasicMonochrome8BitUnsignedCompressDecompressLossless) {
59137
DcmFileFormat fileformat;
60138
DcmDataset *dataset = fileformat.getDataset();
61139

62-
// Required image attributes
63-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_Rows, rows).good());
64-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_Columns, cols).good());
65-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_SamplesPerPixel, 1).good());
66-
ASSERT_TRUE(
67-
dataset->putAndInsertString(DCM_PhotometricInterpretation, "MONOCHROME2")
68-
.good());
69-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_BitsAllocated, 8).good());
70-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_BitsStored, 8).good());
71-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_HighBit, 7).good());
72-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_PixelRepresentation, 0).good());
73-
74-
// Minimal required UIDs
75-
char studyUid[100];
76-
char seriesUid[100];
77-
char instanceUid[100];
78-
dcmGenerateUniqueIdentifier(studyUid, SITE_STUDY_UID_ROOT);
79-
dcmGenerateUniqueIdentifier(seriesUid, SITE_SERIES_UID_ROOT);
80-
dcmGenerateUniqueIdentifier(instanceUid, SITE_INSTANCE_UID_ROOT);
81-
82-
ASSERT_TRUE(dataset
83-
->putAndInsertString(DCM_SOPClassUID,
84-
UID_SecondaryCaptureImageStorage)
85-
.good());
86-
ASSERT_TRUE(
87-
dataset->putAndInsertString(DCM_StudyInstanceUID, studyUid).good());
88-
ASSERT_TRUE(
89-
dataset->putAndInsertString(DCM_SeriesInstanceUID, seriesUid).good());
90-
ASSERT_TRUE(
91-
dataset->putAndInsertString(DCM_SOPInstanceUID, instanceUid).good());
140+
// Populate dataset for an 8-bit monochrome image
141+
PopulateDatasetWithRequiredAttributes(dataset, rows, cols, 8, 1,
142+
"MONOCHROME2", 0);
92143

93144
// Pixel data
94145
ASSERT_TRUE(
@@ -101,7 +152,8 @@ TEST(CodecTest, BasicMonochrome8BitUnsignedCompressDecompressLossless) {
101152
HtJ2kEncoderRegistration::registerCodecs();
102153
HtJ2kDecoderRegistration::registerCodecs();
103154

104-
const E_TransferSyntax htj2kLossless = EXS_HighThroughputJPEG2000LosslessOnly;
155+
const E_TransferSyntax htj2kLossless =
156+
EXS_HighThroughputJPEG2000withRPCLOptionsLosslessOnly;
105157
ASSERT_TRUE(dataset->chooseRepresentation(htj2kLossless, nullptr).good());
106158
ASSERT_TRUE(dataset->canWriteXfer(htj2kLossless));
107159

@@ -111,6 +163,9 @@ TEST(CodecTest, BasicMonochrome8BitUnsignedCompressDecompressLossless) {
111163
ASSERT_TRUE(
112164
fileformat.saveFile(tempFile.getFilename(), htj2kLossless).good());
113165

166+
// RPCL progression order [0x02]
167+
VerifyProgressionOrder(tempFile, 0x02);
168+
114169
// Read back
115170
DcmFileFormat readFile;
116171
ASSERT_TRUE(readFile.loadFile(tempFile.getFilename()).good());
@@ -156,36 +211,9 @@ TEST(CodecTest, BasicMonochrome16BitUnsignedCompressDecompressLossless) {
156211
DcmFileFormat fileformat;
157212
DcmDataset *dataset = fileformat.getDataset();
158213

159-
// Required image attributes
160-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_Rows, rows).good());
161-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_Columns, cols).good());
162-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_SamplesPerPixel, 1).good());
163-
ASSERT_TRUE(
164-
dataset->putAndInsertString(DCM_PhotometricInterpretation, "MONOCHROME2")
165-
.good());
166-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_BitsAllocated, 16).good());
167-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_BitsStored, 16).good());
168-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_HighBit, 15).good());
169-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_PixelRepresentation, 0).good());
170-
171-
// Minimal required UIDs
172-
char studyUid[100];
173-
char seriesUid[100];
174-
char instanceUid[100];
175-
dcmGenerateUniqueIdentifier(studyUid, SITE_STUDY_UID_ROOT);
176-
dcmGenerateUniqueIdentifier(seriesUid, SITE_SERIES_UID_ROOT);
177-
dcmGenerateUniqueIdentifier(instanceUid, SITE_INSTANCE_UID_ROOT);
178-
179-
ASSERT_TRUE(dataset
180-
->putAndInsertString(DCM_SOPClassUID,
181-
UID_SecondaryCaptureImageStorage)
182-
.good());
183-
ASSERT_TRUE(
184-
dataset->putAndInsertString(DCM_StudyInstanceUID, studyUid).good());
185-
ASSERT_TRUE(
186-
dataset->putAndInsertString(DCM_SeriesInstanceUID, seriesUid).good());
187-
ASSERT_TRUE(
188-
dataset->putAndInsertString(DCM_SOPInstanceUID, instanceUid).good());
214+
// Populate dataset for an 16-bit unsigned monochrome image
215+
PopulateDatasetWithRequiredAttributes(dataset, rows, cols, 16, 1,
216+
"MONOCHROME2", 0);
189217

190218
// Pixel data
191219
ASSERT_TRUE(
@@ -208,6 +236,9 @@ TEST(CodecTest, BasicMonochrome16BitUnsignedCompressDecompressLossless) {
208236
ASSERT_TRUE(
209237
fileformat.saveFile(tempFile.getFilename(), htj2kLossless).good());
210238

239+
// Default LRCP progression order [0x00]
240+
VerifyProgressionOrder(tempFile, 0x00);
241+
211242
// Read back
212243
DcmFileFormat readFile;
213244
ASSERT_TRUE(readFile.loadFile(tempFile.getFilename()).good());
@@ -257,36 +288,9 @@ TEST(CodecTest, BasicMonochrome16BitSignedCompressDecompressLossless) {
257288
DcmFileFormat fileformat;
258289
DcmDataset *dataset = fileformat.getDataset();
259290

260-
// Required image attributes
261-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_Rows, rows).good());
262-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_Columns, cols).good());
263-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_SamplesPerPixel, 1).good());
264-
ASSERT_TRUE(
265-
dataset->putAndInsertString(DCM_PhotometricInterpretation, "MONOCHROME2")
266-
.good());
267-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_BitsAllocated, 16).good());
268-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_BitsStored, 16).good());
269-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_HighBit, 15).good());
270-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_PixelRepresentation, 1).good());
271-
272-
// Minimal required UIDs
273-
char studyUid[100];
274-
char seriesUid[100];
275-
char instanceUid[100];
276-
dcmGenerateUniqueIdentifier(studyUid, SITE_STUDY_UID_ROOT);
277-
dcmGenerateUniqueIdentifier(seriesUid, SITE_SERIES_UID_ROOT);
278-
dcmGenerateUniqueIdentifier(instanceUid, SITE_INSTANCE_UID_ROOT);
279-
280-
ASSERT_TRUE(dataset
281-
->putAndInsertString(DCM_SOPClassUID,
282-
UID_SecondaryCaptureImageStorage)
283-
.good());
284-
ASSERT_TRUE(
285-
dataset->putAndInsertString(DCM_StudyInstanceUID, studyUid).good());
286-
ASSERT_TRUE(
287-
dataset->putAndInsertString(DCM_SeriesInstanceUID, seriesUid).good());
288-
ASSERT_TRUE(
289-
dataset->putAndInsertString(DCM_SOPInstanceUID, instanceUid).good());
291+
// Populate dataset for an 16-bit signed monochrome image
292+
PopulateDatasetWithRequiredAttributes(dataset, rows, cols, 16, 1,
293+
"MONOCHROME2", 1);
290294

291295
// Pixel data
292296
ASSERT_TRUE(
@@ -358,37 +362,9 @@ TEST(CodecTest, BasicColorCompressDecompressLossless) {
358362
DcmFileFormat fileformat;
359363
DcmDataset *dataset = fileformat.getDataset();
360364

361-
// Required image attributes
362-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_Rows, rows).good());
363-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_Columns, cols).good());
364-
ASSERT_TRUE(
365-
dataset->putAndInsertUint16(DCM_SamplesPerPixel, samplesPerPixel).good());
366-
ASSERT_TRUE(
367-
dataset->putAndInsertString(DCM_PhotometricInterpretation, "RGB").good());
365+
// Populate dataset for an 8-bit color image
366+
PopulateDatasetWithRequiredAttributes(dataset, rows, cols, 8, 3, "RGB", 0);
368367
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_PlanarConfiguration, 0).good());
369-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_BitsAllocated, 8).good());
370-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_BitsStored, 8).good());
371-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_HighBit, 7).good());
372-
ASSERT_TRUE(dataset->putAndInsertUint16(DCM_PixelRepresentation, 0).good());
373-
374-
// Minimal required UIDs
375-
char studyUid[100];
376-
char seriesUid[100];
377-
char instanceUid[100];
378-
dcmGenerateUniqueIdentifier(studyUid, SITE_STUDY_UID_ROOT);
379-
dcmGenerateUniqueIdentifier(seriesUid, SITE_SERIES_UID_ROOT);
380-
dcmGenerateUniqueIdentifier(instanceUid, SITE_INSTANCE_UID_ROOT);
381-
382-
ASSERT_TRUE(dataset
383-
->putAndInsertString(DCM_SOPClassUID,
384-
UID_SecondaryCaptureImageStorage)
385-
.good());
386-
ASSERT_TRUE(
387-
dataset->putAndInsertString(DCM_StudyInstanceUID, studyUid).good());
388-
ASSERT_TRUE(
389-
dataset->putAndInsertString(DCM_SeriesInstanceUID, seriesUid).good());
390-
ASSERT_TRUE(
391-
dataset->putAndInsertString(DCM_SOPInstanceUID, instanceUid).good());
392368

393369
// Pixel data
394370
ASSERT_TRUE(

0 commit comments

Comments
 (0)