Skip to content

Commit c03efda

Browse files
committed
Support for reading JPEG compressed TIFF files where JPEG tables are stored globally in JPEGTables tag and not per tile.
1 parent 99288d3 commit c03efda

File tree

8 files changed

+53
-7
lines changed

8 files changed

+53
-7
lines changed

source/FAST/Algorithms/Compression/JPEGCompression.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ void* JPEGCompression::decompress(uchar* compressedData, std::size_t bytes, int*
1818
cinfo.err = jpeg_std_error(&jerr);
1919
try {
2020
jpeg_create_decompress(&cinfo);
21+
if(m_tableCount > 0) {
22+
jpeg_mem_src(&cinfo, (const uchar*)m_tableData, m_tableCount);
23+
if(jpeg_read_header(&cinfo, FALSE) != JPEG_HEADER_TABLES_ONLY) {
24+
throw Exception("Error setting JPEG tables");
25+
}
26+
}
2127
jpeg_mem_src(&cinfo, compressedData, bytes);
2228
int ret = jpeg_read_header(&cinfo, TRUE);
2329
if(ret != JPEG_HEADER_OK) {
@@ -83,8 +89,9 @@ void JPEGCompression::compress(void *data, int width, int height, std::vector<ui
8389
free(resultBuffer);
8490
jpeg_destroy_compress(&cinfo);
8591
}
86-
JPEGCompression::JPEGCompression() {
87-
92+
JPEGCompression::JPEGCompression(uint32_t tableCount, void* tableData) {
93+
m_tableCount = tableCount;
94+
m_tableData = tableData;
8895
}
8996

9097
} // End namespace

source/FAST/Algorithms/Compression/JPEGCompression.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace fast {
1111
*/
1212
class JPEGCompression {
1313
public:
14-
JPEGCompression();
14+
explicit JPEGCompression(uint32_t JPEGTableCount = 0, void* tableData = nullptr);
1515
void compress(void* data, int width, int height, std::vector<uint8_t>* compressedData, int quality = 90);
1616
/**
1717
* @brief Decompress
@@ -24,5 +24,8 @@ class JPEGCompression {
2424
*/
2525
void* decompress(uchar* compressedData, std::size_t bytes, int* widthOut, int* heightOut, uchar* outputBuffer = nullptr);
2626

27+
private:
28+
uint32_t m_tableCount = 0;
29+
const void* m_tableData = nullptr;
2730
};
2831
}

source/FAST/Data/Access/ImagePyramidAccess.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ int ImagePyramidAccess::readTileFromTIFF(void *data, int x, int y, int level) {
546546
auto buffer = make_uninitialized_unique<char[]>(tileWidth*tileHeight*channels);
547547
bytesRead = TIFFReadRawTile(m_tiffHandle, tile_id, buffer.get(), tileWidth*tileHeight*channels);
548548

549-
JPEGCompression jpeg;
549+
JPEGCompression jpeg(m_JPEGTablesCount, m_JPEGTablesData);
550550
int width, height;
551551
jpeg.decompress((uchar*)buffer.get(), bytesRead, &width, &height, (uchar*)data);
552552
} else if(m_compressionFormat == ImageCompression::JPEGXL) {
@@ -580,5 +580,10 @@ void ImagePyramidAccess::setBlankPatch(int level, int x, int y) {
580580
// TODO Propagate or not?
581581
}
582582

583+
void ImagePyramidAccess::setJPEGTables(uint32_t tableCount, void *tableData) {
584+
m_JPEGTablesCount = tableCount;
585+
m_JPEGTablesData = tableData;
586+
}
587+
583588

584589
}

source/FAST/Data/Access/ImagePyramidAccess.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class FAST_EXPORT ImagePyramidAccess : Object {
101101
std::shared_ptr<Image> getPatchAsImageForMagnification(float magnification, float offsetX, float offsetY, int width, int height, bool convertToRGB = true);
102102
void release();
103103
~ImagePyramidAccess();
104+
void setJPEGTables(uint32_t tableCount, void* tableData);
104105
private:
105106
std::unique_ptr<uchar[]> getPatchDataChar(int level, int x, int y, int width, int height);
106107
std::shared_ptr<ImagePyramid> m_image;
@@ -119,6 +120,9 @@ class FAST_EXPORT ImagePyramidAccess : Object {
119120
uint32_t writeTileToTIFFNeuralNetwork(int level, int x, int y, std::shared_ptr<Image> image);
120121
int readTileFromTIFF(void* data, int x, int y, int level);
121122
void propagatePatch(std::shared_ptr<Image> patch, int level, int x, int y);
123+
124+
uint32_t m_JPEGTablesCount = 0;
125+
void* m_JPEGTablesData = nullptr;
122126
};
123127

124128
template <class T>

source/FAST/Data/ImagePyramid.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,10 @@ ImagePyramidAccess::pointer ImagePyramid::getAccess(accessType type) {
322322
std::unique_lock<std::mutex> lock(mDataIsBeingAccessedMutex);
323323
mDataIsBeingAccessed = true;
324324
}
325-
return std::make_unique<ImagePyramidAccess>(m_levels, m_fileHandle, m_tiffHandle, std::static_pointer_cast<ImagePyramid>(mPtr.lock()), type == ACCESS_READ_WRITE, m_initializedPatchList, m_readMutex, m_compressionFormat);
325+
auto access = std::make_unique<ImagePyramidAccess>(m_levels, m_fileHandle, m_tiffHandle, std::static_pointer_cast<ImagePyramid>(mPtr.lock()), type == ACCESS_READ_WRITE, m_initializedPatchList, m_readMutex, m_compressionFormat);
326+
if(m_JPEGTablesCount > 0)
327+
access->setJPEGTables(m_JPEGTablesCount, m_JPEGTablesData);
328+
return access;
326329
}
327330

328331
void ImagePyramid::setDirtyPatch(int level, int patchIdX, int patchIdY) {
@@ -437,6 +440,20 @@ ImagePyramid::ImagePyramid(TIFF *fileHandle, std::vector<ImagePyramidLevel> leve
437440
throw Exception("Unable to get JPEG quality from TIFF");
438441
m_compressionQuality = quality;
439442
}
443+
{
444+
// Check if there is JPEG table data stored in the TIFF
445+
// If so we use this table for all tiles
446+
uint32_t tablesCount = 0;
447+
void *tableData;
448+
int res = TIFFGetField(m_tiffHandle, TIFFTAG_JPEGTABLES, &tablesCount, &tableData);
449+
if(res == 1) {
450+
reportInfo() << "Got JPEG tables data from the TIFF file with size: " << tablesCount << reportEnd();
451+
m_JPEGTablesCount = tablesCount;
452+
// Have to copy the table
453+
m_JPEGTablesData = new uchar[tablesCount];
454+
std::memcpy(m_JPEGTablesData, tableData, sizeof(uchar)*tablesCount);
455+
}
456+
}
440457
break;
441458
case COMPRESSION_JXL:
442459
compression = ImageCompression::JPEGXL;

source/FAST/Data/ImagePyramid.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ class FAST_EXPORT ImagePyramid : public SpatialDataObject {
139139
float m_decompressionOutputScaleFactor = 1.0f;
140140

141141
float m_magnification = -1.0f;
142+
143+
uint32_t m_JPEGTablesCount = 0;
144+
void* m_JPEGTablesData = nullptr;
142145
};
143146

144147
}

source/FAST/Importers/WholeSlideImageImporter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ void WholeSlideImageImporter::execute() {
127127
lf.find(".ome.tiff") != std::string::npos ||
128128
lf.find(".ome.tif") != std::string::npos ||
129129
lf.find(".ome.btf") != std::string::npos ||
130-
lf.find(".tiff") != std::string::npos
130+
lf.find(".tiff") != std::string::npos ||
131+
lf.find(".tif") != std::string::npos
131132
) {
132133
// OpenSlide doesn't support reading OME-TIFF, use FAST and lbitiff instead.
133134
// Also FAST supports more TIFF compression formats, thus use FAST and libtiff for regular TIFF files as well.

source/FAST/Utility.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1269,7 +1269,13 @@ void Progress::update(uint64_t current) {
12691269
if(!m_unit.empty()) {
12701270
ss << " " << round(m_current*m_unitScale, m_unitPrecision) << m_unit << " / " << round(m_max*m_unitScale, m_unitPrecision) << m_unit;
12711271
}
1272-
ss << " | ETA " << ETA << " mins";
1272+
if(ETA > 60) {
1273+
ss << " | ETA " << round(ETA / 60.0f, 1) << " hours";
1274+
} else if(ETA > 1) {
1275+
ss << " | ETA " << round(ETA) << " mins";
1276+
} else {
1277+
ss << " | ETA " << std::floor(ETA * 60.0f) << " seconds";
1278+
}
12731279
std::string startString = m_text + (m_text.empty() ? "" : " ") + "[";
12741280
const int totalWidth = getConsoleWidth();
12751281
const int progressbarWidth = totalWidth - ss.str().size() - startString.size() - 1; // have to leave last line open to avoid jumping to next line (windows)

0 commit comments

Comments
 (0)