comparison Applications/Samples/SingleFrameEditorApplication.h @ 356:885f0a5eaa49 am-2

mouse tracker to set windowing
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 29 Oct 2018 17:21:40 +0100
parents d2468dd75b3f
children ec4ad6c5eb99
comparison
equal deleted inserted replaced
355:d2468dd75b3f 356:885f0a5eaa49
360 360
361 return extent; 361 return extent;
362 } 362 }
363 363
364 364
365 virtual void Render(Orthanc::ImageAccessor& buffer,
366 const ViewportGeometry& view,
367 ImageInterpolation interpolation) const = 0;
368
369
370 bool Contains(double x, 365 bool Contains(double x,
371 double y) const 366 double y) const
372 { 367 {
373 ApplyTransform(x, y, transformInverse_); 368 ApplyTransform(x, y, transformInverse_);
374 369
378 return (x >= cropX && x <= cropX + cropWidth && 373 return (x >= cropX && x <= cropX + cropWidth &&
379 y >= cropY && y <= cropY + cropHeight); 374 y >= cropY && y <= cropY + cropHeight);
380 } 375 }
381 376
382 377
383 bool GetPixel(unsigned int& pixelX, 378 bool GetPixel(unsigned int& imageX,
384 unsigned int& pixelY, 379 unsigned int& imageY,
385 double sceneX, 380 double sceneX,
386 double sceneY) const 381 double sceneY) const
387 { 382 {
388 if (width_ == 0 || 383 if (width_ == 0 ||
389 height_ == 0) 384 height_ == 0)
397 int x = static_cast<int>(std::floor(sceneX)); 392 int x = static_cast<int>(std::floor(sceneX));
398 int y = static_cast<int>(std::floor(sceneY)); 393 int y = static_cast<int>(std::floor(sceneY));
399 394
400 if (x < 0) 395 if (x < 0)
401 { 396 {
402 pixelX = 0; 397 imageX = 0;
403 } 398 }
404 else if (x >= static_cast<int>(width_)) 399 else if (x >= static_cast<int>(width_))
405 { 400 {
406 pixelX = width_; 401 imageX = width_;
407 } 402 }
408 else 403 else
409 { 404 {
410 pixelX = static_cast<unsigned int>(x); 405 imageX = static_cast<unsigned int>(x);
411 } 406 }
412 407
413 if (y < 0) 408 if (y < 0)
414 { 409 {
415 pixelY = 0; 410 imageY = 0;
416 } 411 }
417 else if (y >= static_cast<int>(height_)) 412 else if (y >= static_cast<int>(height_))
418 { 413 {
419 pixelY = height_; 414 imageY = height_;
420 } 415 }
421 else 416 else
422 { 417 {
423 pixelY = static_cast<unsigned int>(y); 418 imageY = static_cast<unsigned int>(y);
424 } 419 }
425 420
426 return true; 421 return true;
427 } 422 }
428 } 423 }
461 } 456 }
462 457
463 double GetPanY() const 458 double GetPanY() const
464 { 459 {
465 return panY_; 460 return panY_;
466 }
467
468 virtual bool GetDefaultWindowing(float& center,
469 float& width) const
470 {
471 return false;
472 } 461 }
473 462
474 void GetCenter(double& centerX, 463 void GetCenter(double& centerX,
475 double& centerY) const 464 double& centerY) const
476 { 465 {
582 571
583 void SetResizeable(bool resizeable) 572 void SetResizeable(bool resizeable)
584 { 573 {
585 resizeable_ = resizeable; 574 resizeable_ = resizeable;
586 } 575 }
576
577 virtual bool GetDefaultWindowing(float& center,
578 float& width) const
579 {
580 return false;
581 }
582
583 virtual void Render(Orthanc::ImageAccessor& buffer,
584 const ViewportGeometry& view,
585 ImageInterpolation interpolation) const = 0;
586
587 virtual bool GetRange(float& minValue,
588 float& maxValue) const = 0;
587 }; 589 };
588 590
589 591
590 class BitmapAccessor : public boost::noncopyable 592 class BitmapAccessor : public boost::noncopyable
591 { 593 {
786 788
787 *q = (a * value + (1.0f - a) * (*q)); 789 *q = (a * value + (1.0f - a) * (*q));
788 } 790 }
789 } 791 }
790 } 792 }
793
794 virtual bool GetRange(float& minValue,
795 float& maxValue) const
796 {
797 if (useWindowing_)
798 {
799 return false;
800 }
801 else
802 {
803 minValue = 0;
804 maxValue = 0;
805
806 if (foreground_ < 0)
807 {
808 minValue = foreground_;
809 }
810
811 if (foreground_ > 0)
812 {
813 maxValue = foreground_;
814 }
815
816 return true;
817 }
818 }
791 }; 819 };
792 820
793 821
794 822
795 private: 823 private:
796 class DicomBitmap : public Bitmap 824 class DicomBitmap : public Bitmap
797 { 825 {
798 private: 826 private:
799 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData 827 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData
800 std::auto_ptr<DicomFrameConverter> converter_; 828 std::auto_ptr<DicomFrameConverter> converter_;
801 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 or RGB24 829 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32
802 830
803 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) 831 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag)
804 { 832 {
805 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); 833 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement());
806 } 834 }
909 if (converter_.get() != NULL && 937 if (converter_.get() != NULL &&
910 converter_->HasDefaultWindow()) 938 converter_->HasDefaultWindow())
911 { 939 {
912 center = static_cast<float>(converter_->GetDefaultWindowCenter()); 940 center = static_cast<float>(converter_->GetDefaultWindowCenter());
913 width = static_cast<float>(converter_->GetDefaultWindowWidth()); 941 width = static_cast<float>(converter_->GetDefaultWindowWidth());
942 return true;
943 }
944 else
945 {
946 return false;
947 }
948 }
949
950
951 virtual bool GetRange(float& minValue,
952 float& maxValue) const
953 {
954 if (converted_.get() != NULL)
955 {
956 if (converted_->GetFormat() != Orthanc::PixelFormat_Float32)
957 {
958 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
959 }
960
961 Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, *converted_);
914 return true; 962 return true;
915 } 963 }
916 else 964 else
917 { 965 {
918 return false; 966 return false;
1001 return false; 1049 return false;
1002 } 1050 }
1003 } 1051 }
1004 1052
1005 1053
1054 void GetWindowingWithDefault(float& center,
1055 float& width) const
1056 {
1057 if (!GetWindowing(center, width))
1058 {
1059 center = 128;
1060 width = 256;
1061 }
1062 }
1063
1064
1006 void SetWindowing(float center, 1065 void SetWindowing(float center,
1007 float width) 1066 float width)
1008 1067
1009 { 1068 {
1010 hasWindowing_ = true; 1069 hasWindowing_ = true;
1011 windowingCenter_ = center; 1070 windowingCenter_ = center;
1012 windowingWidth_ = width; 1071 windowingWidth_ = width;
1013 1072
1014 EmitMessage(ContentChangedMessage(*this)); 1073 //EmitMessage(ContentChangedMessage(*this));
1015 } 1074 }
1016 1075
1017 1076
1018 Bitmap& LoadText(const Orthanc::Font& font, 1077 Bitmap& LoadText(const Orthanc::Font& font,
1019 const std::string& utf8) 1078 const std::string& utf8)
1023 std::auto_ptr<AlphaBitmap> alpha(new AlphaBitmap(bitmap, *this)); 1082 std::auto_ptr<AlphaBitmap> alpha(new AlphaBitmap(bitmap, *this));
1024 alpha->LoadText(font, utf8); 1083 alpha->LoadText(font, utf8);
1025 1084
1026 AlphaBitmap* ptr = alpha.get(); 1085 AlphaBitmap* ptr = alpha.get();
1027 bitmaps_[bitmap] = alpha.release(); 1086 bitmaps_[bitmap] = alpha.release();
1028 1087
1029 return *ptr; 1088 return *ptr;
1030 } 1089 }
1031 1090
1032 1091
1033 Bitmap& LoadTestBlock(unsigned int width, 1092 Bitmap& LoadTestBlock(unsigned int width,
1229 1288
1230 context.SetSourceColor(255, 0, 0); 1289 context.SetSourceColor(255, 0, 0);
1231 view.ApplyTransform(context); 1290 view.ApplyTransform(context);
1232 bitmap->second->DrawBorders(context, view.GetZoom()); 1291 bitmap->second->DrawBorders(context, view.GetZoom());
1233 } 1292 }
1293 }
1294 }
1295
1296
1297 void GetRange(float& minValue,
1298 float& maxValue) const
1299 {
1300 bool first = true;
1301
1302 for (Bitmaps::const_iterator it = bitmaps_.begin();
1303 it != bitmaps_.end(); it++)
1304 {
1305 assert(it->second != NULL);
1306
1307 float a, b;
1308 if (it->second->GetRange(a, b))
1309 {
1310 if (first)
1311 {
1312 minValue = a;
1313 maxValue = b;
1314 first = false;
1315 }
1316 else
1317 {
1318 minValue = std::min(a, minValue);
1319 maxValue = std::max(b, maxValue);
1320 }
1321 }
1322 }
1323
1324 if (first)
1325 {
1326 minValue = 0;
1327 maxValue = 0;
1234 } 1328 }
1235 } 1329 }
1236 }; 1330 };
1237 1331
1238 1332
1915 } 2009 }
1916 } 2010 }
1917 }; 2011 };
1918 2012
1919 2013
2014 class WindowingTracker : public IWorldSceneMouseTracker
2015 {
2016 public:
2017 enum Action
2018 {
2019 Action_IncreaseWidth,
2020 Action_DecreaseWidth,
2021 Action_IncreaseCenter,
2022 Action_DecreaseCenter
2023 };
2024
2025 private:
2026 UndoRedoStack& undoRedoStack_;
2027 BitmapStack& stack_;
2028 int clickX_;
2029 int clickY_;
2030 Action leftAction_;
2031 Action rightAction_;
2032 Action upAction_;
2033 Action downAction_;
2034 float strength_;
2035 float sourceCenter_;
2036 float sourceWidth_;
2037
2038 static void ComputeAxisEffect(int& deltaCenter,
2039 int& deltaWidth,
2040 int delta,
2041 Action actionNegative,
2042 Action actionPositive)
2043 {
2044 if (delta < 0)
2045 {
2046 switch (actionNegative)
2047 {
2048 case Action_IncreaseWidth:
2049 deltaWidth = -delta;
2050 break;
2051
2052 case Action_DecreaseWidth:
2053 deltaWidth = delta;
2054 break;
2055
2056 case Action_IncreaseCenter:
2057 deltaCenter = -delta;
2058 break;
2059
2060 case Action_DecreaseCenter:
2061 deltaCenter = delta;
2062 break;
2063
2064 default:
2065 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
2066 }
2067 }
2068 else if (delta > 0)
2069 {
2070 switch (actionPositive)
2071 {
2072 case Action_IncreaseWidth:
2073 deltaWidth = delta;
2074 break;
2075
2076 case Action_DecreaseWidth:
2077 deltaWidth = -delta;
2078 break;
2079
2080 case Action_IncreaseCenter:
2081 deltaCenter = delta;
2082 break;
2083
2084 case Action_DecreaseCenter:
2085 deltaCenter = -delta;
2086 break;
2087
2088 default:
2089 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
2090 }
2091 }
2092 }
2093
2094
2095 class UndoRedoCommand : public UndoRedoStack::ICommand
2096 {
2097 private:
2098 BitmapStack& stack_;
2099 float sourceCenter_;
2100 float sourceWidth_;
2101 float targetCenter_;
2102 float targetWidth_;
2103
2104 public:
2105 UndoRedoCommand(const WindowingTracker& tracker) :
2106 stack_(tracker.stack_),
2107 sourceCenter_(tracker.sourceCenter_),
2108 sourceWidth_(tracker.sourceWidth_)
2109 {
2110 stack_.GetWindowingWithDefault(targetCenter_, targetWidth_);
2111 }
2112
2113 virtual void Undo() const
2114 {
2115 stack_.SetWindowing(sourceCenter_, sourceWidth_);
2116 }
2117
2118 virtual void Redo() const
2119 {
2120 stack_.SetWindowing(targetCenter_, targetWidth_);
2121 }
2122 };
2123
2124
2125 public:
2126 WindowingTracker(UndoRedoStack& undoRedoStack,
2127 BitmapStack& stack,
2128 int x,
2129 int y,
2130 Action leftAction,
2131 Action rightAction,
2132 Action upAction,
2133 Action downAction) :
2134 undoRedoStack_(undoRedoStack),
2135 stack_(stack),
2136 clickX_(x),
2137 clickY_(y),
2138 leftAction_(leftAction),
2139 rightAction_(rightAction),
2140 upAction_(upAction),
2141 downAction_(downAction)
2142 {
2143 stack_.GetWindowingWithDefault(sourceCenter_, sourceWidth_);
2144
2145 float minValue, maxValue;
2146 stack.GetRange(minValue, maxValue);
2147
2148 assert(minValue <= maxValue);
2149
2150 float tmp;
2151
2152 float delta = (maxValue - minValue);
2153 if (delta <= 1)
2154 {
2155 tmp = 0;
2156 }
2157 else
2158 {
2159 tmp = log2(delta);
2160 }
2161
2162 strength_ = tmp - 7;
2163 if (strength_ < 1)
2164 {
2165 strength_ = 1;
2166 }
2167 }
2168
2169 virtual bool HasRender() const
2170 {
2171 return false;
2172 }
2173
2174 virtual void Render(CairoContext& context,
2175 double zoom)
2176 {
2177 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
2178 }
2179
2180 virtual void MouseUp()
2181 {
2182 undoRedoStack_.Add(new UndoRedoCommand(*this));
2183 }
2184
2185
2186 virtual void MouseMove(int displayX,
2187 int displayY,
2188 double sceneX,
2189 double sceneY)
2190 {
2191 // https://bitbucket.org/osimis/osimis-webviewer-plugin/src/master/frontend/src/app/viewport/image-plugins/windowing-viewport-tool.class.js
2192
2193 static const float SCALE = 1.0;
2194
2195 int deltaCenter = 0;
2196 int deltaWidth = 0;
2197
2198 ComputeAxisEffect(deltaCenter, deltaWidth, displayX - clickX_, leftAction_, rightAction_);
2199 ComputeAxisEffect(deltaCenter, deltaWidth, displayY - clickY_, upAction_, downAction_);
2200
2201 float newCenter = sourceCenter_ + (deltaCenter / SCALE * strength_);
2202 float newWidth = sourceWidth_ + (deltaWidth / SCALE * strength_);
2203 stack_.SetWindowing(newCenter, newWidth);
2204 }
2205 };
2206
2207
1920 class BitmapStackInteractor : public IWorldSceneInteractor 2208 class BitmapStackInteractor : public IWorldSceneInteractor
1921 { 2209 {
1922 private: 2210 private:
1923 enum Tool 2211 enum Tool
1924 { 2212 {
1925 Tool_Move, 2213 Tool_Move,
1926 Tool_Rotate, 2214 Tool_Rotate,
1927 Tool_Crop, 2215 Tool_Crop,
1928 Tool_Resize 2216 Tool_Resize,
2217 Tool_Windowing
1929 }; 2218 };
1930 2219
1931 static double GetHandleSize() 2220 static double GetHandleSize()
1932 { 2221 {
1933 return 10.0; 2222 return 10.0;
1948 2237
1949 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, 2238 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
1950 const ViewportGeometry& view, 2239 const ViewportGeometry& view,
1951 MouseButton button, 2240 MouseButton button,
1952 KeyboardModifiers modifiers, 2241 KeyboardModifiers modifiers,
2242 int viewportX,
2243 int viewportY,
1953 double x, 2244 double x,
1954 double y, 2245 double y,
1955 IStatusBar* statusBar) 2246 IStatusBar* statusBar)
1956 { 2247 {
1957 if (button == MouseButton_Left) 2248 if (button == MouseButton_Left)
1958 { 2249 {
1959 size_t selected; 2250 size_t selected;
1960 2251
1961 if (!stack_.GetSelectedBitmap(selected)) 2252 if (tool_ == Tool_Windowing)
2253 {
2254 return new WindowingTracker(undoRedoStack_, stack_,
2255 viewportX, viewportY,
2256 WindowingTracker::Action_DecreaseWidth,
2257 WindowingTracker::Action_IncreaseWidth,
2258 WindowingTracker::Action_DecreaseCenter,
2259 WindowingTracker::Action_IncreaseCenter);
2260 }
2261 else if (!stack_.GetSelectedBitmap(selected))
1962 { 2262 {
1963 size_t bitmap; 2263 size_t bitmap;
1964 if (stack_.LookupBitmap(bitmap, x, y)) 2264 if (stack_.LookupBitmap(bitmap, x, y))
1965 { 2265 {
1966 printf("CLICK on bitmap %ld\n", bitmap); 2266 printf("CLICK on bitmap %ld\n", bitmap);
2106 2406
2107 case 's': 2407 case 's':
2108 tool_ = Tool_Resize; 2408 tool_ = Tool_Resize;
2109 break; 2409 break;
2110 2410
2411 case 'w':
2412 tool_ = Tool_Windowing;
2413 break;
2414
2111 case 'z': 2415 case 'z':
2112 if (modifiers & KeyboardModifiers_Control) 2416 if (modifiers & KeyboardModifiers_Control)
2113 { 2417 {
2114 undoRedoStack_.Undo(); 2418 undoRedoStack_.Undo();
2115 widget.NotifyContentChanged(); 2419 widget.NotifyContentChanged();
2192 //stack_.Render(buffer, GetView(), ImageInterpolation_Bilinear); 2496 //stack_.Render(buffer, GetView(), ImageInterpolation_Bilinear);
2193 2497
2194 // As in GrayscaleFrameRenderer => TODO MERGE? 2498 // As in GrayscaleFrameRenderer => TODO MERGE?
2195 2499
2196 float windowCenter, windowWidth; 2500 float windowCenter, windowWidth;
2197 if (!stack_.GetWindowing(windowCenter, windowWidth)) 2501 stack_.GetWindowingWithDefault(windowCenter, windowWidth);
2198 {
2199 windowCenter = 128;
2200 windowWidth = 256;
2201 }
2202 2502
2203 float x0 = windowCenter - windowWidth / 2.0f; 2503 float x0 = windowCenter - windowWidth / 2.0f;
2204 float x1 = windowCenter + windowWidth / 2.0f; 2504 float x1 = windowCenter + windowWidth / 2.0f;
2205 2505
2206 if (windowWidth >= 0.001f) // Avoid division by zero at (*) 2506 if (windowWidth >= 0.001f) // Avoid division by zero at (*)
2295 2595
2296 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, 2596 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
2297 const ViewportGeometry& view, 2597 const ViewportGeometry& view,
2298 MouseButton button, 2598 MouseButton button,
2299 KeyboardModifiers modifiers, 2599 KeyboardModifiers modifiers,
2600 int viewportX,
2601 int viewportY,
2300 double x, 2602 double x,
2301 double y, 2603 double y,
2302 IStatusBar* statusBar) 2604 IStatusBar* statusBar)
2303 { 2605 {
2304 switch (application_.currentTool_) { 2606 switch (application_.currentTool_) {