Mercurial > hg > orthanc
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 |