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 }