comparison Framework/Inputs/PlainTiff.cpp @ 298:fa734a851551

New option: "tiff-alignment" to control deep zoom of plain TIFF over IIIF
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 18 Jul 2023 08:19:16 +0200
parents
children 7020852a8fa9
comparison
equal deleted inserted replaced
297:c1687b8fc800 298:fa734a851551
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2023 Osimis S.A., Belgium
6 * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
7 *
8 * This program is free software: you can redistribute it and/or
9 * modify it under the terms of the GNU Affero General Public License
10 * as published by the Free Software Foundation, either version 3 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 **/
21
22
23 #include "PlainTiff.h"
24
25 #include "../TiffReader.h"
26
27 #include <Images/Image.h>
28 #include <Images/ImageProcessing.h>
29 #include <Logging.h>
30 #include <OrthancException.h>
31
32 #include <cassert>
33 #include <string.h>
34
35
36 namespace OrthancWSI
37 {
38 PlainTiff::PlainTiff(const std::string& path,
39 unsigned int tileWidth,
40 unsigned int tileHeight,
41 unsigned int paddingAlignement,
42 uint8_t paddingRed,
43 uint8_t paddingGreen,
44 uint8_t paddingBlue) :
45 SingleLevelDecodedPyramid(tileWidth, tileHeight)
46 {
47 TiffReader reader(path);
48
49 // Look for the largest sub-image
50 bool first = true;
51 unsigned int width = 0;
52 unsigned int height = 0;
53 tdir_t largest = 0;
54
55 tdir_t pos = 0;
56
57 do
58 {
59 uint32_t w, h, tw, th;
60
61 if (TIFFSetDirectory(reader.GetTiff(), pos) &&
62 !TIFFGetField(reader.GetTiff(), TIFFTAG_TILEWIDTH, &tw) && // Must not be a tiled image
63 !TIFFGetField(reader.GetTiff(), TIFFTAG_TILELENGTH, &th) && // Must not be a tiled image
64 TIFFGetField(reader.GetTiff(), TIFFTAG_IMAGEWIDTH, &w) &&
65 TIFFGetField(reader.GetTiff(), TIFFTAG_IMAGELENGTH, &h) &&
66 w > 0 &&
67 h > 0)
68 {
69 if (first)
70 {
71 first = false;
72 width = w;
73 height = h;
74 largest = pos;
75 }
76 else if (w > width &&
77 h > height)
78 {
79 width = w;
80 height = h;
81 largest = pos;
82 }
83 }
84
85 pos++;
86 }
87 while (TIFFReadDirectory(reader.GetTiff()));
88
89 if (first)
90 {
91 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "This is an empty TIFF image");
92 }
93
94 // Back to the largest directory
95 if (!TIFFSetDirectory(reader.GetTiff(), largest))
96 {
97 throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile);
98 }
99
100 ImageCompression compression;
101 Orthanc::PixelFormat pixelFormat;
102 Orthanc::PhotometricInterpretation photometric;
103
104 if (!reader.GetCurrentDirectoryInformation(compression, pixelFormat, photometric))
105 {
106 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
107 }
108
109 if (pixelFormat != Orthanc::PixelFormat_RGB24)
110 {
111 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
112 }
113
114 LOG(WARNING) << "Size of the source TIFF image: " << width << "x" << height;
115
116 const unsigned int paddedWidth = paddingAlignement * CeilingDivision(width, paddingAlignement);
117 const unsigned int paddedHeight = paddingAlignement * CeilingDivision(height, paddingAlignement);
118 assert(paddedWidth >= width &&
119 paddedHeight >= height);
120
121 LOG(WARNING) << "Size of the padded TIFF image: " << paddedWidth << "x" << paddedHeight;
122
123 decoded_.reset(new Orthanc::Image(pixelFormat, paddedWidth, paddedHeight, false));
124 Orthanc::ImageProcessing::Set(*decoded_, paddingRed, paddingGreen, paddingBlue, 255);
125
126 std::string strip;
127 strip.resize(TIFFStripSize(reader.GetTiff()));
128
129 const size_t stripPitch = width * Orthanc::GetBytesPerPixel(pixelFormat);
130
131 if (strip.empty() ||
132 stripPitch == 0 ||
133 strip.size() < stripPitch ||
134 strip.size() % stripPitch != 0)
135 {
136 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
137 }
138
139 const size_t stripHeight = (strip.size() / stripPitch);
140 const size_t stripCount = CeilingDivision(height, stripHeight);
141
142 if (TIFFNumberOfStrips(reader.GetTiff()) != stripCount)
143 {
144 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
145 }
146
147 for (unsigned int i = 0; i < stripCount; i++)
148 {
149 TIFFReadEncodedStrip(reader.GetTiff(), i, &strip[0], static_cast<tsize_t>(-1));
150
151 const unsigned int y = i * stripHeight;
152
153 const uint8_t* p = reinterpret_cast<const uint8_t*>(&strip[0]);
154 uint8_t* q = reinterpret_cast<uint8_t*>(decoded_->GetRow(y));
155
156 for (unsigned j = 0; j < stripHeight && y + j < height; j++)
157 {
158 memcpy(q, p, stripPitch);
159 p += stripPitch;
160 q += decoded_->GetPitch();
161 }
162 }
163
164 SetImage(*decoded_);
165 }
166 }