Mercurial > hg > orthanc-stone
comparison Framework/Scene2D/LookupTableTextureSceneLayer.cpp @ 1179:177e7d431cd1 broker
log scale in textures, remove redundant code for LUTs
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 20 Nov 2019 15:24:20 +0100 |
parents | 640feb146fa8 |
children | 96d4f4fee5bb |
comparison
equal
deleted
inserted
replaced
1178:3c7cdbf32e2a | 1179:177e7d431cd1 |
---|---|
130 maxValue_ = maxValue; | 130 maxValue_ = maxValue; |
131 IncrementRevision(); | 131 IncrementRevision(); |
132 } | 132 } |
133 } | 133 } |
134 | 134 |
135 void LookupTableTextureSceneLayer::SetApplyLog(bool apply) | |
136 { | |
137 applyLog_ = apply; | |
138 IncrementRevision(); | |
139 } | |
140 | |
135 void LookupTableTextureSceneLayer::FitRange() | 141 void LookupTableTextureSceneLayer::FitRange() |
136 { | 142 { |
137 Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue_, maxValue_, GetTexture()); | 143 Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue_, maxValue_, GetTexture()); |
138 assert(minValue_ <= maxValue_); | 144 assert(minValue_ <= maxValue_); |
139 // TODO: debug to be removed | 145 // TODO: debug to be removed |
156 cloned->maxValue_ = maxValue_; | 162 cloned->maxValue_ = maxValue_; |
157 cloned->lut_ = lut_; | 163 cloned->lut_ = lut_; |
158 | 164 |
159 return cloned.release(); | 165 return cloned.release(); |
160 } | 166 } |
167 | |
168 | |
169 // Templatized function to speed up computations, by avoiding | |
170 // testing conditions on each pixel | |
171 template <bool IsApplyLog, | |
172 Orthanc::PixelFormat TargetFormat> | |
173 static void RenderInternal(Orthanc::ImageAccessor& target, | |
174 const Orthanc::ImageAccessor& source, | |
175 float minValue, | |
176 float slope, | |
177 const std::vector<uint8_t>& lut) | |
178 { | |
179 static const float LOG_NORMALIZATION = 255.0f / log(1.0f + 255.0f); | |
180 | |
181 const unsigned int width = source.GetWidth(); | |
182 const unsigned int height = source.GetHeight(); | |
183 | |
184 for (unsigned int y = 0; y < height; y++) | |
185 { | |
186 const float* p = reinterpret_cast<const float*>(source.GetConstRow(y)); | |
187 uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y)); | |
188 | |
189 for (unsigned int x = 0; x < width; x++) | |
190 { | |
191 float v = (*p - minValue) * slope; | |
192 if (v <= 0) | |
193 { | |
194 v = 0; | |
195 } | |
196 else if (v >= 255) | |
197 { | |
198 v = 255; | |
199 } | |
200 | |
201 if (IsApplyLog) | |
202 { | |
203 // https://theailearner.com/2019/01/01/log-transformation/ | |
204 v = LOG_NORMALIZATION * log(1.0f + static_cast<float>(v)); | |
205 } | |
206 | |
207 assert(v >= 0.0f && v <= 255.0f); | |
208 | |
209 uint8_t vv = static_cast<uint8_t>(v); | |
210 | |
211 switch (TargetFormat) | |
212 { | |
213 case Orthanc::PixelFormat_BGRA32: | |
214 // For Cairo surfaces | |
215 q[0] = lut[4 * vv + 2]; // B | |
216 q[1] = lut[4 * vv + 1]; // G | |
217 q[2] = lut[4 * vv + 0]; // R | |
218 q[3] = lut[4 * vv + 3]; // A | |
219 break; | |
220 | |
221 case Orthanc::PixelFormat_RGBA32: | |
222 // For OpenGL | |
223 q[0] = lut[4 * vv + 0]; // R | |
224 q[1] = lut[4 * vv + 1]; // G | |
225 q[2] = lut[4 * vv + 2]; // B | |
226 q[3] = lut[4 * vv + 3]; // A | |
227 break; | |
228 | |
229 default: | |
230 assert(0); | |
231 } | |
232 | |
233 p++; | |
234 q += 4; | |
235 } | |
236 } | |
237 } | |
238 | |
239 | |
240 void LookupTableTextureSceneLayer::Render(Orthanc::ImageAccessor& target) const | |
241 { | |
242 assert(sizeof(float) == 4); | |
243 | |
244 if (!HasTexture()) | |
245 { | |
246 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
247 } | |
248 | |
249 const Orthanc::ImageAccessor& source = GetTexture(); | |
250 | |
251 if (source.GetFormat() != Orthanc::PixelFormat_Float32 || | |
252 (target.GetFormat() != Orthanc::PixelFormat_RGBA32 && | |
253 target.GetFormat() != Orthanc::PixelFormat_BGRA32)) | |
254 { | |
255 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
256 } | |
257 | |
258 if (source.GetWidth() != target.GetWidth() || | |
259 source.GetHeight() != target.GetHeight()) | |
260 { | |
261 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize); | |
262 } | |
263 | |
264 const float minValue = GetMinValue(); | |
265 float slope; | |
266 | |
267 if (GetMinValue() >= GetMaxValue()) | |
268 { | |
269 slope = 0; | |
270 } | |
271 else | |
272 { | |
273 slope = 256.0f / (GetMaxValue() - GetMinValue()); | |
274 } | |
275 | |
276 const std::vector<uint8_t>& lut = GetLookupTable(); | |
277 if (lut.size() != 4 * 256) | |
278 { | |
279 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
280 } | |
281 | |
282 switch (target.GetFormat()) | |
283 { | |
284 case Orthanc::PixelFormat_RGBA32: | |
285 if (applyLog_) | |
286 { | |
287 RenderInternal<true, Orthanc::PixelFormat_RGBA32>(target, source, minValue, slope, lut); | |
288 } | |
289 else | |
290 { | |
291 RenderInternal<false, Orthanc::PixelFormat_RGBA32>(target, source, minValue, slope, lut); | |
292 } | |
293 break; | |
294 | |
295 case Orthanc::PixelFormat_BGRA32: | |
296 if (applyLog_) | |
297 { | |
298 RenderInternal<true, Orthanc::PixelFormat_BGRA32>(target, source, minValue, slope, lut); | |
299 } | |
300 else | |
301 { | |
302 RenderInternal<false, Orthanc::PixelFormat_BGRA32>(target, source, minValue, slope, lut); | |
303 } | |
304 break; | |
305 | |
306 default: | |
307 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
308 } | |
309 } | |
161 } | 310 } |