comparison OrthancFramework/Sources/Images/ImageProcessing.cpp @ 4065:d6362b2c4b61 framework

export dcmdata in shared library, rounding in convolution tests
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 11 Jun 2020 18:04:28 +0200
parents 55727d85f419
children 259c33759937
comparison
equal deleted inserted replaced
4063:e00f3d089991 4065:d6362b2c4b61
364 for (unsigned int x = 0; x < width; x++, p++) 364 for (unsigned int x = 0; x < width; x++, p++)
365 { 365 {
366 int64_t v; 366 int64_t v;
367 if (UseRound) 367 if (UseRound)
368 { 368 {
369 assert(sizeof(long long) == sizeof(int64_t));
369 // The "round" operation is very costly 370 // The "round" operation is very costly
370 v = boost::math::llround(static_cast<float>(*p) * factor); 371 v = boost::math::llround(static_cast<float>(*p) * factor);
371 } 372 }
372 else 373 else
373 { 374 {
440 else if (v <= minFloatValue) 441 else if (v <= minFloatValue)
441 { 442 {
442 *p = minPixelValue; 443 *p = minPixelValue;
443 } 444 }
444 else if (UseRound) 445 else if (UseRound)
445 { 446 {
446 // The "round" operation is very costly 447 // The "round" operation is very costly
448 assert(sizeof(TargetType) < sizeof(int));
447 *p = static_cast<TargetType>(boost::math::iround(v)); 449 *p = static_cast<TargetType>(boost::math::iround(v));
448 } 450 }
449 else 451 else
450 { 452 {
451 *p = static_cast<TargetType>(std::floor(v)); 453 *p = static_cast<TargetType>(std::floor(v));
2195 2197
2196 // This is an implementation of separable convolution that uses 2198 // This is an implementation of separable convolution that uses
2197 // floating-point arithmetics, and an intermediate Float32 2199 // floating-point arithmetics, and an intermediate Float32
2198 // image. The out-of-image values are taken as the border 2200 // image. The out-of-image values are taken as the border
2199 // value. Further optimization is possible. 2201 // value. Further optimization is possible.
2200 template <typename RawPixel, unsigned int ChannelsCount> 2202 template <typename RawPixel, unsigned int ChannelsCount, bool UseRound>
2201 static void SeparableConvolutionFloat(ImageAccessor& image /* inplace */, 2203 static void SeparableConvolutionFloat(ImageAccessor& image /* inplace */,
2202 const std::vector<float>& horizontal, 2204 const std::vector<float>& horizontal,
2203 size_t horizontalAnchor, 2205 size_t horizontalAnchor,
2204 const std::vector<float>& vertical, 2206 const std::vector<float>& vertical,
2205 size_t verticalAnchor, 2207 size_t verticalAnchor,
2333 { 2335 {
2334 *p = std::numeric_limits<RawPixel>::max(); 2336 *p = std::numeric_limits<RawPixel>::max();
2335 } 2337 }
2336 else 2338 else
2337 { 2339 {
2338 *p = static_cast<RawPixel>(accumulator); 2340 if (UseRound)
2341 {
2342 assert(sizeof(RawPixel) < sizeof(int));
2343 *p = static_cast<RawPixel>(boost::math::iround(accumulator));
2344 }
2345 else
2346 {
2347 *p = static_cast<RawPixel>(accumulator);
2348 }
2339 } 2349 }
2340 } 2350 }
2341 } 2351 }
2342 } 2352 }
2343 } 2353 }
2345 2355
2346 void ImageProcessing::SeparableConvolution(ImageAccessor& image /* inplace */, 2356 void ImageProcessing::SeparableConvolution(ImageAccessor& image /* inplace */,
2347 const std::vector<float>& horizontal, 2357 const std::vector<float>& horizontal,
2348 size_t horizontalAnchor, 2358 size_t horizontalAnchor,
2349 const std::vector<float>& vertical, 2359 const std::vector<float>& vertical,
2350 size_t verticalAnchor) 2360 size_t verticalAnchor,
2361 bool useRound)
2351 { 2362 {
2352 if (horizontal.size() == 0 || 2363 if (horizontal.size() == 0 ||
2353 vertical.size() == 0 || 2364 vertical.size() == 0 ||
2354 horizontalAnchor >= horizontal.size() || 2365 horizontalAnchor >= horizontal.size() ||
2355 verticalAnchor >= vertical.size()) 2366 verticalAnchor >= vertical.size())
2388 const float normalization = 1.0f / (sumHorizontal * sumVertical); 2399 const float normalization = 1.0f / (sumHorizontal * sumVertical);
2389 2400
2390 switch (image.GetFormat()) 2401 switch (image.GetFormat())
2391 { 2402 {
2392 case PixelFormat_Grayscale8: 2403 case PixelFormat_Grayscale8:
2393 SeparableConvolutionFloat<uint8_t, 1u> 2404 if (useRound)
2394 (image, horizontal, horizontalAnchor, vertical, verticalAnchor, normalization); 2405 {
2406 SeparableConvolutionFloat<uint8_t, 1u, true>
2407 (image, horizontal, horizontalAnchor, vertical, verticalAnchor, normalization);
2408 }
2409 else
2410 {
2411 SeparableConvolutionFloat<uint8_t, 1u, false>
2412 (image, horizontal, horizontalAnchor, vertical, verticalAnchor, normalization);
2413 }
2395 break; 2414 break;
2396 2415
2397 case PixelFormat_RGB24: 2416 case PixelFormat_RGB24:
2398 SeparableConvolutionFloat<uint8_t, 3u> 2417 if (useRound)
2399 (image, horizontal, horizontalAnchor, vertical, verticalAnchor, normalization); 2418 {
2419 SeparableConvolutionFloat<uint8_t, 3u, true>
2420 (image, horizontal, horizontalAnchor, vertical, verticalAnchor, normalization);
2421 }
2422 else
2423 {
2424 SeparableConvolutionFloat<uint8_t, 3u, false>
2425 (image, horizontal, horizontalAnchor, vertical, verticalAnchor, normalization);
2426 }
2400 break; 2427 break;
2401 2428
2402 default: 2429 default:
2403 throw OrthancException(ErrorCode_NotImplemented); 2430 throw OrthancException(ErrorCode_NotImplemented);
2404 } 2431 }
2405 } 2432 }
2406 2433
2407 2434
2408 void ImageProcessing::SmoothGaussian5x5(ImageAccessor& image) 2435 void ImageProcessing::SmoothGaussian5x5(ImageAccessor& image,
2436 bool useRound)
2409 { 2437 {
2410 std::vector<float> kernel(5); 2438 std::vector<float> kernel(5);
2411 kernel[0] = 1; 2439 kernel[0] = 1;
2412 kernel[1] = 4; 2440 kernel[1] = 4;
2413 kernel[2] = 6; 2441 kernel[2] = 6;
2414 kernel[3] = 4; 2442 kernel[3] = 4;
2415 kernel[4] = 1; 2443 kernel[4] = 1;
2416 2444
2417 SeparableConvolution(image, kernel, 2, kernel, 2); 2445 SeparableConvolution(image, kernel, 2, kernel, 2, useRound);
2418 } 2446 }
2419 2447
2420 2448
2421 void ImageProcessing::FitSize(ImageAccessor& target, 2449 void ImageProcessing::FitSize(ImageAccessor& target,
2422 const ImageAccessor& source) 2450 const ImageAccessor& source)
2443 static_cast<float>(target.GetWidth()) / cw, 2471 static_cast<float>(target.GetWidth()) / cw,
2444 static_cast<float>(target.GetHeight()) / ch); 2472 static_cast<float>(target.GetHeight()) / ch);
2445 2473
2446 unsigned int sw = std::min(static_cast<unsigned int>(boost::math::iround(cw * r)), target.GetWidth()); 2474 unsigned int sw = std::min(static_cast<unsigned int>(boost::math::iround(cw * r)), target.GetWidth());
2447 unsigned int sh = std::min(static_cast<unsigned int>(boost::math::iround(ch * r)), target.GetHeight()); 2475 unsigned int sh = std::min(static_cast<unsigned int>(boost::math::iround(ch * r)), target.GetHeight());
2476
2448 Image resized(target.GetFormat(), sw, sh, false); 2477 Image resized(target.GetFormat(), sw, sh, false);
2449 2478
2450 //ImageProcessing::SmoothGaussian5x5(source); 2479 //ImageProcessing::SmoothGaussian5x5(source);
2451 ImageProcessing::Resize(resized, source); 2480 ImageProcessing::Resize(resized, source);
2452 2481