From 4d2a81f34b3ae0c0d1410c69f24dddf4e708a5a7 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 7 Nov 2018 16:14:33 +0300 Subject: [PATCH] viz: move to opencv_contrib https://github.com/opencv/opencv/commit/787f5236a95f9669ea9ebde11888b3ac2216b535 --- modules/viz/CMakeLists.txt | 38 + modules/viz/doc/images/cpw1.png | Bin 0 -> 4321 bytes modules/viz/doc/images/cpw2.png | Bin 0 -> 3548 bytes modules/viz/doc/images/cpw3.png | Bin 0 -> 17724 bytes modules/viz/doc/images/cube_widget.png | Bin 0 -> 6507 bytes modules/viz/include/opencv2/viz.hpp | 84 ++ modules/viz/include/opencv2/viz/types.hpp | 387 ++++++ modules/viz/include/opencv2/viz/viz3d.hpp | 353 ++++++ modules/viz/include/opencv2/viz/vizcore.hpp | 226 ++++ .../include/opencv2/viz/widget_accessor.hpp | 89 ++ modules/viz/include/opencv2/viz/widgets.hpp | 850 +++++++++++++ modules/viz/src/clouds.cpp | 530 ++++++++ modules/viz/src/precomp.hpp | 343 +++++ modules/viz/src/shapes.cpp | 1107 +++++++++++++++++ modules/viz/src/types.cpp | 228 ++++ modules/viz/src/viz3d.cpp | 156 +++ modules/viz/src/vizcore.cpp | 352 ++++++ modules/viz/src/vizimpl.cpp | 622 +++++++++ modules/viz/src/vizimpl.hpp | 146 +++ modules/viz/src/vtk/vtkCloudMatSink.cpp | 174 +++ modules/viz/src/vtk/vtkCloudMatSink.h | 88 ++ modules/viz/src/vtk/vtkCloudMatSource.cpp | 286 +++++ modules/viz/src/vtk/vtkCloudMatSource.h | 96 ++ modules/viz/src/vtk/vtkCocoaInteractorFix.mm | 226 ++++ modules/viz/src/vtk/vtkImageMatSource.cpp | 143 +++ modules/viz/src/vtk/vtkImageMatSource.h | 82 ++ modules/viz/src/vtk/vtkOBJWriter.cpp | 270 ++++ modules/viz/src/vtk/vtkOBJWriter.h | 91 ++ modules/viz/src/vtk/vtkTrajectorySource.cpp | 110 ++ modules/viz/src/vtk/vtkTrajectorySource.h | 84 ++ modules/viz/src/vtk/vtkVizInteractorStyle.cpp | 1076 ++++++++++++++++ modules/viz/src/vtk/vtkVizInteractorStyle.hpp | 169 +++ modules/viz/src/vtk/vtkXYZReader.cpp | 107 ++ modules/viz/src/vtk/vtkXYZReader.h | 80 ++ modules/viz/src/vtk/vtkXYZWriter.cpp | 122 ++ modules/viz/src/vtk/vtkXYZWriter.h | 90 ++ modules/viz/src/widget.cpp | 352 ++++++ modules/viz/test/test_common.cpp | 27 + modules/viz/test/test_common.hpp | 87 ++ modules/viz/test/test_main.cpp | 6 + modules/viz/test/test_precomp.hpp | 10 + modules/viz/test/test_tutorial2.cpp | 58 + modules/viz/test/test_tutorial3.cpp | 64 + modules/viz/test/test_viz3d.cpp | 65 + modules/viz/test/tests_simple.cpp | 454 +++++++ 45 files changed, 9928 insertions(+) create mode 100644 modules/viz/CMakeLists.txt create mode 100644 modules/viz/doc/images/cpw1.png create mode 100644 modules/viz/doc/images/cpw2.png create mode 100644 modules/viz/doc/images/cpw3.png create mode 100644 modules/viz/doc/images/cube_widget.png create mode 100644 modules/viz/include/opencv2/viz.hpp create mode 100644 modules/viz/include/opencv2/viz/types.hpp create mode 100644 modules/viz/include/opencv2/viz/viz3d.hpp create mode 100644 modules/viz/include/opencv2/viz/vizcore.hpp create mode 100644 modules/viz/include/opencv2/viz/widget_accessor.hpp create mode 100644 modules/viz/include/opencv2/viz/widgets.hpp create mode 100644 modules/viz/src/clouds.cpp create mode 100644 modules/viz/src/precomp.hpp create mode 100644 modules/viz/src/shapes.cpp create mode 100644 modules/viz/src/types.cpp create mode 100644 modules/viz/src/viz3d.cpp create mode 100644 modules/viz/src/vizcore.cpp create mode 100644 modules/viz/src/vizimpl.cpp create mode 100644 modules/viz/src/vizimpl.hpp create mode 100644 modules/viz/src/vtk/vtkCloudMatSink.cpp create mode 100644 modules/viz/src/vtk/vtkCloudMatSink.h create mode 100644 modules/viz/src/vtk/vtkCloudMatSource.cpp create mode 100644 modules/viz/src/vtk/vtkCloudMatSource.h create mode 100644 modules/viz/src/vtk/vtkCocoaInteractorFix.mm create mode 100644 modules/viz/src/vtk/vtkImageMatSource.cpp create mode 100644 modules/viz/src/vtk/vtkImageMatSource.h create mode 100644 modules/viz/src/vtk/vtkOBJWriter.cpp create mode 100644 modules/viz/src/vtk/vtkOBJWriter.h create mode 100644 modules/viz/src/vtk/vtkTrajectorySource.cpp create mode 100644 modules/viz/src/vtk/vtkTrajectorySource.h create mode 100644 modules/viz/src/vtk/vtkVizInteractorStyle.cpp create mode 100644 modules/viz/src/vtk/vtkVizInteractorStyle.hpp create mode 100644 modules/viz/src/vtk/vtkXYZReader.cpp create mode 100644 modules/viz/src/vtk/vtkXYZReader.h create mode 100644 modules/viz/src/vtk/vtkXYZWriter.cpp create mode 100644 modules/viz/src/vtk/vtkXYZWriter.h create mode 100644 modules/viz/src/widget.cpp create mode 100644 modules/viz/test/test_common.cpp create mode 100644 modules/viz/test/test_common.hpp create mode 100644 modules/viz/test/test_main.cpp create mode 100644 modules/viz/test/test_precomp.hpp create mode 100644 modules/viz/test/test_tutorial2.cpp create mode 100644 modules/viz/test/test_tutorial3.cpp create mode 100644 modules/viz/test/test_viz3d.cpp create mode 100644 modules/viz/test/tests_simple.cpp diff --git a/modules/viz/CMakeLists.txt b/modules/viz/CMakeLists.txt new file mode 100644 index 000000000..1f1e1af3b --- /dev/null +++ b/modules/viz/CMakeLists.txt @@ -0,0 +1,38 @@ +if(NOT HAVE_VTK) + ocv_module_disable(viz) +endif() + +set(the_description "Viz") +include(${VTK_USE_FILE}) + +if(NOT BUILD_SHARED_LIBS) + # We observed conflict between builtin 3rdparty libraries and + # system-wide similar libraries (but with different versions) from VTK dependencies + set(_conflicts "") + foreach(dep ${VTK_LIBRARIES}) + if(("${dep}" MATCHES "libz\\." AND BUILD_ZLIB) + OR ("${dep}" MATCHES "libjpeg\\." AND BUILD_JPEG) + OR ("${dep}" MATCHES "libpng\\." AND BUILD_PNG) + OR ("${dep}" MATCHES "libtiff\\." AND BUILD_TIFF) + ) + list(APPEND _conflicts "${dep}") + endif() + endforeach() + if(_conflicts) + message(STATUS "Disabling VIZ module due to conflicts with VTK dependencies: ${_conflicts}") + ocv_module_disable(viz) + endif() +endif() + +ocv_warnings_disable(CMAKE_CXX_FLAGS -Winconsistent-missing-override -Wsuggest-override) + +ocv_define_module(viz opencv_core WRAP python) +ocv_target_link_libraries(${the_module} LINK_PRIVATE ${VTK_LIBRARIES}) + +if(APPLE AND BUILD_opencv_viz) + ocv_target_link_libraries(${the_module} LINK_PRIVATE "-framework Cocoa") +endif() + +if(TARGET opencv_test_viz) + set_target_properties(opencv_test_viz PROPERTIES MACOSX_BUNDLE TRUE) +endif() diff --git a/modules/viz/doc/images/cpw1.png b/modules/viz/doc/images/cpw1.png new file mode 100644 index 0000000000000000000000000000000000000000..985b1eeaffbed1e207128eaa8167cde04d3dcf04 GIT binary patch literal 4321 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 literal 0 HcmV?d00001 diff --git a/modules/viz/doc/images/cpw2.png b/modules/viz/doc/images/cpw2.png new file mode 100644 index 0000000000000000000000000000000000000000..5733a6af6579373d6b9512b70cd5ada26b62db3d GIT binary patch literal 3548 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 literal 0 HcmV?d00001 diff --git a/modules/viz/doc/images/cpw3.png b/modules/viz/doc/images/cpw3.png new file mode 100644 index 0000000000000000000000000000000000000000..585b836b66ddfdbfa34219972f9c766eccb5f9d4 GIT binary patch literal 17724 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

gB=@dpOOp^?a9R9TDL$j)- zMXOuI1HZR%h@Qv%*6b@~Dzl3CHF6(_6hN@Pn5ESv@|UTNF@KR5V80 zeqQbSQC<-?VBq<9-g?orw&{7rquy$+mTtD{C~19`EHgAs3SMqCYfQ?quZO7#yGC)7v?)rzgR4l89MG2c zc&Yb1DNMd>9g-_Y4kOg4EhZj_ktSHNGYy~%Vy^kmRIhj(d z&buf!6zpinUc1qYuI4r+@U!IR$M)Kd32JEdzh=ywF6X~bnW6`%e<6b`H(9T}zuXz) zD$q|T>}Yg@sd+_BeP|SoT4NLA{cZCBrG4h@vc;=W_NJne>CAocLr%)#wx;ti_o!yLg0JF`UWOn$p=}DJU z%?I;CSdrj-t=YEw;}JYjK{1#Ka$D_L1^jzffBZ!U&v+(cb=>@Xeij+s!k#krUdGj$ zm~9$2TcjPL-o06qQP`G>{3q+!`5_L;+oS$}qLg~$)X%bVZYCX>?4((H@i|`c63jom zh0V&tZxPB-v*-&m#~Ca22gdsPL?smnHGF&@_j3x&VJLc+jL{3HsN%zr@Fq_AlKJ@UVEZVh9RXW1!)b9 zM0~m{XqT=B2mZ%z|p7*2`oMvA$r?AU+-ujC=p<7XAng1 z0YpJV{s-pzEytS|-Bvp|SBW%@Xzg%6YYLKysE)mfq^%z~+Xgx>|Ag-#$hu4VDlxy4 zl0Ds+=hN=vTCF*nrDc9wh?kUwEXw`kkhv!1+5eUnzS>kBu2e_5v#hXFF-FQzg2xO5?Aa> z@y*024CT%xeU`U0?%yyqxL+!2dhpHTl1Ga0)olCFQwY1HE5iR;{qAp%7Xk>( z-T>_vs2fT9Xtqhm?<_y^+maaLL@?GH7(+n zLK35fg@W_1t*yL6RH$WcGRxSadQE>|m`qki|=%l-| zbE8`l1FP|9LdDo|Of5Mb9ab1`2m-E4Al4$W;Zh4Ffu@GWasBRM01_{|@8`SqTGPy{ zor$C&uh|P*0aWFO0Mx%hBmSjv(3<8=V*gSLKBC9&6wAX&1OrCpCdcxa*8Gk&q9%X^k`+j=Go`4zw znStr%je*87etcKBikbN<5?9~R{$EpQaDLS8AkOBhC3vQFJem40lvDBIaj2dq>wtOl z{Sc$jV^KK?TO$7*hV|7Ri4z}zz#kx^9DMiFLRZ~ee4*`mc?Mfu-Z6hI#X1`s8^e;~ zl5Ue0GB4HKjjNgj{L0|vgu&Q%p})P&zK8UGc|TypNB51%$#Nk%Hk~wA=r|b{h=`2V z;25HjNe?5>MB>M83Jf+m`xeH}dc&t)V~c-y-QC^MwH@a{;k)tAPoyvyj>P@U7mGg3 zkot$1Q&1p`j_-9XpnwhNfOiMNk$9iCX*8RL6RgIU!6dYgXV(#6js0(OzFL;f0fA8j z`~$6by4QzChBKx+_eBt{P#IGrfBvG;&$XtB=0_Qpf~}d2T(ccBaQ!hSTN1%Zq!W|9 zx;j8v_!v4bl}3ykKJfW|#5XnuWb`wk5Jd002{v*bB<7Ltxl4C_KBVz!i`Y zD~RDe01)zX>gzqaq_m`j{i0!x->nzn#pss7TNW_ELA) z=yfcK{ZZ!Uj~QKc^*__j2U^dA+aFz)78V8N(uDg<>$>9tro(eYj^Ae48eYffQvs|; zQJfLiMyOB-yipo_GV+=@_RKiw58wA2648hh7R$xb;;c5e8v~Hq-_l;nA^MaW4P=>! zdapawqz!eemsatf%rZP%un*2@uLw#8b8J3O?W6KcL#1TXA|7yRIMCJKn&(K3Ch31s z$*ZYBZ6_$nGWcF%U|;|p(rE5mXlp*8ch6ur7&_Q%inz_S^)d+x^@DJ94?ypSDTbe) zPu<{d2Z?I3+&97swmYoqH$}0ee<^In!HO{LuK%6yMeB1CORLNuz~{Yk!|#wqF&kERi-7H7e#Are81hHLx(zjBAQJTqTiH_0#w2 z&3~+M=bU%jjhWcuO=@E2QyJZAHt2hH z_jZ@9S#pKcgS~8;9mGD~i1l)?YcR<%l>uM}o*#~gg!s{7`3i$6d+sKJ^c}#G&UpNS zWvP~+7OP4?yvQ-TdpXLs{korv4niA`_<+ZcE25c;rLVSI){k3IElxwV2HUQms&TJB zXg5>mTywcfillM;^L;w6qq!!gkbq&vSrC0Q^4;SU5kpdc3Dt;va( z7G`QCF`mv5pi--vrv&1Z>0}1Q<6k9TT&v9o<^UabG2@>H-Ix8W*~p2#H&}IAU9NN{ z6R@^$qHJ96s#W;(WGPYdFNp%E~y{pobm8+Xu`fYy|))|lWAFQ`J@;z?{gy4G|18t9lC^irJ=lN14I5;?6&kGi&B>lU?2`<;m z0w!pZoJ1#y^}r72S&53=f7OqlEO>eipdg=*Yv-*;HHJ`8JeVMB?a2&~gUc#vwSYP^ zaJgu*`k|;{r2*#Pz-zU^90%8B1`F7Rgri+W>M{=Z&8N_zJ)jL)z_H9+yt+RUFXFcv z$+r1C&NS5%v%lLDu)3)DGWlJc|&2a@6SVgyf6FlmX?+WT{kM_s$n?(=AL{bzD{2< zq$RS^+7KXKZ)>VJQkmATtBH#(jTnRIdN0R*p8_dUh&X+Wjy%$N*(WqRZkydDWn~`k zH$x)ouj5CPnN<5Hz-CW~0XQ@mfdJ7@v@+XyQGJ(hx4HYn8fSvla1dSz9g1R3;D#RT z>m~b;vVp>!;@qB2_&ZePkb3rmwq;Nd&Uc`&rMOSBZ{mg6fIi{g&2w|tLuLFgIaPF%LG0^Q%W9c4W(igsOG`9vD0y97 zAQ?#On}-+PdcQqNX!4MLfU}v=omHNhT7OD5`ZX1a7}F_pSizcB+TN+BC}*=pFD(L< zK-US0V_`GQucN3lYu?hD*%!IXNINFJyc&_3PAqG0V3~dYXLAurAW+Nhhk!{H!yYy`zrJ z>XlFeY}sb=#N&R+Ivx^QI?f9!dHguEolZv?j#1}3xgq#*=7}HH_*uFeTBym%PXDYH zKF%BF%%tI*6N8@nQ0$U%AQ1kF^XH#Hm&O*rBwyFw?*b9Nqoh zf1A*LLX&GP>CAnFBAh={Rz~^HjzatAl*q(%u8@dfS}+P^-_x2c-wolz_!vFABY0|8 zYJ|8mMc>_>l724aFLl2hMEfR&#$e0@wFsQPSuSJ_k{FWz8J>FRw3DxAs&X+;9iGqg z&aJAD5I8i`QiZzEhWV~QX;m~HS5sl3@h4D1A5F1s;i7vXvK$}u!YOFNCH|d<674)r zI9;rx7}p=Y774G@@UI)`4mJeq9040hY z%o~1ffcgLe z!xJEC0f}JP$ulf7;dMHe|GuT6t^kS~Iq}n8cHGvV;N4Fq?c4DwbH^BBrnSb?YNawj zx!Z61uaLGeAQv=4HotUm0j=qUx*|BcZ8g`ek5Q`Xl;#qjstY2lpyo{vsdEi~YwPtk znjgYtj&!Uho7_!?Vw|(sCotCTFn*EW2a;(FQyY1Msh=qM6$$e*Xlerc-R0%3ub*p_ zwSd9qbUfYG#u-k4h^&8+?LAml786`QQPB1W0?zJ!rqn?c4?l?vGQ$BMEZKn>wz^H*KC4-dS}OsrTI>_0kb?EYPxrza6%dQyJfXm|ZOH|0Z1 zto=RSCYY6+?0VJtQe)xy#CJayZ? z3<6@Ve*sXYRse6y?%|3Ubwmc#s_yQs&>jr`I$5ki%Uw54o#&468hRl04n`9LAQ2=x zX(Q)?*lhY|#PUm^X=unPKzp7p3^ym824u)hO01m9VAu~j9PK>X!+?3s>1lfZZ&k3v z*ov*|jTQ@(cQ@MYE+e-9;zDx6(SQaf)CB1c(pr0dCTj_9nIQ_TAvxZTc5|zy}FAE`sA;{-0fGQb2`(RO64H zRMRXNzf$!%&7d@JD(EkE-XEUo)HVhUwhK*g>u zdvCM|ooNNlDcHn&{e*ae=V-o5qiyiLgn8}XP4Oi^2&$&P_4vN;5kzA%2&%Hi{5Y;7 zsFWV5Hg?Ttsk2uNw*howgg>Sr`uIqWaOBGfJdShecY8~Zc@0;Y6@E9EpvOSVAPk4vlDvRT>XL{} zfGwVW5EcMcaBv43$+|x3zNPwZbDHwTEg2r9SM?fBeKYN940`b+$~+(Uk1UosG~=Q1 z!J*^pGR$|`ZfP8b5u7_$-Cv*At{0n~nn_y#w+wP|ap8dEzT9PB_xG^;VAgBfOEBqw z?|}fR7Z+CM=GB+RGCNxo6J;;L#J2~p5TlL>aeI5GKde&*aSq(8?o*8K73Jt1(4hm; z+;lDO{IZrqzWV2#Pz!lQt&EG}Ee?+-3P|*+v)!A>?hhJhec?_m_c%BczLya>(utCt zPO}-}3w5{TyrRy|OwOa*yE`01-?z1`{z9V`b1cI7l|g@Cdhvf6JT51Q?^BM>N){V) za<p4yo>a;{xIoJbE34^K@ikkJEx+ta!u(j6+3e5i2vA#syx5)m!1H+hH2 z-S34~OjgVEE3;cW)-5a89MBT)=@-u({1&l8ofz~iz~>+z{u{2Xu7OTzt6#)%WYzUMyAh4pVd5=^lE{oSixQL{^O zW1(df`^kt`yz`tALr#{nnRAUyHX{YNf@5EJ*n3};5hqrul_HkD(aG&w{z`KHr+mz| zon>p1)A;S%zGHMdd_sOnIkkxcgqx&ny@V{MY^`|~)9Z##G z31g~T7s0L8=}dE^-5VhQt_4Ke8fW!|g(67}MFo0oi~3_!P(hsdqA42*?iC znyk_81_Ebie`UE5$A776`{ldZ5Qn<~C0V0>AEe`582MB>TQs9qK+F{MZTZ@xb2x<_ zKtcYDDEWD{#L$p#9Lp0Q%op{afT5e=`un1K4Mc z@IyU;&Q7%My8;Nvx90yo7hwMp7?w^klx6Ynh)QSQ$K1VfFH^;_l0ExsNs2K^XBWA z?k6!VK$R9+%Ir3+*)XB8ZaGfz92^|1qyWf+{e;IZ)<1n}V%iDx^5U5LVP46w7Legm z4MpLlw~3_jyq(wAQ8Q&r^&8aw7gjY6fND~f=V3kr9Z(ay@CV;$1s8=&(RBYkq{$D7 zu*)eem3VZ#Xk41LA@F(DpYs4Z9cyBd-xCdiq5~Lb=B?nqO4bjpVO@5hRHAS60t|I@ z%zkfhWrt!7zclI4Y|l5R47`L6|QI7@^S<|mOvw$ZQZVKMvTCE z#VIKqc#z|7uR%w|b_;1r>L&KvJ%Hg#+5))1`et`PLk$2CxO_ex*L}(8ShibTJRvMV zS#BFrr9YNLV{qejwpeL66lHB?WfUPo9IG?>;~n7mM2TaGghDhP>}rYLPs^(}+g+X!3y?NBKcDw@M!tZ}snl*~T~opfAz8}1{_*;M#$7A$#R@n!adP>bLp@&& z7#{4|y7lWNZ++gXGT`{u{onV#uSlBj4IBx406K%9f$cEk3*O3qpHAyfpE5_+0V zPZG&G`ugveO!nK%-`fj^Uxd!{ZaXa%Kf}1}K!>2( z;q$iN?--x6a9-vh*7hA30Yyf@5G-D6`9-WMGdL?Yb}lfzUkfM&6)*+=em?*IfZd+K zcg`H=%rn+4ptBkJ(tDn2yDYT$ z-T&2B-p{i9g+K80jsD}0^Y{Hs1FnUh`Yt63t50!3OH1o%4>QewNjyF zQvmR^f(Z*>giPX8JiL8!vEUTl)SgaZ54ICkGmbFKIw`YB_T`Dsw{G21khHttVm{|f z*$Jg3lYN%0TJ`GVarx=bCwSQLI{+8x=C|j#R2a={YisM3Hg~!!Iqxv%RVU!e%?U?2 zZq?{*T6Q{(Ik@ET&3k-1TY%dtJ6%?-Uj6vvjuY$E4&7o~aP{qj37dgqRu?lubV}-f zJZxtU&=oHTp5?6Ptha=Bjzb7L-9Y=9qyO1!m5)w)^J=p%@ca`7Pgg&ebxsLQ0QrLz AnE(I) literal 0 HcmV?d00001 diff --git a/modules/viz/doc/images/cube_widget.png b/modules/viz/doc/images/cube_widget.png new file mode 100644 index 0000000000000000000000000000000000000000..ffe56811aedb6fc57e77097e6b2840cdd6bb64e4 GIT binary patch literal 6507 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 V6?X0&0K6`tFw`^Ct<-jn{U22ce2xGB literal 0 HcmV?d00001 diff --git a/modules/viz/include/opencv2/viz.hpp b/modules/viz/include/opencv2/viz.hpp new file mode 100644 index 000000000..fc79b8b60 --- /dev/null +++ b/modules/viz/include/opencv2/viz.hpp @@ -0,0 +1,84 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef OPENCV_VIZ_HPP +#define OPENCV_VIZ_HPP + +#include +#include +#include +#include + +/** + @defgroup viz 3D Visualizer + +This section describes 3D visualization window as well as classes and methods that are used to +interact with it. + +3D visualization window (see Viz3d) is used to display widgets (see Widget), and it provides several +methods to interact with scene and widgets. + + @{ + @defgroup viz_widget Widget + +In this section, the widget framework is explained. Widgets represent 2D or 3D objects, varying from +simple ones such as lines to complex ones such as point clouds and meshes. + +Widgets are **implicitly shared**. Therefore, one can add a widget to the scene, and modify the +widget without re-adding the widget. + +@code +// Create a cloud widget +viz::WCloud cw(cloud, viz::Color::red()); +// Display it in a window +myWindow.showWidget("CloudWidget1", cw); +// Modify it, and it will be modified in the window. +cw.setColor(viz::Color::yellow()); +@endcode + + @} +*/ + +#endif /* OPENCV_VIZ_HPP */ diff --git a/modules/viz/include/opencv2/viz/types.hpp b/modules/viz/include/opencv2/viz/types.hpp new file mode 100644 index 000000000..62068ac89 --- /dev/null +++ b/modules/viz/include/opencv2/viz/types.hpp @@ -0,0 +1,387 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef OPENCV_VIZ_TYPES_HPP +#define OPENCV_VIZ_TYPES_HPP + +#include +#include +#include + +namespace cv +{ + namespace viz + { + +//! @addtogroup viz +//! @{ + + /** @brief This class represents color in BGR order. + */ + class Color : public Scalar + { + public: + Color(); + //! The three channels will have the same value equal to gray. + Color(double gray); + Color(double blue, double green, double red); + + Color(const Scalar& color); + + operator Vec3b() const; + + static Color black(); + static Color blue(); + static Color green(); + static Color cyan(); + + static Color red(); + static Color magenta(); + static Color yellow(); + static Color white(); + + static Color gray(); + + static Color mlab(); + + static Color navy(); + static Color olive(); + static Color maroon(); + static Color teal(); + static Color rose(); + static Color azure(); + static Color lime(); + static Color gold(); + static Color brown(); + static Color orange(); + static Color chartreuse(); + static Color orange_red(); + static Color purple(); + static Color indigo(); + + static Color pink(); + static Color cherry(); + static Color bluberry(); + static Color raspberry(); + static Color silver(); + static Color violet(); + static Color apricot(); + static Color turquoise(); + static Color celestial_blue(); + static Color amethyst(); + + static Color not_set(); + }; + + /** @brief This class wraps mesh attributes, and it can load a mesh from a ply file. : + */ + class CV_EXPORTS Mesh + { + public: + enum { + LOAD_AUTO = 0, + LOAD_PLY = 1, + LOAD_OBJ = 2 + }; + + Mat cloud; //!< point coordinates of type CV_32FC3 or CV_64FC3 with only 1 row + Mat colors; //!< point color of type CV_8UC3 or CV_8UC4 with only 1 row + Mat normals; //!< point normals of type CV_32FC3, CV_32FC4, CV_64FC3 or CV_64FC4 with only 1 row + + //! Raw integer list of the form: (n,id1,id2,...,idn, n,id1,id2,...,idn, ...) + //! where n is the number of points in the polygon, and id is a zero-offset index into an associated cloud. + Mat polygons; //!< CV_32SC1 with only 1 row + + Mat texture; + Mat tcoords; //!< CV_32FC2 or CV_64FC2 with only 1 row + + /** @brief Loads a mesh from a ply or a obj file. + + @param file File name + @param type File type (for now only PLY and OBJ are supported) + + **File type** can be one of the following: + - **LOAD_PLY** + - **LOAD_OBJ** + */ + static Mesh load(const String& file, int type = LOAD_PLY); + + }; + + /** @brief This class wraps intrinsic parameters of a camera. + + It provides several constructors that can extract the intrinsic parameters from field of + view, intrinsic matrix and projection matrix. : + */ + class CV_EXPORTS Camera + { + public: + + /** @brief Constructs a Camera. + + @param fx Horizontal focal length. + @param fy Vertical focal length. + @param cx x coordinate of the principal point. + @param cy y coordinate of the principal point. + @param window_size Size of the window. This together with focal length and principal + point determines the field of view. + */ + Camera(double fx, double fy, double cx, double cy, const Size &window_size); + + /** @overload + @param fov Field of view (horizontal, vertical) + @param window_size Size of the window. Principal point is at the center of the window + by default. + */ + Camera(const Vec2d &fov, const Size &window_size); + + /** @overload + @param K Intrinsic matrix of the camera with the following form + \f[ + \begin{bmatrix} + f_x & 0 & c_x\\ + 0 & f_y & c_y\\ + 0 & 0 & 1\\ + \end{bmatrix} + \f] + @param window_size Size of the window. This together with intrinsic matrix determines + the field of view. + */ + Camera(const Matx33d &K, const Size &window_size); + + /** @overload + @param proj Projection matrix of the camera with the following form + \f[ + \begin{bmatrix} + \frac{2n}{r-l} & 0 & \frac{r+l}{r-l} & 0\\ + 0 & \frac{2n}{t-b} & \frac{t+b}{t-b} & 0\\ + 0 & 0 & -\frac{f+n}{f-n} & -\frac{2fn}{f-n}\\ + 0 & 0 & -1 & 0\\ + \end{bmatrix} + \f] + + @param window_size Size of the window. This together with projection matrix determines + the field of view. + */ + explicit Camera(const Matx44d &proj, const Size &window_size); + + const Vec2d & getClip() const { return clip_; } + void setClip(const Vec2d &clip) { clip_ = clip; } + + const Size & getWindowSize() const { return window_size_; } + void setWindowSize(const Size &window_size); + + const Vec2d& getFov() const { return fov_; } + void setFov(const Vec2d& fov) { fov_ = fov; } + + const Vec2d& getPrincipalPoint() const { return principal_point_; } + const Vec2d& getFocalLength() const { return focal_; } + + /** @brief Computes projection matrix using intrinsic parameters of the camera. + + + @param proj Output projection matrix with the following form + \f[ + \begin{bmatrix} + \frac{2n}{r-l} & 0 & \frac{r+l}{r-l} & 0\\ + 0 & \frac{2n}{t-b} & \frac{t+b}{t-b} & 0\\ + 0 & 0 & -\frac{f+n}{f-n} & -\frac{2fn}{f-n}\\ + 0 & 0 & -1 & 0\\ + \end{bmatrix} + \f] + */ + void computeProjectionMatrix(Matx44d &proj) const; + + /** @brief Creates a Kinect Camera with + - fx = fy = 525 + - cx = 320 + - cy = 240 + + @param window_size Size of the window. This together with intrinsic matrix of a Kinect Camera + determines the field of view. + */ + static Camera KinectCamera(const Size &window_size); + + private: + void init(double fx, double fy, double cx, double cy, const Size &window_size); + + /** The near plane and the far plane. + * - clip_[0]: the near plane; default value is 0.01 + * - clip_[1]: the far plane; default value is 1000.01 + */ + Vec2d clip_; + + /** + * Field of view. + * - fov_[0]: horizontal(x-axis) field of view in radians + * - fov_[1]: vertical(y-axis) field of view in radians + */ + Vec2d fov_; + + /** Window size.*/ + Size window_size_; + + /** + * Principal point. + * - principal_point_[0]: cx + * - principal_point_[1]: cy + */ + Vec2d principal_point_; + /** + * Focal length. + * - focal_[0]: fx + * - focal_[1]: fy + */ + Vec2d focal_; + }; + + /** @brief This class represents a keyboard event. + */ + class CV_EXPORTS KeyboardEvent + { + public: + enum { NONE = 0, ALT = 1, CTRL = 2, SHIFT = 4 }; + enum Action { KEY_UP = 0, KEY_DOWN = 1 }; + + /** @brief Constructs a KeyboardEvent. + + @param action Signals if key is pressed or released. + @param symbol Name of the key. + @param code Code of the key. + @param modifiers Signals if alt, ctrl or shift are pressed or their combination. + */ + KeyboardEvent(Action action, const String& symbol, unsigned char code, int modifiers); + + Action action; + String symbol; + unsigned char code; + int modifiers; + }; + + /** @brief This class represents a mouse event. + */ + class CV_EXPORTS MouseEvent + { + public: + enum Type { MouseMove = 1, MouseButtonPress, MouseButtonRelease, MouseScrollDown, MouseScrollUp, MouseDblClick } ; + enum MouseButton { NoButton = 0, LeftButton, MiddleButton, RightButton, VScroll } ; + + /** @brief Constructs a MouseEvent. + + @param type Type of the event. This can be **MouseMove**, **MouseButtonPress**, + **MouseButtonRelease**, **MouseScrollDown**, **MouseScrollUp**, **MouseDblClick**. + @param button Mouse button. This can be **NoButton**, **LeftButton**, **MiddleButton**, + **RightButton**, **VScroll**. + @param pointer Position of the event. + @param modifiers Signals if alt, ctrl or shift are pressed or their combination. + */ + MouseEvent(const Type& type, const MouseButton& button, const Point& pointer, int modifiers); + + Type type; + MouseButton button; + Point pointer; + int modifiers; + }; + +//! @} viz + + } /* namespace viz */ +} /* namespace cv */ + +//! @cond IGNORED + +////////////////////////////////////////////////////////////////////////////////////////////////////// +/// cv::viz::Color + +inline cv::viz::Color::Color() : Scalar(0, 0, 0) {} +inline cv::viz::Color::Color(double _gray) : Scalar(_gray, _gray, _gray) {} +inline cv::viz::Color::Color(double _blue, double _green, double _red) : Scalar(_blue, _green, _red) {} +inline cv::viz::Color::Color(const Scalar& color) : Scalar(color) {} + +inline cv::viz::Color::operator cv::Vec3b() const { return cv::Vec3d(val); } + +inline cv::viz::Color cv::viz::Color::black() { return Color( 0, 0, 0); } +inline cv::viz::Color cv::viz::Color::green() { return Color( 0, 255, 0); } +inline cv::viz::Color cv::viz::Color::blue() { return Color(255, 0, 0); } +inline cv::viz::Color cv::viz::Color::cyan() { return Color(255, 255, 0); } +inline cv::viz::Color cv::viz::Color::red() { return Color( 0, 0, 255); } +inline cv::viz::Color cv::viz::Color::yellow() { return Color( 0, 255, 255); } +inline cv::viz::Color cv::viz::Color::magenta() { return Color(255, 0, 255); } +inline cv::viz::Color cv::viz::Color::white() { return Color(255, 255, 255); } +inline cv::viz::Color cv::viz::Color::gray() { return Color(128, 128, 128); } + +inline cv::viz::Color cv::viz::Color::mlab() { return Color(255, 128, 128); } + +inline cv::viz::Color cv::viz::Color::navy() { return Color(0, 0, 128); } +inline cv::viz::Color cv::viz::Color::olive() { return Color(0, 128, 128); } +inline cv::viz::Color cv::viz::Color::maroon() { return Color(0, 0, 128); } +inline cv::viz::Color cv::viz::Color::teal() { return Color(128, 128, 0); } +inline cv::viz::Color cv::viz::Color::rose() { return Color(128, 0, 255); } +inline cv::viz::Color cv::viz::Color::azure() { return Color(255, 128, 0); } +inline cv::viz::Color cv::viz::Color::lime() { return Color(0, 255, 191); } +inline cv::viz::Color cv::viz::Color::gold() { return Color(0, 215, 255); } +inline cv::viz::Color cv::viz::Color::brown() { return Color(42, 42, 165); } +inline cv::viz::Color cv::viz::Color::orange() { return Color(0, 165, 255); } +inline cv::viz::Color cv::viz::Color::chartreuse() { return Color(0, 255, 128); } +inline cv::viz::Color cv::viz::Color::orange_red() { return Color(0, 69, 255); } +inline cv::viz::Color cv::viz::Color::purple() { return Color(128, 0, 128); } +inline cv::viz::Color cv::viz::Color::indigo() { return Color(130, 0, 75); } + +inline cv::viz::Color cv::viz::Color::pink() { return Color(203, 192, 255); } +inline cv::viz::Color cv::viz::Color::cherry() { return Color( 99, 29, 222); } +inline cv::viz::Color cv::viz::Color::bluberry() { return Color(247, 134, 79); } +inline cv::viz::Color cv::viz::Color::raspberry() { return Color( 92, 11, 227); } +inline cv::viz::Color cv::viz::Color::silver() { return Color(192, 192, 192); } +inline cv::viz::Color cv::viz::Color::violet() { return Color(226, 43, 138); } +inline cv::viz::Color cv::viz::Color::apricot() { return Color(177, 206, 251); } +inline cv::viz::Color cv::viz::Color::turquoise() { return Color(208, 224, 64); } +inline cv::viz::Color cv::viz::Color::celestial_blue() { return Color(208, 151, 73); } +inline cv::viz::Color cv::viz::Color::amethyst() { return Color(204, 102, 153); } + +inline cv::viz::Color cv::viz::Color::not_set() { return Color(-1, -1, -1); } + +//! @endcond + +#endif diff --git a/modules/viz/include/opencv2/viz/viz3d.hpp b/modules/viz/include/opencv2/viz/viz3d.hpp new file mode 100644 index 000000000..1e6e9d5e5 --- /dev/null +++ b/modules/viz/include/opencv2/viz/viz3d.hpp @@ -0,0 +1,353 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef OPENCV_VIZ_VIZ3D_HPP +#define OPENCV_VIZ_VIZ3D_HPP + +#if !defined YES_I_AGREE_THAT_VIZ_API_IS_NOT_STABLE_NOW_AND_BINARY_COMPARTIBILITY_WONT_BE_SUPPORTED && !defined CVAPI_EXPORTS + //#error "Viz is in beta state now. Please define macro above to use it" +#endif + +#include +#include +#include + +namespace cv +{ + namespace viz + { + +//! @addtogroup viz +//! @{ + + /** @brief The Viz3d class represents a 3D visualizer window. This class is implicitly shared. + */ + class CV_EXPORTS Viz3d + { + public: + typedef cv::viz::Color Color; + typedef void (*KeyboardCallback)(const KeyboardEvent&, void*); + typedef void (*MouseCallback)(const MouseEvent&, void*); + + /** @brief The constructors. + + @param window_name Name of the window. + */ + Viz3d(const String& window_name = String()); + Viz3d(const Viz3d&); + Viz3d& operator=(const Viz3d&); + ~Viz3d(); + + /** @brief Shows a widget in the window. + + @param id A unique id for the widget. @param widget The widget to be displayed in the window. + @param pose Pose of the widget. + */ + void showWidget(const String &id, const Widget &widget, const Affine3d &pose = Affine3d::Identity()); + + /** @brief Removes a widget from the window. + + @param id The id of the widget that will be removed. + */ + void removeWidget(const String &id); + + /** @brief Retrieves a widget from the window. + + A widget is implicitly shared; that is, if the returned widget is modified, the changes + will be immediately visible in the window. + + @param id The id of the widget that will be returned. + */ + Widget getWidget(const String &id) const; + + /** @brief Removes all widgets from the window. + */ + void removeAllWidgets(); + + /** @brief Removed all widgets and displays image scaled to whole window area. + + @param image Image to be displayed. + @param window_size Size of Viz3d window. Default value means no change. + */ + void showImage(InputArray image, const Size& window_size = Size(-1, -1)); + + /** @brief Sets pose of a widget in the window. + + @param id The id of the widget whose pose will be set. @param pose The new pose of the widget. + */ + void setWidgetPose(const String &id, const Affine3d &pose); + + /** @brief Updates pose of a widget in the window by pre-multiplying its current pose. + + @param id The id of the widget whose pose will be updated. @param pose The pose that the current + pose of the widget will be pre-multiplied by. + */ + void updateWidgetPose(const String &id, const Affine3d &pose); + + /** @brief Returns the current pose of a widget in the window. + + @param id The id of the widget whose pose will be returned. + */ + Affine3d getWidgetPose(const String &id) const; + + /** @brief Sets the intrinsic parameters of the viewer using Camera. + + @param camera Camera object wrapping intrinsic parameters. + */ + void setCamera(const Camera &camera); + + /** @brief Returns a camera object that contains intrinsic parameters of the current viewer. + */ + Camera getCamera() const; + + /** @brief Returns the current pose of the viewer. + */ + Affine3d getViewerPose() const; + + /** @brief Sets pose of the viewer. + + @param pose The new pose of the viewer. + */ + void setViewerPose(const Affine3d &pose); + + /** @brief Resets camera viewpoint to a 3D widget in the scene. + + @param id Id of a 3D widget. + */ + void resetCameraViewpoint(const String &id); + + /** @brief Resets camera. + */ + void resetCamera(); + + /** @brief Transforms a point in world coordinate system to window coordinate system. + + @param pt Point in world coordinate system. + @param window_coord Output point in window coordinate system. + */ + void convertToWindowCoordinates(const Point3d &pt, Point3d &window_coord); + + /** @brief Transforms a point in window coordinate system to a 3D ray in world coordinate system. + + @param window_coord Point in window coordinate system. @param origin Output origin of the ray. + @param direction Output direction of the ray. + */ + void converTo3DRay(const Point3d &window_coord, Point3d &origin, Vec3d &direction); + + /** @brief Returns the current size of the window. + */ + Size getWindowSize() const; + /** @brief Sets the size of the window. + + @param window_size New size of the window. + */ + void setWindowSize(const Size &window_size); + + /** @brief Returns the name of the window which has been set in the constructor. + * `Viz - ` is prepended to the name if necessary. + */ + String getWindowName() const; + + /** @brief Returns the Mat screenshot of the current scene. + */ + cv::Mat getScreenshot() const; + + /** @brief Saves screenshot of the current scene. + + @param file Name of the file. + */ + void saveScreenshot(const String &file); + + /** @brief Sets the position of the window in the screen. + + @param window_position coordinates of the window + */ + void setWindowPosition(const Point& window_position); + + /** @brief Sets or unsets full-screen rendering mode. + + @param mode If true, window will use full-screen mode. + */ + void setFullScreen(bool mode = true); + + /** @brief Sets background color. + */ + void setBackgroundColor(const Color& color = Color::black(), const Color& color2 = Color::not_set()); + void setBackgroundTexture(InputArray image = noArray()); + void setBackgroundMeshLab(); + + /** @brief The window renders and starts the event loop. + */ + void spin(); + + /** @brief Starts the event loop for a given time. + + @param time Amount of time in milliseconds for the event loop to keep running. + @param force_redraw If true, window renders. + */ + void spinOnce(int time = 1, bool force_redraw = false); + + /** @brief Create a window in memory instead of on the screen. + */ + void setOffScreenRendering(); + + /** @brief Remove all lights from the current scene. + */ + void removeAllLights(); + + /** @brief Add a light in the scene. + + @param position The position of the light. + @param focalPoint The point at which the light is shining + @param color The color of the light + @param diffuseColor The diffuse color of the light + @param ambientColor The ambient color of the light + @param specularColor The specular color of the light + */ + void addLight(const Vec3d &position, const Vec3d &focalPoint = Vec3d(0, 0, 0), const Color &color = Color::white(), + const Color &diffuseColor = Color::white(), const Color &ambientColor = Color::black(), + const Color &specularColor = Color::white()); + + /** @brief Returns whether the event loop has been stopped. + */ + bool wasStopped() const; + void close(); + + /** @brief Sets keyboard handler. + + @param callback Keyboard callback (void (\*KeyboardCallbackFunction(const + KeyboardEvent&, void\*)). + @param cookie The optional parameter passed to the callback. + */ + void registerKeyboardCallback(KeyboardCallback callback, void* cookie = 0); + + /** @brief Sets mouse handler. + + @param callback Mouse callback (void (\*MouseCallback)(const MouseEvent&, void\*)). + @param cookie The optional parameter passed to the callback. + */ + void registerMouseCallback(MouseCallback callback, void* cookie = 0); + + /** @brief Sets rendering property of a widget. + + @param id Id of the widget. + @param property Property that will be modified. + @param value The new value of the property. + + Rendering property can be one of the following: + - **POINT_SIZE** + - **OPACITY** + - **LINE_WIDTH** + - **FONT_SIZE** + + REPRESENTATION: Expected values are + - **REPRESENTATION_POINTS** + - **REPRESENTATION_WIREFRAME** + - **REPRESENTATION_SURFACE** + + IMMEDIATE_RENDERING: + - Turn on immediate rendering by setting the value to 1. + - Turn off immediate rendering by setting the value to 0. + + SHADING: Expected values are + - **SHADING_FLAT** + - **SHADING_GOURAUD** + - **SHADING_PHONG** + */ + void setRenderingProperty(const String &id, int property, double value); + /** @brief Returns rendering property of a widget. + + @param id Id of the widget. + @param property Property. + + Rendering property can be one of the following: + - **POINT_SIZE** + - **OPACITY** + - **LINE_WIDTH** + - **FONT_SIZE** + + REPRESENTATION: Expected values are + - **REPRESENTATION_POINTS** + - **REPRESENTATION_WIREFRAME** + - **REPRESENTATION_SURFACE** + + IMMEDIATE_RENDERING: + - Turn on immediate rendering by setting the value to 1. + - Turn off immediate rendering by setting the value to 0. + + SHADING: Expected values are + - **SHADING_FLAT** + - **SHADING_GOURAUD** + - **SHADING_PHONG** + */ + double getRenderingProperty(const String &id, int property); + + /** @brief Sets geometry representation of the widgets to surface, wireframe or points. + + @param representation Geometry representation which can be one of the following: + - **REPRESENTATION_POINTS** + - **REPRESENTATION_WIREFRAME** + - **REPRESENTATION_SURFACE** + */ + void setRepresentation(int representation); + + void setGlobalWarnings(bool enabled = false); + private: + + struct VizImpl; + VizImpl* impl_; + + void create(const String &window_name); + void release(); + + friend class VizStorage; + }; + +//! @} + + } /* namespace viz */ +} /* namespace cv */ + +#endif diff --git a/modules/viz/include/opencv2/viz/vizcore.hpp b/modules/viz/include/opencv2/viz/vizcore.hpp new file mode 100644 index 000000000..85b75131d --- /dev/null +++ b/modules/viz/include/opencv2/viz/vizcore.hpp @@ -0,0 +1,226 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef OPENCV_VIZCORE_HPP +#define OPENCV_VIZCORE_HPP + +#include +#include +#include + +namespace cv +{ + namespace viz + { + +//! @addtogroup viz +//! @{ + + /** @brief Takes coordinate frame data and builds transform to global coordinate frame. + + @param axis_x X axis vector in global coordinate frame. + @param axis_y Y axis vector in global coordinate frame. + @param axis_z Z axis vector in global coordinate frame. + @param origin Origin of the coordinate frame in global coordinate frame. + + @return An affine transform that describes transformation between global coordinate frame + and a given coordinate frame. + The returned transforms can transform a point in the given coordinate frame to the global + coordinate frame. + */ + CV_EXPORTS Affine3d makeTransformToGlobal(const Vec3d& axis_x, const Vec3d& axis_y, const Vec3d& axis_z, const Vec3d& origin = Vec3d::all(0)); + + /** @brief Constructs camera pose from position, focal_point and up_vector (see gluLookAt() for more + information). + + @param position Position of the camera in global coordinate frame. + @param focal_point Focal point of the camera in global coordinate frame. + @param y_dir Up vector of the camera in global coordinate frame. + + This function returns pose of the camera in global coordinate frame. + */ + CV_EXPORTS Affine3d makeCameraPose(const Vec3d& position, const Vec3d& focal_point, const Vec3d& y_dir); + + /** @brief Retrieves a window by its name. + + @param window_name Name of the window that is to be retrieved. + + This function returns a Viz3d object with the given name. + + @note If the window with that name already exists, that window is returned. Otherwise, new window is + created with the given name, and it is returned. + + @note Window names are automatically prefixed by "Viz - " if it is not done by the user. + @code + /// window and window_2 are the same windows. + viz::Viz3d window = viz::getWindowByName("myWindow"); + viz::Viz3d window_2 = viz::getWindowByName("Viz - myWindow"); + @endcode + */ + CV_EXPORTS Viz3d getWindowByName(const String &window_name); + + //! Unregisters all Viz windows from internal database. After it 'getWindowByName()' will create new windows instead of getting existing from the database. + CV_EXPORTS void unregisterAllWindows(); + + //! Displays image in specified window + CV_EXPORTS Viz3d imshow(const String& window_name, InputArray image, const Size& window_size = Size(-1, -1)); + + /** @brief Checks **float/double** value for nan. + + @param x return true if nan. + */ + inline bool isNan(float x) + { + unsigned int *u = reinterpret_cast(&x); + return ((u[0] & 0x7f800000) == 0x7f800000) && (u[0] & 0x007fffff); + } + + /** @brief Checks **float/double** value for nan. + + @param x return true if nan. + */ + inline bool isNan(double x) + { + unsigned int *u = reinterpret_cast(&x); + return (u[1] & 0x7ff00000) == 0x7ff00000 && (u[0] != 0 || (u[1] & 0x000fffff) != 0); + } + + /** @brief Checks **float/double** value for nan. + + @param v return true if **any** of the elements of the vector is *nan*. + */ + template inline bool isNan(const Vec<_Tp, cn>& v) + { return isNan(v.val[0]) || isNan(v.val[1]) || isNan(v.val[2]); } + + /** @brief Checks **float/double** value for nan. + + @param p return true if **any** of the elements of the point is *nan*. + */ + template inline bool isNan(const Point3_<_Tp>& p) + { return isNan(p.x) || isNan(p.y) || isNan(p.z); } + + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Read/write clouds. Supported formats: ply, xyz, obj and stl (readonly) + + /** + * @param file Filename with extension. Supported formats: PLY, XYZ and OBJ. + * @param cloud Supported depths: CV_32F and CV_64F. Supported channels: 3 and 4. + * @param colors Used by PLY format only. Supported depth: CV_8U. Supported channels: 1, 3 and 4. + * @param normals Used by PLY and OBJ format only. Supported depths: CV_32F and CV_64F. + * Supported channels: 3 and 4. + * @param binary Used only for PLY format. + */ + CV_EXPORTS void writeCloud(const String& file, InputArray cloud, InputArray colors = noArray(), InputArray normals = noArray(), bool binary = false); + + /** + * @param file Filename with extension. Supported formats: PLY, XYZ, OBJ and STL. + * @param colors Used by PLY and STL formats only. + * @param normals Used by PLY, OBJ and STL formats only. + * @return A mat containing the point coordinates with depth CV_32F or CV_64F and number of + * channels 3 or 4 with only 1 row. + */ + CV_EXPORTS Mat readCloud (const String& file, OutputArray colors = noArray(), OutputArray normals = noArray()); + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Reads mesh. Only ply format is supported now and no texture load support + + CV_EXPORTS Mesh readMesh(const String& file); + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Read/write poses and trajectories + + /** + * @param file Filename of type supported by cv::FileStorage. + * @param pose Output matrix. + * @param tag Name of the pose in the file. + */ + CV_EXPORTS bool readPose(const String& file, Affine3d& pose, const String& tag = "pose"); + /** + * @param file Filename. + * @param pose Input matrix. + * @param tag Name of the pose to be saved into the given file. + */ + CV_EXPORTS void writePose(const String& file, const Affine3d& pose, const String& tag = "pose"); + + /** takes vector> with T = float/dobule and writes to a sequence of files with given filename format + * @param traj Trajectory containing a list of poses. It can be + * - std::vector, each cv::Mat is of type CV_32F16 or CV_64FC16 + * - std::vector, std::vector + * - cv::Mat of type CV_32FC16 OR CV_64F16 + * @param files_format Format specifier string for constructing filenames. + * The only placeholder in the string should support `int`. + * @param start The initial counter for files_format. + * @param tag Name of the matrix in the file. + */ + CV_EXPORTS void writeTrajectory(InputArray traj, const String& files_format = "pose%05d.xml", int start = 0, const String& tag = "pose"); + + /** takes vector> with T = float/dobule and loads poses from sequence of files + * + * @param traj Output array containing a lists of poses. It can be + * - std::vector, std::vector + * - cv::Mat + * @param files_format Format specifier string for constructing filenames. + * The only placeholder in the string should support `int`. + * @param start The initial counter for files_format. It must be greater than or equal to 0. + * @param end The final counter for files_format. + * @param tag Name of the matrix in the file. + */ + CV_EXPORTS void readTrajectory(OutputArray traj, const String& files_format = "pose%05d.xml", int start = 0, int end = INT_MAX, const String& tag = "pose"); + + + /////////////////////////////////////////////////////////////////////////////////////////////// + /** Computing normals for mesh + * @param mesh Input mesh. + * @param normals Normals at very point in the mesh of type CV_64FC3. + */ + CV_EXPORTS void computeNormals(const Mesh& mesh, OutputArray normals); + +//! @} + + } /* namespace viz */ +} /* namespace cv */ + +#endif /* OPENCV_VIZCORE_HPP */ diff --git a/modules/viz/include/opencv2/viz/widget_accessor.hpp b/modules/viz/include/opencv2/viz/widget_accessor.hpp new file mode 100644 index 000000000..7b4be543f --- /dev/null +++ b/modules/viz/include/opencv2/viz/widget_accessor.hpp @@ -0,0 +1,89 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef OPENCV_VIZ_WIDGET_ACCESSOR_HPP +#define OPENCV_VIZ_WIDGET_ACCESSOR_HPP + +#include +#include +#include + +namespace cv +{ + namespace viz + { + +//! @addtogroup viz_widget +//! @{ + + class Widget; + + /** @brief This class is for users who want to develop their own widgets using VTK library API. : + */ + struct CV_EXPORTS WidgetAccessor + { + /** @brief Returns vtkProp of a given widget. + + @param widget Widget whose vtkProp is to be returned. + + @note vtkProp has to be down cast appropriately to be modified. + @code + vtkActor * actor = vtkActor::SafeDownCast(viz::WidgetAccessor::getProp(widget)); + @endcode + */ + static vtkSmartPointer getProp(const Widget &widget); + /** @brief Sets vtkProp of a given widget. + + @param widget Widget whose vtkProp is to be set. @param prop A vtkProp. + */ + static void setProp(Widget &widget, vtkSmartPointer prop); + }; + +//! @} + + } +} + +#endif diff --git a/modules/viz/include/opencv2/viz/widgets.hpp b/modules/viz/include/opencv2/viz/widgets.hpp new file mode 100644 index 000000000..469107981 --- /dev/null +++ b/modules/viz/include/opencv2/viz/widgets.hpp @@ -0,0 +1,850 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef OPENCV_VIZ_WIDGETS_HPP +#define OPENCV_VIZ_WIDGETS_HPP + +#include + +namespace cv +{ + namespace viz + { + +//! @addtogroup viz_widget +//! @{ + + ///////////////////////////////////////////////////////////////////////////// + /// Widget rendering properties + enum RenderingProperties + { + POINT_SIZE, + OPACITY, + LINE_WIDTH, + FONT_SIZE, + REPRESENTATION, + IMMEDIATE_RENDERING, + SHADING, + AMBIENT, + LIGHTING + }; + + enum RepresentationValues + { + REPRESENTATION_POINTS, + REPRESENTATION_WIREFRAME, + REPRESENTATION_SURFACE + }; + + enum ShadingValues + { + SHADING_FLAT, + SHADING_GOURAUD, + SHADING_PHONG + }; + + ///////////////////////////////////////////////////////////////////////////// + + /** @brief Base class of all widgets. Widget is implicitly shared. + */ + class CV_EXPORTS Widget + { + public: + Widget(); + Widget(const Widget& other); + Widget& operator=(const Widget& other); + virtual ~Widget(); + + /** @brief Creates a widget from ply file. + + @param file_name Ply file name. + */ + static Widget fromPlyFile(const String &file_name); + + /** @brief Sets rendering property of the widget. + + @param property Property that will be modified. + @param value The new value of the property. + + Rendering property can be one of the following: + - **POINT_SIZE** + - **OPACITY** + - **LINE_WIDTH** + - **FONT_SIZE** + + REPRESENTATION: Expected values are + - **REPRESENTATION_POINTS** + - **REPRESENTATION_WIREFRAME** + - **REPRESENTATION_SURFACE** + + IMMEDIATE_RENDERING: + - Turn on immediate rendering by setting the value to 1. + - Turn off immediate rendering by setting the value to 0. + + SHADING: Expected values are + - **SHADING_FLAT** + - **SHADING_GOURAUD** + - **SHADING_PHONG** + */ + void setRenderingProperty(int property, double value); + /** @brief Returns rendering property of the widget. + + @param property Property. + + Rendering property can be one of the following: + - **POINT_SIZE** + - **OPACITY** + - **LINE_WIDTH** + - **FONT_SIZE** + - **AMBIENT** + + REPRESENTATION: Expected values are + - **REPRESENTATION_POINTS** + - **REPRESENTATION_WIREFRAME** + - **REPRESENTATION_SURFACE** + + **IMMEDIATE_RENDERING**: + - Turn on immediate rendering by setting the value to 1. + - Turn off immediate rendering by setting the value to 0. + + SHADING: Expected values are + - **SHADING_FLAT** + - **SHADING_GOURAUD** + - **SHADING_PHONG** + */ + double getRenderingProperty(int property) const; + + /** @brief Casts a widget to another. + + @code + // Create a sphere widget + viz::WSphere sw(Point3f(0.0f,0.0f,0.0f), 0.5f); + // Cast sphere widget to cloud widget + viz::WCloud cw = sw.cast(); + @endcode + + @note 3D Widgets can only be cast to 3D Widgets. 2D Widgets can only be cast to 2D Widgets. + */ + template _W cast() const; + private: + class Impl; + Impl *impl_; + friend struct WidgetAccessor; + }; + + ///////////////////////////////////////////////////////////////////////////// + + /** @brief Base class of all 3D widgets. + */ + class CV_EXPORTS Widget3D : public Widget + { + public: + Widget3D() {} + + /** @brief Sets pose of the widget. + + @param pose The new pose of the widget. + */ + void setPose(const Affine3d &pose); + /** @brief Updates pose of the widget by pre-multiplying its current pose. + + @param pose The pose that the current pose of the widget will be pre-multiplied by. + */ + void updatePose(const Affine3d &pose); + /** @brief Returns the current pose of the widget. + */ + Affine3d getPose() const; + + /** @brief Transforms internal widget data (i.e. points, normals) using the given transform. + + @param transform Specified transformation to apply. + */ + void applyTransform(const Affine3d &transform); + + /** @brief Sets the color of the widget. + + @param color color of type Color + */ + void setColor(const Color &color); + + }; + + ///////////////////////////////////////////////////////////////////////////// + + /** @brief Base class of all 2D widgets. + */ + class CV_EXPORTS Widget2D : public Widget + { + public: + Widget2D() {} + + /** @brief Sets the color of the widget. + + @param color color of type Color + */ + void setColor(const Color &color); + }; + + ///////////////////////////////////////////////////////////////////////////// + /// Simple widgets + + /** @brief This 3D Widget defines a finite line. + */ + class CV_EXPORTS WLine : public Widget3D + { + public: + /** @brief Constructs a WLine. + + @param pt1 Start point of the line. + @param pt2 End point of the line. + @param color Color of the line. + */ + WLine(const Point3d &pt1, const Point3d &pt2, const Color &color = Color::white()); + }; + + /** @brief This 3D Widget defines a finite plane. + */ + class CV_EXPORTS WPlane : public Widget3D + { + public: + /** @brief Constructs a default plane with center point at origin and normal oriented along z-axis. + + @param size Size of the plane + @param color Color of the plane. + */ + WPlane(const Size2d& size = Size2d(1.0, 1.0), const Color &color = Color::white()); + + /** @brief Constructs a repositioned plane + + @param center Center of the plane + @param normal Plane normal orientation + @param new_yaxis Up-vector. New orientation of plane y-axis. + @param size + @param color Color of the plane. + */ + WPlane(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, + const Size2d& size = Size2d(1.0, 1.0), const Color &color = Color::white()); + }; + + /** @brief This 3D Widget defines a sphere. : + */ + class CV_EXPORTS WSphere : public Widget3D + { + public: + /** @brief Constructs a WSphere. + + @param center Center of the sphere. + @param radius Radius of the sphere. + @param sphere_resolution Resolution of the sphere. + @param color Color of the sphere. + */ + WSphere(const cv::Point3d ¢er, double radius, int sphere_resolution = 10, const Color &color = Color::white()); + }; + + /** @brief This 3D Widget defines an arrow. + */ + class CV_EXPORTS WArrow : public Widget3D + { + public: + /** @brief Constructs an WArrow. + + @param pt1 Start point of the arrow. + @param pt2 End point of the arrow. + @param thickness Thickness of the arrow. Thickness of arrow head is also adjusted + accordingly. + @param color Color of the arrow. + + Arrow head is located at the end point of the arrow. + */ + WArrow(const Point3d& pt1, const Point3d& pt2, double thickness = 0.03, const Color &color = Color::white()); + }; + + /** @brief This 3D Widget defines a circle. + */ + class CV_EXPORTS WCircle : public Widget3D + { + public: + /** @brief Constructs default planar circle centered at origin with plane normal along z-axis + + @param radius Radius of the circle. + @param thickness Thickness of the circle. + @param color Color of the circle. + */ + WCircle(double radius, double thickness = 0.01, const Color &color = Color::white()); + + /** @brief Constructs repositioned planar circle. + + @param radius Radius of the circle. + @param center Center of the circle. + @param normal Normal of the plane in which the circle lies. + @param thickness Thickness of the circle. + @param color Color of the circle. + */ + WCircle(double radius, const Point3d& center, const Vec3d& normal, double thickness = 0.01, const Color &color = Color::white()); + }; + + /** @brief This 3D Widget defines a cone. : + */ + class CV_EXPORTS WCone : public Widget3D + { + public: + /** @brief Constructs default cone oriented along x-axis with center of its base located at origin + + @param length Length of the cone. + @param radius Radius of the cone. + @param resolution Resolution of the cone. + @param color Color of the cone. + */ + WCone(double length, double radius, int resolution = 6.0, const Color &color = Color::white()); + + /** @brief Constructs repositioned planar cone. + + @param radius Radius of the cone. + @param center Center of the cone base. + @param tip Tip of the cone. + @param resolution Resolution of the cone. + @param color Color of the cone. + + */ + WCone(double radius, const Point3d& center, const Point3d& tip, int resolution = 6.0, const Color &color = Color::white()); + }; + + /** @brief This 3D Widget defines a cylinder. : + */ + class CV_EXPORTS WCylinder : public Widget3D + { + public: + /** @brief Constructs a WCylinder. + + @param axis_point1 A point1 on the axis of the cylinder. + @param axis_point2 A point2 on the axis of the cylinder. + @param radius Radius of the cylinder. + @param numsides Resolution of the cylinder. + @param color Color of the cylinder. + */ + WCylinder(const Point3d& axis_point1, const Point3d& axis_point2, double radius, int numsides = 30, const Color &color = Color::white()); + }; + + /** @brief This 3D Widget defines a cube. + */ + class CV_EXPORTS WCube : public Widget3D + { + public: + /** @brief Constructs a WCube. + + @param min_point Specifies minimum point of the bounding box. + @param max_point Specifies maximum point of the bounding box. + @param wire_frame If true, cube is represented as wireframe. + @param color Color of the cube. + + ![Cube Widget](images/cube_widget.png) + */ + WCube(const Point3d& min_point = Vec3d::all(-0.5), const Point3d& max_point = Vec3d::all(0.5), + bool wire_frame = true, const Color &color = Color::white()); + }; + + /** @brief This 3D Widget defines a poly line. : + */ + class CV_EXPORTS WPolyLine : public Widget3D + { + public: + WPolyLine(InputArray points, InputArray colors); + /** @brief Constructs a WPolyLine. + + @param points Point set. + @param color Color of the poly line. + */ + WPolyLine(InputArray points, const Color &color = Color::white()); + }; + + ///////////////////////////////////////////////////////////////////////////// + /// Text and image widgets + + /** @brief This 2D Widget represents text overlay. + */ + class CV_EXPORTS WText : public Widget2D + { + public: + /** @brief Constructs a WText. + + @param text Text content of the widget. + @param pos Position of the text. + @param font_size Font size. + @param color Color of the text. + */ + WText(const String &text, const Point &pos, int font_size = 20, const Color &color = Color::white()); + + /** @brief Sets the text content of the widget. + + @param text Text content of the widget. + */ + void setText(const String &text); + /** @brief Returns the current text content of the widget. + */ + String getText() const; + }; + + /** @brief This 3D Widget represents 3D text. The text always faces the camera. + */ + class CV_EXPORTS WText3D : public Widget3D + { + public: + /** @brief Constructs a WText3D. + + @param text Text content of the widget. + @param position Position of the text. + @param text_scale Size of the text. + @param face_camera If true, text always faces the camera. + @param color Color of the text. + */ + WText3D(const String &text, const Point3d &position, double text_scale = 1., bool face_camera = true, const Color &color = Color::white()); + + /** @brief Sets the text content of the widget. + + @param text Text content of the widget. + + */ + void setText(const String &text); + /** @brief Returns the current text content of the widget. + */ + String getText() const; + }; + + /** @brief This 2D Widget represents an image overlay. : + */ + class CV_EXPORTS WImageOverlay : public Widget2D + { + public: + /** @brief Constructs an WImageOverlay. + + @param image BGR or Gray-Scale image. + @param rect Image is scaled and positioned based on rect. + */ + WImageOverlay(InputArray image, const Rect &rect); + /** @brief Sets the image content of the widget. + + @param image BGR or Gray-Scale image. + */ + void setImage(InputArray image); + }; + + /** @brief This 3D Widget represents an image in 3D space. : + */ + class CV_EXPORTS WImage3D : public Widget3D + { + public: + /** @brief Constructs an WImage3D. + + @param image BGR or Gray-Scale image. + @param size Size of the image. + */ + WImage3D(InputArray image, const Size2d &size); + + /** @brief Constructs an WImage3D. + + @param image BGR or Gray-Scale image. + @param size Size of the image. + @param center Position of the image. + @param normal Normal of the plane that represents the image. + @param up_vector Determines orientation of the image. + */ + WImage3D(InputArray image, const Size2d &size, const Vec3d ¢er, const Vec3d &normal, const Vec3d &up_vector); + + /** @brief Sets the image content of the widget. + + @param image BGR or Gray-Scale image. + */ + void setImage(InputArray image); + + /** @brief Sets the image size of the widget. + + @param size the new size of the image. + */ + void setSize(const Size& size); + }; + + ///////////////////////////////////////////////////////////////////////////// + /// Compound widgets + + /** @brief This 3D Widget represents a coordinate system. : + */ + class CV_EXPORTS WCoordinateSystem : public Widget3D + { + public: + /** @brief Constructs a WCoordinateSystem. + + @param scale Determines the size of the axes. + */ + WCoordinateSystem(double scale = 1.0); + }; + + /** @brief This 3D Widget defines a grid. : + */ + class CV_EXPORTS WGrid : public Widget3D + { + public: + /** @brief Constructs a WGrid. + + @param cells Number of cell columns and rows, respectively. + @param cells_spacing Size of each cell, respectively. + @param color Color of the grid. + */ + WGrid(const Vec2i &cells = Vec2i::all(10), const Vec2d &cells_spacing = Vec2d::all(1.0), const Color &color = Color::white()); + + //! Creates repositioned grid + WGrid(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, + const Vec2i &cells = Vec2i::all(10), const Vec2d &cells_spacing = Vec2d::all(1.0), const Color &color = Color::white()); + }; + + /** @brief This 3D Widget represents camera position in a scene by its axes or viewing frustum. : + */ + class CV_EXPORTS WCameraPosition : public Widget3D + { + public: + /** @brief Creates camera coordinate frame at the origin. + + ![Camera coordinate frame](images/cpw1.png) + */ + WCameraPosition(double scale = 1.0); + /** @brief Display the viewing frustum + @param K Intrinsic matrix of the camera. + @param scale Scale of the frustum. + @param color Color of the frustum. + + Creates viewing frustum of the camera based on its intrinsic matrix K. + + ![Camera viewing frustum](images/cpw2.png) + */ + WCameraPosition(const Matx33d &K, double scale = 1.0, const Color &color = Color::white()); + /** @brief Display the viewing frustum + @param fov Field of view of the camera (horizontal, vertical). + @param scale Scale of the frustum. + @param color Color of the frustum. + + Creates viewing frustum of the camera based on its field of view fov. + + ![Camera viewing frustum](images/cpw2.png) + */ + WCameraPosition(const Vec2d &fov, double scale = 1.0, const Color &color = Color::white()); + /** @brief Display image on the far plane of the viewing frustum + + @param K Intrinsic matrix of the camera. + @param image BGR or Gray-Scale image that is going to be displayed on the far plane of the frustum. + @param scale Scale of the frustum and image. + @param color Color of the frustum. + + Creates viewing frustum of the camera based on its intrinsic matrix K, and displays image on + the far end plane. + + ![Camera viewing frustum with image](images/cpw3.png) + */ + WCameraPosition(const Matx33d &K, InputArray image, double scale = 1.0, const Color &color = Color::white()); + /** @brief Display image on the far plane of the viewing frustum + + @param fov Field of view of the camera (horizontal, vertical). + @param image BGR or Gray-Scale image that is going to be displayed on the far plane of the frustum. + @param scale Scale of the frustum and image. + @param color Color of the frustum. + + Creates viewing frustum of the camera based on its intrinsic matrix K, and displays image on + the far end plane. + + ![Camera viewing frustum with image](images/cpw3.png) + */ + WCameraPosition(const Vec2d &fov, InputArray image, double scale = 1.0, const Color &color = Color::white()); + }; + + ///////////////////////////////////////////////////////////////////////////// + /// Trajectories + + /** @brief This 3D Widget represents a trajectory. : + */ + class CV_EXPORTS WTrajectory : public Widget3D + { + public: + enum {FRAMES = 1, PATH = 2, BOTH = FRAMES + PATH }; + + /** @brief Constructs a WTrajectory. + + @param path List of poses on a trajectory. Takes std::vector\\> with T == [float | double] + @param display_mode Display mode. This can be PATH, FRAMES, and BOTH. + @param scale Scale of the frames. Polyline is not affected. + @param color Color of the polyline that represents path. + + Frames are not affected. + Displays trajectory of the given path as follows: + - PATH : Displays a poly line that represents the path. + - FRAMES : Displays coordinate frames at each pose. + - PATH & FRAMES : Displays both poly line and coordinate frames. + */ + WTrajectory(InputArray path, int display_mode = WTrajectory::PATH, double scale = 1.0, const Color &color = Color::white()); + }; + + /** @brief This 3D Widget represents a trajectory. : + */ + class CV_EXPORTS WTrajectoryFrustums : public Widget3D + { + public: + /** @brief Constructs a WTrajectoryFrustums. + + @param path List of poses on a trajectory. Takes std::vector\\> with T == [float | double] + @param K Intrinsic matrix of the camera. + @param scale Scale of the frustums. + @param color Color of the frustums. + + Displays frustums at each pose of the trajectory. + */ + WTrajectoryFrustums(InputArray path, const Matx33d &K, double scale = 1., const Color &color = Color::white()); + + /** @brief Constructs a WTrajectoryFrustums. + + @param path List of poses on a trajectory. Takes std::vector\\> with T == [float | double] + @param fov Field of view of the camera (horizontal, vertical). + @param scale Scale of the frustums. + @param color Color of the frustums. + + Displays frustums at each pose of the trajectory. + */ + WTrajectoryFrustums(InputArray path, const Vec2d &fov, double scale = 1., const Color &color = Color::white()); + }; + + /** @brief This 3D Widget represents a trajectory using spheres and lines + + where spheres represent the positions of the camera, and lines represent the direction from + previous position to the current. : + */ + class CV_EXPORTS WTrajectorySpheres: public Widget3D + { + public: + /** @brief Constructs a WTrajectorySpheres. + + @param path List of poses on a trajectory. Takes std::vector\\> with T == [float | double] + @param line_length Max length of the lines which point to previous position + @param radius Radius of the spheres. + @param from Color for first sphere. + @param to Color for last sphere. Intermediate spheres will have interpolated color. + */ + WTrajectorySpheres(InputArray path, double line_length = 0.05, double radius = 0.007, + const Color &from = Color::red(), const Color &to = Color::white()); + }; + + ///////////////////////////////////////////////////////////////////////////// + /// Clouds + + /** @brief This 3D Widget defines a point cloud. : + + @note In case there are four channels in the cloud, fourth channel is ignored. + */ + class CV_EXPORTS WCloud: public Widget3D + { + public: + /** @brief Constructs a WCloud. + + @param cloud Set of points which can be of type: CV_32FC3, CV_32FC4, CV_64FC3, CV_64FC4. + @param colors Set of colors. It has to be of the same size with cloud. + + Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + */ + WCloud(InputArray cloud, InputArray colors); + + /** @brief Constructs a WCloud. + @param cloud Set of points which can be of type: CV_32FC3, CV_32FC4, CV_64FC3, CV_64FC4. + @param color A single Color for the whole cloud. + + Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + */ + WCloud(InputArray cloud, const Color &color = Color::white()); + + /** @brief Constructs a WCloud. + @param cloud Set of points which can be of type: CV_32FC3, CV_32FC4, CV_64FC3, CV_64FC4. + @param colors Set of colors. It has to be of the same size with cloud. + @param normals Normals for each point in cloud. Size and type should match with the cloud parameter. + + Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + */ + WCloud(InputArray cloud, InputArray colors, InputArray normals); + + /** @brief Constructs a WCloud. + @param cloud Set of points which can be of type: CV_32FC3, CV_32FC4, CV_64FC3, CV_64FC4. + @param color A single Color for the whole cloud. + @param normals Normals for each point in cloud. + + Size and type should match with the cloud parameter. + Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + */ + WCloud(InputArray cloud, const Color &color, InputArray normals); + }; + + class CV_EXPORTS WPaintedCloud: public Widget3D + { + public: + //! Paint cloud with default gradient between cloud bounds points + WPaintedCloud(InputArray cloud); + + //! Paint cloud with default gradient between given points + WPaintedCloud(InputArray cloud, const Point3d& p1, const Point3d& p2); + + //! Paint cloud with gradient specified by given colors between given points + WPaintedCloud(InputArray cloud, const Point3d& p1, const Point3d& p2, const Color& c1, const Color c2); + }; + + /** @brief This 3D Widget defines a collection of clouds. : + @note In case there are four channels in the cloud, fourth channel is ignored. + */ + class CV_EXPORTS WCloudCollection : public Widget3D + { + public: + WCloudCollection(); + + /** @brief Adds a cloud to the collection. + + @param cloud Point set which can be of type: CV_32FC3, CV_32FC4, CV_64FC3, CV_64FC4. + @param colors Set of colors. It has to be of the same size with cloud. + @param pose Pose of the cloud. Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + */ + void addCloud(InputArray cloud, InputArray colors, const Affine3d &pose = Affine3d::Identity()); + /** @brief Adds a cloud to the collection. + + @param cloud Point set which can be of type: CV_32FC3, CV_32FC4, CV_64FC3, CV_64FC4. + @param color A single Color for the whole cloud. + @param pose Pose of the cloud. Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + */ + void addCloud(InputArray cloud, const Color &color = Color::white(), const Affine3d &pose = Affine3d::Identity()); + /** @brief Finalizes cloud data by repacking to single cloud. + + Useful for large cloud collections to reduce memory usage + */ + void finalize(); + }; + + /** @brief This 3D Widget represents normals of a point cloud. : + */ + class CV_EXPORTS WCloudNormals : public Widget3D + { + public: + /** @brief Constructs a WCloudNormals. + + @param cloud Point set which can be of type: CV_32FC3, CV_32FC4, CV_64FC3, CV_64FC4. + @param normals A set of normals that has to be of same type with cloud. + @param level Display only every level th normal. + @param scale Scale of the arrows that represent normals. + @param color Color of the arrows that represent normals. + + @note In case there are four channels in the cloud, fourth channel is ignored. + */ + WCloudNormals(InputArray cloud, InputArray normals, int level = 64, double scale = 0.1, const Color &color = Color::white()); + }; + + /** @brief Constructs a WMesh. + + @param mesh Mesh object that will be displayed. + @param cloud Points of the mesh object. + @param polygons Points of the mesh object. + @param colors Point colors. + @param normals Point normals. + */ + class CV_EXPORTS WMesh : public Widget3D + { + public: + WMesh(const Mesh &mesh); + WMesh(InputArray cloud, InputArray polygons, InputArray colors = noArray(), InputArray normals = noArray()); + }; + + /** @brief This class allows to merge several widgets to single one. + + It has quite limited functionality and can't merge widgets with different attributes. For + instance, if widgetA has color array and widgetB has only global color defined, then result + of merge won't have color at all. The class is suitable for merging large amount of similar + widgets. : + */ + class CV_EXPORTS WWidgetMerger : public Widget3D + { + public: + WWidgetMerger(); + + //! Add widget to merge with optional position change + void addWidget(const Widget3D& widget, const Affine3d &pose = Affine3d::Identity()); + + //! Repacks internal structure to single widget + void finalize(); + }; + + ///////////////////////////////////////////////////////////////////////////// + /// Utility exports + + template<> CV_EXPORTS Widget2D Widget::cast() const; + template<> CV_EXPORTS Widget3D Widget::cast() const; + template<> CV_EXPORTS WLine Widget::cast() const; + template<> CV_EXPORTS WPlane Widget::cast() const; + template<> CV_EXPORTS WSphere Widget::cast() const; + template<> CV_EXPORTS WCylinder Widget::cast() const; + template<> CV_EXPORTS WArrow Widget::cast() const; + template<> CV_EXPORTS WCircle Widget::cast() const; + template<> CV_EXPORTS WCone Widget::cast() const; + template<> CV_EXPORTS WCube Widget::cast() const; + template<> CV_EXPORTS WCoordinateSystem Widget::cast() const; + template<> CV_EXPORTS WPolyLine Widget::cast() const; + template<> CV_EXPORTS WGrid Widget::cast() const; + template<> CV_EXPORTS WText3D Widget::cast() const; + template<> CV_EXPORTS WText Widget::cast() const; + template<> CV_EXPORTS WImageOverlay Widget::cast() const; + template<> CV_EXPORTS WImage3D Widget::cast() const; + template<> CV_EXPORTS WCameraPosition Widget::cast() const; + template<> CV_EXPORTS WTrajectory Widget::cast() const; + template<> CV_EXPORTS WTrajectoryFrustums Widget::cast() const; + template<> CV_EXPORTS WTrajectorySpheres Widget::cast() const; + template<> CV_EXPORTS WCloud Widget::cast() const; + template<> CV_EXPORTS WPaintedCloud Widget::cast() const; + template<> CV_EXPORTS WCloudCollection Widget::cast() const; + template<> CV_EXPORTS WCloudNormals Widget::cast() const; + template<> CV_EXPORTS WMesh Widget::cast() const; + template<> CV_EXPORTS WWidgetMerger Widget::cast() const; + +//! @} + + } /* namespace viz */ +} /* namespace cv */ + +#endif diff --git a/modules/viz/src/clouds.cpp b/modules/viz/src/clouds.cpp new file mode 100644 index 000000000..c9a13fcfe --- /dev/null +++ b/modules/viz/src/clouds.cpp @@ -0,0 +1,530 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Point Cloud Widget implementation + +cv::viz::WCloud::WCloud(InputArray cloud, InputArray colors) +{ + WCloud cloud_widget(cloud, colors, cv::noArray()); + *this = cloud_widget; +} + +cv::viz::WCloud::WCloud(InputArray cloud, const Color &color) +{ + WCloud cloud_widget(cloud, Mat(cloud.size(), CV_8UC3, color)); + *this = cloud_widget; +} + +cv::viz::WCloud::WCloud(InputArray cloud, const Color &color, InputArray normals) +{ + WCloud cloud_widget(cloud, Mat(cloud.size(), CV_8UC3, color), normals); + *this = cloud_widget; +} + +cv::viz::WCloud::WCloud(cv::InputArray cloud, cv::InputArray colors, cv::InputArray normals) +{ + CV_Assert(!cloud.empty() && !colors.empty()); + + vtkSmartPointer cloud_source = vtkSmartPointer::New(); + cloud_source->SetColorCloudNormals(cloud, colors, normals); + cloud_source->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, cloud_source->GetOutput()); + mapper->SetScalarModeToUsePointData(); +#if VTK_MAJOR_VERSION < 8 + mapper->ImmediateModeRenderingOff(); +#endif + mapper->SetScalarRange(0, 255); + mapper->ScalarVisibilityOn(); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WCloud cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Painted Cloud Widget implementation + +cv::viz::WPaintedCloud::WPaintedCloud(InputArray cloud) +{ + vtkSmartPointer cloud_source = vtkSmartPointer::New(); + cloud_source->SetCloud(cloud); + cloud_source->Update(); + + Vec6d bounds(cloud_source->GetOutput()->GetPoints()->GetBounds()); + + vtkSmartPointer elevation = vtkSmartPointer::New(); + elevation->SetInputConnection(cloud_source->GetOutputPort()); + elevation->SetLowPoint(bounds[0], bounds[2], bounds[4]); + elevation->SetHighPoint(bounds[1], bounds[3], bounds[5]); + elevation->SetScalarRange(0.0, 1.0); + elevation->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, vtkPolyData::SafeDownCast(elevation->GetOutput())); +#if VTK_MAJOR_VERSION < 8 + mapper->ImmediateModeRenderingOff(); +#endif + mapper->ScalarVisibilityOn(); + mapper->SetColorModeToMapScalars(); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WPaintedCloud::WPaintedCloud(InputArray cloud, const Point3d& p1, const Point3d& p2) +{ + vtkSmartPointer cloud_source = vtkSmartPointer::New(); + cloud_source->SetCloud(cloud); + + vtkSmartPointer elevation = vtkSmartPointer::New(); + elevation->SetInputConnection(cloud_source->GetOutputPort()); + elevation->SetLowPoint(p1.x, p1.y, p1.z); + elevation->SetHighPoint(p2.x, p2.y, p2.z); + elevation->SetScalarRange(0.0, 1.0); + elevation->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, vtkPolyData::SafeDownCast(elevation->GetOutput())); +#if VTK_MAJOR_VERSION < 8 + mapper->ImmediateModeRenderingOff(); +#endif + mapper->ScalarVisibilityOn(); + mapper->SetColorModeToMapScalars(); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WPaintedCloud::WPaintedCloud(InputArray cloud, const Point3d& p1, const Point3d& p2, const Color& c1, const Color c2) +{ + vtkSmartPointer cloud_source = vtkSmartPointer::New(); + cloud_source->SetCloud(cloud); + + vtkSmartPointer elevation = vtkSmartPointer::New(); + elevation->SetInputConnection(cloud_source->GetOutputPort()); + elevation->SetLowPoint(p1.x, p1.y, p1.z); + elevation->SetHighPoint(p2.x, p2.y, p2.z); + elevation->SetScalarRange(0.0, 1.0); + elevation->Update(); + + Color vc1 = vtkcolor(c1), vc2 = vtkcolor(c2); + vtkSmartPointer color_transfer = vtkSmartPointer::New(); + color_transfer->SetColorSpaceToRGB(); + color_transfer->AddRGBPoint(0.0, vc1[0], vc1[1], vc1[2]); + color_transfer->AddRGBPoint(1.0, vc2[0], vc2[1], vc2[2]); + color_transfer->SetScaleToLinear(); + color_transfer->Build(); + + //if in future some need to replace color table with real scalars, then this can be done usine next calls: + //vtkDataArray *float_scalars = vtkPolyData::SafeDownCast(elevation->GetOutput())->GetPointData()->GetArray("Elevation"); + //vtkSmartPointer polydata = cloud_source->GetOutput(); + //polydata->GetPointData()->SetScalars(color_transfer->MapScalars(float_scalars, VTK_COLOR_MODE_DEFAULT, 0)); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, vtkPolyData::SafeDownCast(elevation->GetOutput())); +#if VTK_MAJOR_VERSION < 8 + mapper->ImmediateModeRenderingOff(); +#endif + mapper->ScalarVisibilityOn(); + mapper->SetColorModeToMapScalars(); + mapper->SetLookupTable(color_transfer); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WPaintedCloud cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Cloud Collection Widget implementation + +cv::viz::WCloudCollection::WCloudCollection() +{ + vtkSmartPointer append_filter = vtkSmartPointer::New(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(append_filter->GetOutputPort()); + mapper->SetScalarModeToUsePointData(); +#if VTK_MAJOR_VERSION < 8 + mapper->ImmediateModeRenderingOff(); +#endif + mapper->SetScalarRange(0, 255); + mapper->ScalarVisibilityOn(); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetNumberOfCloudPoints(1); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +void cv::viz::WCloudCollection::addCloud(InputArray cloud, InputArray colors, const Affine3d &pose) +{ + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetColorCloud(cloud, colors); + + vtkSmartPointer polydata = VtkUtils::TransformPolydata(source->GetOutputPort(), pose); + + vtkSmartPointer actor = vtkLODActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Correctness check." && actor); + + vtkSmartPointer producer = actor->GetMapper()->GetInputConnection(0, 0)->GetProducer(); + vtkSmartPointer append_filter = vtkAppendPolyData::SafeDownCast(producer); + VtkUtils::AddInputData(append_filter, polydata); + + actor->SetNumberOfCloudPoints(std::max(1, actor->GetNumberOfCloudPoints() + polydata->GetNumberOfPoints()/10)); +} + +void cv::viz::WCloudCollection::addCloud(InputArray cloud, const Color &color, const Affine3d &pose) +{ + addCloud(cloud, Mat(cloud.size(), CV_8UC3, color), pose); +} + +void cv::viz::WCloudCollection::finalize() +{ + vtkSmartPointer actor = vtkLODActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Incompatible widget type." && actor); + + vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + CV_Assert("Need to add at least one cloud." && mapper); + + vtkSmartPointer producer = mapper->GetInputConnection(0, 0)->GetProducer(); + vtkSmartPointer append_filter = vtkAppendPolyData::SafeDownCast(producer); + append_filter->Update(); + + vtkSmartPointer polydata = append_filter->GetOutput(); + mapper->RemoveInputConnection(0, 0); + VtkUtils::SetInputData(mapper, polydata); +} + +template<> cv::viz::WCloudCollection cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Cloud Normals Widget implementation + +cv::viz::WCloudNormals::WCloudNormals(InputArray _cloud, InputArray _normals, int level, double scale, const Color &color) +{ + Mat cloud = _cloud.getMat(); + Mat normals = _normals.getMat(); + + CV_Assert(cloud.type() == CV_32FC3 || cloud.type() == CV_64FC3 || cloud.type() == CV_32FC4 || cloud.type() == CV_64FC4); + CV_Assert(cloud.size() == normals.size() && cloud.type() == normals.type()); + + int sqlevel = (int)std::sqrt((double)level); + int ystep = (cloud.cols > 1 && cloud.rows > 1) ? sqlevel : 1; + int xstep = (cloud.cols > 1 && cloud.rows > 1) ? sqlevel : level; + + vtkSmartPointer points = vtkSmartPointer::New(); + points->SetDataType(cloud.depth() == CV_32F ? VTK_FLOAT : VTK_DOUBLE); + + vtkSmartPointer lines = vtkSmartPointer::New(); + + int s_chs = cloud.channels(); + int n_chs = normals.channels(); + int total = 0; + + for(int y = 0; y < cloud.rows; y += ystep) + { + if (cloud.depth() == CV_32F) + { + const float *srow = cloud.ptr(y); + const float *send = srow + cloud.cols * s_chs; + const float *nrow = normals.ptr(y); + + for (; srow < send; srow += xstep * s_chs, nrow += xstep * n_chs) + if (!isNan(srow) && !isNan(nrow)) + { + Vec3f endp = Vec3f(srow) + Vec3f(nrow) * (float)scale; + + points->InsertNextPoint(srow); + points->InsertNextPoint(endp.val); + + lines->InsertNextCell(2); + lines->InsertCellPoint(total++); + lines->InsertCellPoint(total++); + } + } + else + { + const double *srow = cloud.ptr(y); + const double *send = srow + cloud.cols * s_chs; + const double *nrow = normals.ptr(y); + + for (; srow < send; srow += xstep * s_chs, nrow += xstep * n_chs) + if (!isNan(srow) && !isNan(nrow)) + { + Vec3d endp = Vec3d(srow) + Vec3d(nrow) * (double)scale; + + points->InsertNextPoint(srow); + points->InsertNextPoint(endp.val); + + lines->InsertNextCell(2); + lines->InsertCellPoint(total++); + lines->InsertCellPoint(total++); + } + } + } + + vtkSmartPointer polydata = vtkSmartPointer::New(); + polydata->SetPoints(points); + polydata->SetLines(lines); + VtkUtils::FillScalars(polydata, color); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WCloudNormals cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Mesh Widget implementation + +cv::viz::WMesh::WMesh(const Mesh &mesh) +{ + CV_Assert(mesh.cloud.rows == 1 && mesh.polygons.type() == CV_32SC1); + + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetColorCloudNormalsTCoords(mesh.cloud, mesh.colors, mesh.normals, mesh.tcoords); + source->Update(); + + Mat lookup_buffer(1, (int)mesh.cloud.total(), CV_32SC1); + int *lookup = lookup_buffer.ptr(); + for(int y = 0, index = 0; y < mesh.cloud.rows; ++y) + { + int s_chs = mesh.cloud.channels(); + + if (mesh.cloud.depth() == CV_32F) + { + const float* srow = mesh.cloud.ptr(y); + const float* send = srow + mesh.cloud.cols * s_chs; + + for (; srow != send; srow += s_chs, ++lookup) + if (!isNan(srow[0]) && !isNan(srow[1]) && !isNan(srow[2])) + *lookup = index++; + } + + if (mesh.cloud.depth() == CV_64F) + { + const double* srow = mesh.cloud.ptr(y); + const double* send = srow + mesh.cloud.cols * s_chs; + + for (; srow != send; srow += s_chs, ++lookup) + if (!isNan(srow[0]) && !isNan(srow[1]) && !isNan(srow[2])) + *lookup = index++; + } + } + lookup = lookup_buffer.ptr(); + + vtkSmartPointer polydata = source->GetOutput(); + polydata->SetVerts(0); + + const int * polygons = mesh.polygons.ptr(); + vtkSmartPointer cell_array = vtkSmartPointer::New(); + + int idx = 0; + size_t polygons_size = mesh.polygons.total(); + for (size_t i = 0; i < polygons_size; ++idx) + { + int n_points = polygons[i++]; + + cell_array->InsertNextCell(n_points); + for (int j = 0; j < n_points; ++j, ++idx) + cell_array->InsertCellPoint(lookup[polygons[i++]]); + } + cell_array->GetData()->SetNumberOfValues(idx); + cell_array->Squeeze(); + polydata->SetStrips(cell_array); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetScalarModeToUsePointData(); +#if VTK_MAJOR_VERSION < 8 + mapper->ImmediateModeRenderingOff(); +#endif + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + //actor->SetNumberOfCloudPoints(std::max(1, polydata->GetNumberOfPoints() / 10)); + actor->GetProperty()->SetRepresentationToSurface(); + actor->GetProperty()->BackfaceCullingOff(); // Backface culling is off for higher efficiency + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->EdgeVisibilityOff(); + actor->GetProperty()->ShadingOff(); + actor->SetMapper(mapper); + + if (!mesh.texture.empty()) + { + vtkSmartPointer image_source = vtkSmartPointer::New(); + image_source->SetImage(mesh.texture); + + vtkSmartPointer texture = vtkSmartPointer::New(); + texture->SetInputConnection(image_source->GetOutputPort()); + actor->SetTexture(texture); + } + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WMesh::WMesh(InputArray cloud, InputArray polygons, InputArray colors, InputArray normals) +{ + Mesh mesh; + mesh.cloud = cloud.getMat(); + mesh.colors = colors.getMat(); + mesh.normals = normals.getMat(); + mesh.polygons = polygons.getMat(); + *this = WMesh(mesh); +} + +template<> CV_EXPORTS cv::viz::WMesh cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Widget Merger implementation + +cv::viz::WWidgetMerger::WWidgetMerger() +{ + vtkSmartPointer append_filter = vtkSmartPointer::New(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(append_filter->GetOutputPort()); + mapper->SetScalarModeToUsePointData(); +#if VTK_MAJOR_VERSION < 8 + mapper->ImmediateModeRenderingOff(); +#endif + mapper->SetScalarRange(0, 255); + mapper->ScalarVisibilityOn(); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +void cv::viz::WWidgetMerger::addWidget(const Widget3D& widget, const Affine3d &pose) +{ + vtkActor *widget_actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(widget)); + CV_Assert("Widget is not 3D actor." && widget_actor); + + vtkSmartPointer widget_mapper = vtkPolyDataMapper::SafeDownCast(widget_actor->GetMapper()); + CV_Assert("Widget doesn't have a polydata mapper" && widget_mapper); + widget_mapper->Update(); + + vtkSmartPointer actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + vtkSmartPointer producer = actor->GetMapper()->GetInputConnection(0, 0)->GetProducer(); + vtkSmartPointer append_filter = vtkAppendPolyData::SafeDownCast(producer); + CV_Assert("Correctness check" && append_filter); + + VtkUtils::AddInputData(append_filter, VtkUtils::TransformPolydata(widget_mapper->GetInput(), pose)); +} + +void cv::viz::WWidgetMerger::finalize() +{ + vtkSmartPointer actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + vtkSmartPointer producer = actor->GetMapper()->GetInputConnection(0, 0)->GetProducer(); + vtkSmartPointer append_filter = vtkAppendPolyData::SafeDownCast(producer); + CV_Assert("Correctness check" && append_filter); + append_filter->Update(); + + vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + mapper->RemoveInputConnection(0, 0); + VtkUtils::SetInputData(mapper, append_filter->GetOutput()); + mapper->Modified(); +} + +template<> CV_EXPORTS cv::viz::WWidgetMerger cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} diff --git a/modules/viz/src/precomp.hpp b/modules/viz/src/precomp.hpp new file mode 100644 index 000000000..9596b96f0 --- /dev/null +++ b/modules/viz/src/precomp.hpp @@ -0,0 +1,343 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __OPENCV_VIZ_PRECOMP_HPP__ +#define __OPENCV_VIZ_PRECOMP_HPP__ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vtkCallbackCommand.h" + +#if !defined(_WIN32) || defined(__CYGWIN__) +# include /* unlink */ +#else +# include /* unlink */ +#endif + +#include "vtk/vtkOBJWriter.h" +#include "vtk/vtkXYZWriter.h" +#include "vtk/vtkXYZReader.h" +#include "vtk/vtkCloudMatSink.h" +#include "vtk/vtkCloudMatSource.h" +#include "vtk/vtkTrajectorySource.h" +#include "vtk/vtkImageMatSource.h" + + +#include +#include +#include +#include + + +namespace cv +{ + namespace viz + { + typedef std::map > WidgetActorMap; + + struct VizMap + { + typedef std::map type; + typedef type::iterator iterator; + + type m; + ~VizMap(); + void replace_clear(); + }; + + class VizStorage + { + public: + static void unregisterAll(); + + //! window names automatically have Viz - prefix even though not provided by the users + static String generateWindowName(const String &window_name); + + private: + VizStorage(); // Static + + static void add(const Viz3d& window); + static Viz3d& get(const String &window_name); + static void remove(const String &window_name); + static bool windowExists(const String &window_name); + static void removeUnreferenced(); + + static VizMap storage; + friend class Viz3d; + + static VizStorage init; + }; + + template inline _Tp normalized(const _Tp& v) { return v * 1/norm(v); } + + template inline bool isNan(const _Tp* data) + { + return isNan(data[0]) || isNan(data[1]) || isNan(data[2]); + } + + inline vtkSmartPointer getActor(const Widget3D& widget) + { + return vtkActor::SafeDownCast(WidgetAccessor::getProp(widget)); + } + + inline vtkSmartPointer getPolyData(const Widget3D& widget) + { + vtkSmartPointer mapper = getActor(widget)->GetMapper(); + return vtkPolyData::SafeDownCast(mapper->GetInput()); + } + + inline vtkSmartPointer vtkmatrix(const cv::Matx44d &matrix) + { + vtkSmartPointer vtk_matrix = vtkSmartPointer::New(); + vtk_matrix->DeepCopy(matrix.val); + return vtk_matrix; + } + + inline Color vtkcolor(const Color& color) + { + Color scaled_color = color * (1.0/255.0); + std::swap(scaled_color[0], scaled_color[2]); + return scaled_color; + } + + inline Vec3d get_random_vec(double from = -10.0, double to = 10.0) + { + RNG& rng = theRNG(); + return Vec3d(rng.uniform(from, to), rng.uniform(from, to), rng.uniform(from, to)); + } + + struct VtkUtils + { + template + static void SetInputData(vtkSmartPointer filter, vtkPolyData* polydata) + { + #if VTK_MAJOR_VERSION <= 5 + filter->SetInput(polydata); + #else + filter->SetInputData(polydata); + #endif + } + template + static void SetSourceData(vtkSmartPointer filter, vtkPolyData* polydata) + { + #if VTK_MAJOR_VERSION <= 5 + filter->SetSource(polydata); + #else + filter->SetSourceData(polydata); + #endif + } + + template + static void SetInputData(vtkSmartPointer filter, vtkImageData* polydata) + { + #if VTK_MAJOR_VERSION <= 5 + filter->SetInput(polydata); + #else + filter->SetInputData(polydata); + #endif + } + + template + static void AddInputData(vtkSmartPointer filter, vtkPolyData *polydata) + { + #if VTK_MAJOR_VERSION <= 5 + filter->AddInput(polydata); + #else + filter->AddInputData(polydata); + #endif + } + + static vtkSmartPointer FillScalars(size_t size, const Color& color) + { + Vec3b rgb = Vec3d(color[2], color[1], color[0]); + Vec3b* color_data = new Vec3b[size]; + std::fill(color_data, color_data + size, rgb); + + vtkSmartPointer scalars = vtkSmartPointer::New(); + scalars->SetName("Colors"); + scalars->SetNumberOfComponents(3); + scalars->SetNumberOfTuples((vtkIdType)size); + scalars->SetArray(color_data->val, (vtkIdType)(size * 3), 0, vtkUnsignedCharArray::VTK_DATA_ARRAY_DELETE); + return scalars; + } + + static vtkSmartPointer FillScalars(vtkSmartPointer polydata, const Color& color) + { + return polydata->GetPointData()->SetScalars(FillScalars(polydata->GetNumberOfPoints(), color)), polydata; + } + + static vtkSmartPointer ComputeNormals(vtkSmartPointer polydata) + { + vtkSmartPointer normals_generator = vtkSmartPointer::New(); + normals_generator->ComputePointNormalsOn(); + normals_generator->ComputeCellNormalsOff(); + normals_generator->SetFeatureAngle(0.1); + normals_generator->SetSplitting(0); + normals_generator->SetConsistency(1); + normals_generator->SetAutoOrientNormals(0); + normals_generator->SetFlipNormals(0); + normals_generator->SetNonManifoldTraversal(1); + VtkUtils::SetInputData(normals_generator, polydata); + normals_generator->Update(); + return normals_generator->GetOutput(); + } + + static vtkSmartPointer TransformPolydata(vtkSmartPointer algorithm_output_port, const Affine3d& pose) + { + vtkSmartPointer transform = vtkSmartPointer::New(); + transform->SetMatrix(vtkmatrix(pose.matrix)); + + vtkSmartPointer transform_filter = vtkSmartPointer::New(); + transform_filter->SetTransform(transform); + transform_filter->SetInputConnection(algorithm_output_port); + transform_filter->Update(); + return transform_filter->GetOutput(); + } + + static vtkSmartPointer TransformPolydata(vtkSmartPointer polydata, const Affine3d& pose) + { + vtkSmartPointer transform = vtkSmartPointer::New(); + transform->SetMatrix(vtkmatrix(pose.matrix)); + + vtkSmartPointer transform_filter = vtkSmartPointer::New(); + VtkUtils::SetInputData(transform_filter, polydata); + transform_filter->SetTransform(transform); + transform_filter->Update(); + return transform_filter->GetOutput(); + } + }; + + vtkSmartPointer vtkCocoaRenderWindowInteractorNew(); + } +} + +#include "vtk/vtkVizInteractorStyle.hpp" +#include "vizimpl.hpp" + +#endif diff --git a/modules/viz/src/shapes.cpp b/modules/viz/src/shapes.cpp new file mode 100644 index 000000000..399106dd3 --- /dev/null +++ b/modules/viz/src/shapes.cpp @@ -0,0 +1,1107 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// line widget implementation +cv::viz::WLine::WLine(const Point3d &pt1, const Point3d &pt2, const Color &color) +{ + vtkSmartPointer line = vtkSmartPointer::New(); + line->SetPoint1(pt1.x, pt1.y, pt1.z); + line->SetPoint2(pt2.x, pt2.y, pt2.z); + line->Update(); + + vtkSmartPointer polydata = line->GetOutput(); + VtkUtils::FillScalars(polydata, color); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WLine cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// sphere widget implementation + +cv::viz::WSphere::WSphere(const Point3d ¢er, double radius, int sphere_resolution, const Color &color) +{ + vtkSmartPointer sphere = vtkSmartPointer::New(); + sphere->SetRadius(radius); + sphere->SetCenter(center.x, center.y, center.z); + sphere->SetPhiResolution(sphere_resolution); + sphere->SetThetaResolution(sphere_resolution); + sphere->LatLongTessellationOff(); + sphere->Update(); + + vtkSmartPointer polydata = sphere->GetOutput(); + VtkUtils::FillScalars(polydata, color); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WSphere cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// plane widget implementation + +cv::viz::WPlane::WPlane(const Size2d& size, const Color &color) +{ + vtkSmartPointer plane = vtkSmartPointer::New(); + plane->SetOrigin(-0.5 * size.width, -0.5 * size.height, 0.0); + plane->SetPoint1( 0.5 * size.width, -0.5 * size.height, 0.0); + plane->SetPoint2(-0.5 * size.width, 0.5 * size.height, 0.0); + plane->Update(); + + vtkSmartPointer polydata = plane->GetOutput(); + VtkUtils::FillScalars(polydata, color); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + actor->GetProperty()->LightingOff(); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WPlane::WPlane(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, const Size2d& size, const Color &color) +{ + Vec3d zvec = normalize(normal); + Vec3d xvec = normalize(new_yaxis.cross(zvec)); + Vec3d yvec = zvec.cross(xvec); + + WPlane plane(size, color); + plane.applyTransform(makeTransformToGlobal(xvec, yvec, zvec, center)); + *this = plane; +} + +template<> cv::viz::WPlane cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// arrow widget implementation + +cv::viz::WArrow::WArrow(const Point3d& pt1, const Point3d& pt2, double thickness, const Color &color) +{ + vtkSmartPointer arrow_source = vtkSmartPointer::New(); + arrow_source->SetShaftRadius(thickness); + arrow_source->SetTipRadius(thickness * 3.0); + arrow_source->SetTipLength(thickness * 10.0); + + Vec3d arbitrary = get_random_vec(); + Vec3d start_point(pt1.x, pt1.y, pt1.z), end_point(pt2.x, pt2.y, pt2.z); + + double length = norm(end_point - start_point); + + Vec3d xvec = normalized(end_point - start_point); + Vec3d zvec = normalized(xvec.cross(arbitrary)); + Vec3d yvec = zvec.cross(xvec); + + Matx33d R = makeTransformToGlobal(xvec, yvec, zvec).rotation(); + Affine3d transform_with_scale(R * length, start_point); + + vtkSmartPointer polydata = VtkUtils::TransformPolydata(arrow_source->GetOutputPort(), transform_with_scale); + VtkUtils::FillScalars(polydata, color); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WArrow cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// circle widget implementation + +cv::viz::WCircle::WCircle(double radius, double thickness, const Color &color) +{ + vtkSmartPointer disk = vtkSmartPointer::New(); + disk->SetCircumferentialResolution(30); + disk->SetInnerRadius(radius - thickness); + disk->SetOuterRadius(radius + thickness); + disk->Update(); + + vtkSmartPointer polydata = disk->GetOutput(); + VtkUtils::FillScalars(polydata, color); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->LightingOff(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WCircle::WCircle(double radius, const Point3d& center, const Vec3d& normal, double thickness, const Color &color) +{ + Vec3d arbitrary = get_random_vec(); + Vec3d zvec = normalized(normal); + Vec3d xvec = normalized(zvec.cross(arbitrary)); + Vec3d yvec = zvec.cross(xvec); + + WCircle circle(radius, thickness, color); + circle.applyTransform(makeTransformToGlobal(xvec, yvec, zvec, center)); + *this = circle; +} + +template<> cv::viz::WCircle cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// WCone widget implementation + +cv::viz::WCone::WCone(double length, double radius, int resolution, const Color &color) +{ + vtkSmartPointer cone_source = vtkSmartPointer::New(); + cone_source->SetCenter(length*0.5, 0.0, 0.0); + cone_source->SetHeight(length); + cone_source->SetRadius(radius); + cone_source->SetResolution(resolution); + cone_source->Update(); + + vtkSmartPointer polydata = cone_source->GetOutput(); + VtkUtils::FillScalars(polydata, color); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WCone::WCone(double radius, const Point3d& center, const Point3d& tip, int resolution, const Color &color) +{ + Vec3d arbitrary = get_random_vec(); + Vec3d xvec = normalized(Vec3d(tip - center)); + Vec3d zvec = normalized(xvec.cross(arbitrary)); + Vec3d yvec = zvec.cross(xvec); + + WCone circle(norm(tip - center), radius, resolution, color); + circle.applyTransform(makeTransformToGlobal(xvec, yvec, zvec, center)); + *this = circle; +} + +template<> cv::viz::WCone cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// cylinder widget implementation + +cv::viz::WCylinder::WCylinder(const Point3d& axis_point1, const Point3d& axis_point2, double radius, int numsides, const Color &color) +{ + vtkSmartPointer line = vtkSmartPointer::New(); + line->SetPoint1(axis_point1.x, axis_point1.y, axis_point1.z); + line->SetPoint2(axis_point2.x, axis_point2.y, axis_point2.z); + + vtkSmartPointer tuber = vtkSmartPointer::New(); + tuber->SetInputConnection(line->GetOutputPort()); + tuber->SetNumberOfSides(numsides); + tuber->SetRadius(radius); + tuber->Update(); + + vtkSmartPointer polydata = tuber->GetOutput(); + VtkUtils::FillScalars(polydata, color); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WCylinder cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// cylinder widget implementation + +cv::viz::WCube::WCube(const Point3d& min_point, const Point3d& max_point, bool wire_frame, const Color &color) +{ + double bounds[6]; + bounds[0] = std::min(min_point.x, max_point.x); + bounds[1] = std::max(min_point.x, max_point.x); + bounds[2] = std::min(min_point.y, max_point.y); + bounds[3] = std::max(min_point.y, max_point.y); + bounds[4] = std::min(min_point.z, max_point.z); + bounds[5] = std::max(min_point.z, max_point.z); + + vtkSmartPointer cube; + if (wire_frame) + { + cube = vtkSmartPointer::New(); + vtkOutlineSource::SafeDownCast(cube)->SetBounds(bounds); + } + else + { + cube = vtkSmartPointer::New(); + vtkCubeSource::SafeDownCast(cube)->SetBounds(bounds); + } + cube->Update(); + vtkSmartPointer polydata =cube->GetOutput(); + VtkUtils::FillScalars(polydata, color); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WCube cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// coordinate system widget implementation + +cv::viz::WCoordinateSystem::WCoordinateSystem(double scale) +{ + vtkSmartPointer axes = vtkSmartPointer::New(); + axes->SetOrigin(0, 0, 0); + axes->SetScaleFactor(scale); + axes->Update(); + + vtkSmartPointer colors = vtkSmartPointer::New(); + colors->SetNumberOfComponents(3); + colors->InsertNextTuple3(255, 0, 0); + colors->InsertNextTuple3(255, 0, 0); + colors->InsertNextTuple3(0, 255, 0); + colors->InsertNextTuple3(0, 255, 0); + colors->InsertNextTuple3(0, 0, 255); + colors->InsertNextTuple3(0, 0, 255); + + vtkSmartPointer polydata = axes->GetOutput(); + polydata->GetPointData()->SetScalars(colors); + + vtkSmartPointer tube_filter = vtkSmartPointer::New(); + VtkUtils::SetInputData(tube_filter, polydata); + tube_filter->SetRadius(axes->GetScaleFactor() / 50.0); + tube_filter->SetNumberOfSides(6); + tube_filter->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetScalarModeToUsePointData(); + VtkUtils::SetInputData(mapper, tube_filter->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WCoordinateSystem cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// polyline widget implementation + +cv::viz::WPolyLine::WPolyLine(InputArray points, InputArray colors) +{ + vtkSmartPointer cloud_source = vtkSmartPointer::New(); + cloud_source->SetColorCloud(points, colors); + cloud_source->Update(); + + vtkSmartPointer polydata = cloud_source->GetOutput(); + + vtkSmartPointer cell_array = vtkSmartPointer::New(); + cell_array->Allocate(cell_array->EstimateSize(1, polydata->GetNumberOfPoints())); + cell_array->InsertNextCell(polydata->GetNumberOfPoints()); + for(vtkIdType i = 0; i < polydata->GetNumberOfPoints(); ++i) + cell_array->InsertCellPoint(i); + + polydata->SetLines(cell_array); + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + mapper->SetScalarRange(0, 255); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WPolyLine::WPolyLine(InputArray points, const Color &color) +{ + WPolyLine polyline(points, Mat(points.size(), CV_8UC3, color)); + *this = polyline; +} + +template<> cv::viz::WPolyLine cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// grid widget implementation + + +cv::viz::WGrid::WGrid(const Vec2i &cells, const Vec2d &cells_spacing, const Color &color) +{ + vtkSmartPointer grid_data = vtkSmartPointer::New(); + + // Add 1 to dimensions because in ImageData dimensions is the number of lines + // - however here it means number of cells + grid_data->SetDimensions(cells[0]+1, cells[1]+1, 1); + grid_data->SetSpacing(cells_spacing[0], cells_spacing[1], 0.); + + // Set origin of the grid to be the middle of the grid + grid_data->SetOrigin(cells[0] * cells_spacing[0] * (-0.5), cells[1] * cells_spacing[1] * (-0.5), 0); + + // Extract the edges so we have the grid + vtkSmartPointer extract_edges = vtkSmartPointer::New(); + VtkUtils::SetInputData(extract_edges, grid_data); + extract_edges->Update(); + + vtkSmartPointer polydata = extract_edges->GetOutput(); + VtkUtils::FillScalars(polydata, color); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WGrid::WGrid(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, const Vec2i &cells, const Vec2d &cells_spacing, const Color &color) +{ + Vec3d zvec = normalize(normal); + Vec3d xvec = normalize(new_yaxis.cross(zvec)); + Vec3d yvec = zvec.cross(xvec); + + WGrid grid(cells, cells_spacing, color); + grid.applyTransform(makeTransformToGlobal(xvec, yvec, zvec, center)); + *this = grid; +} + +template<> cv::viz::WGrid cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// text3D widget implementation + +cv::viz::WText3D::WText3D(const String &text, const Point3d &position, double text_scale, bool face_camera, const Color &color) +{ + vtkSmartPointer textSource = vtkSmartPointer::New(); + textSource->SetText(text.c_str()); + textSource->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(textSource->GetOutputPort()); + + if (face_camera) + { + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + actor->SetPosition(position.x, position.y, position.z); + actor->SetScale(text_scale); + WidgetAccessor::setProp(*this, actor); + } + else + { + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + actor->SetPosition(position.x, position.y, position.z); + actor->SetScale(text_scale); + actor->GetProperty()->LightingOff(); + WidgetAccessor::setProp(*this, actor); + } + + setColor(color); +} + +void cv::viz::WText3D::setText(const String &text) +{ + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("This widget does not support text." && actor); + + // Update text source + vtkPolyDataMapper *mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + vtkVectorText * textSource = vtkVectorText::SafeDownCast(mapper->GetInputConnection(0,0)->GetProducer()); + CV_Assert("This widget does not support text." && textSource); + + textSource->SetText(text.c_str()); + textSource->Modified(); + textSource->Update(); +} + +cv::String cv::viz::WText3D::getText() const +{ + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("This widget does not support text." && actor); + + vtkPolyDataMapper *mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + vtkVectorText * textSource = vtkVectorText::SafeDownCast(mapper->GetInputConnection(0,0)->GetProducer()); + CV_Assert("This widget does not support text." && textSource); + + return textSource->GetText(); +} + +template<> cv::viz::WText3D cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// text widget implementation + +cv::viz::WText::WText(const String &text, const Point &pos, int font_size, const Color &color) +{ + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetDisplayPosition(pos.x, pos.y); + actor->SetInput(text.c_str()); + + actor->GetProperty()->SetDisplayLocationToForeground(); + + vtkSmartPointer tprop = actor->GetTextProperty(); + tprop->SetFontSize(font_size); + tprop->SetFontFamilyToCourier(); + tprop->SetJustificationToLeft(); + tprop->BoldOn(); + + Color c = vtkcolor(color); + tprop->SetColor(c.val); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WText cv::viz::Widget::cast() const +{ + Widget2D widget = this->cast(); + return static_cast(widget); +} + +void cv::viz::WText::setText(const String &text) +{ + vtkTextActor *actor = vtkTextActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("This widget does not support text." && actor); + actor->SetInput(text.c_str()); +} + +cv::String cv::viz::WText::getText() const +{ + vtkTextActor *actor = vtkTextActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("This widget does not support text." && actor); + return actor->GetInput(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// image overlay widget implementation + +cv::viz::WImageOverlay::WImageOverlay(InputArray image, const Rect &rect) +{ + CV_Assert(!image.empty() && image.depth() == CV_8U); + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetImage(image); + Size sz = image.size(); + + // Scale the image based on the Rect, and flip to match y-ais orientation + vtkSmartPointer transform = vtkSmartPointer::New(); + transform->Scale(sz.width/(double)rect.width, sz.height/(double)rect.height, 1.0); + transform->RotateX(180); + + vtkSmartPointer image_reslice = vtkSmartPointer::New(); + image_reslice->SetResliceTransform(transform); + image_reslice->SetInputConnection(source->GetOutputPort()); + image_reslice->SetOutputDimensionality(2); + image_reslice->InterpolateOn(); + image_reslice->AutoCropOutputOn(); + image_reslice->Update(); + + vtkSmartPointer image_mapper = vtkSmartPointer::New(); + image_mapper->SetInputConnection(image_reslice->GetOutputPort()); + image_mapper->SetColorWindow(255); // OpenCV color + image_mapper->SetColorLevel(127.5); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(image_mapper); + actor->SetPosition(rect.x, rect.y); + actor->GetProperty()->SetDisplayLocationToForeground(); + + WidgetAccessor::setProp(*this, actor); +} + +void cv::viz::WImageOverlay::setImage(InputArray image) +{ + CV_Assert(!image.empty() && image.depth() == CV_8U); + + vtkActor2D *actor = vtkActor2D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("This widget does not support overlay image." && actor); + + vtkImageMapper *mapper = vtkImageMapper::SafeDownCast(actor->GetMapper()); + CV_Assert("This widget does not support overlay image." && mapper); + \ + Vec6i extent; + mapper->GetInput()->GetExtent(extent.val); + Size size(extent[1], extent[3]); + + // Create the vtk image and set its parameters based on input image + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetImage(image); + Size sz = image.size(); + + // Scale the image based on the Rect, and flip to match y-ais orientation + vtkSmartPointer transform = vtkSmartPointer::New(); + transform->Scale(sz.width/(double)size.width, sz.height/(double)size.height, 1.0); + transform->RotateX(180); + + vtkSmartPointer image_reslice = vtkSmartPointer::New(); + image_reslice->SetResliceTransform(transform); + image_reslice->SetInputConnection(source->GetOutputPort()); + image_reslice->SetOutputDimensionality(2); + image_reslice->InterpolateOn(); + image_reslice->AutoCropOutputOn(); + image_reslice->Update(); + + mapper->SetInputConnection(image_reslice->GetOutputPort()); +} + +template<> cv::viz::WImageOverlay cv::viz::Widget::cast() const +{ + Widget2D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// image 3D widget implementation + +cv::viz::WImage3D::WImage3D(InputArray image, const Size2d &size) +{ + CV_Assert(!image.empty() && image.depth() == CV_8U); + + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetImage(image); + + vtkSmartPointer texture = vtkSmartPointer::New(); + texture->SetInputConnection(source->GetOutputPort()); + + vtkSmartPointer plane = vtkSmartPointer::New(); + plane->SetOrigin(-0.5 * size.width, -0.5 * size.height, 0.0); + plane->SetPoint1( 0.5 * size.width, -0.5 * size.height, 0.0); + plane->SetPoint2(-0.5 * size.width, 0.5 * size.height, 0.0); + + vtkSmartPointer textured_plane = vtkSmartPointer::New(); + textured_plane->SetInputConnection(plane->GetOutputPort()); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(textured_plane->GetOutputPort()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + actor->SetTexture(texture); + actor->GetProperty()->ShadingOff(); + actor->GetProperty()->LightingOff(); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WImage3D::WImage3D(InputArray image, const Size2d &size, const Vec3d ¢er, const Vec3d &normal, const Vec3d &up_vector) +{ + CV_Assert(!image.empty() && image.depth() == CV_8U); + + // Compute the transformation matrix for drawing the camera frame in a scene + Vec3d n = normalize(normal); + Vec3d u = normalize(up_vector.cross(n)); + Vec3d v = n.cross(u); + Affine3d pose = makeTransformToGlobal(u, v, n, center); + + WImage3D image3d(image, size); + image3d.applyTransform(pose); + *this = image3d; +} + +void cv::viz::WImage3D::setImage(InputArray image) +{ + CV_Assert(!image.empty() && image.depth() == CV_8U); + + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("This widget does not support 3D image." && actor); + + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetImage(image); + + vtkSmartPointer texture = vtkSmartPointer::New(); + texture->SetInputConnection(source->GetOutputPort()); + + actor->SetTexture(texture); +} + +void cv::viz::WImage3D::setSize(const cv::Size& size) +{ + vtkSmartPointer actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + vtkSmartPointer textured_plane; + vtkSmartPointer plane; + #if VTK_MAJOR_VERSION <= 5 + textured_plane = vtkTextureMapToPlane::SafeDownCast(mapper->GetInputConnection(0,0)->GetProducer()); + plane = vtkPlaneSource::SafeDownCast(textured_plane->GetInputConnection(0,0)->GetProducer()); + #else + textured_plane = vtkTextureMapToPlane::SafeDownCast(mapper->GetInputAlgorithm()); + plane = vtkPlaneSource::SafeDownCast(textured_plane->GetInputAlgorithm()); + #endif + plane->SetOrigin(-0.5 * size.width, -0.5 * size.height, 0.0); + plane->SetPoint1( 0.5 * size.width, -0.5 * size.height, 0.0); + plane->SetPoint2(-0.5 * size.width, 0.5 * size.height, 0.0); +} + +template<> cv::viz::WImage3D cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// camera position widget implementation + +namespace cv { namespace viz { namespace +{ + struct CameraPositionUtils + { + static vtkSmartPointer createFrustum(double aspect_ratio, double fovy, double scale) + { + vtkSmartPointer camera = vtkSmartPointer::New(); + camera->SetViewAngle(fovy); + camera->SetPosition(0.0, 0.0, 0.0); + camera->SetViewUp(0.0, 1.0, 0.0); + camera->SetFocalPoint(0.0, 0.0, 1.0); + camera->SetClippingRange(1e-9, scale); + + double planes_array[24]; + camera->GetFrustumPlanes(aspect_ratio, planes_array); + + vtkSmartPointer planes = vtkSmartPointer::New(); + planes->SetFrustumPlanes(planes_array); + + vtkSmartPointer frustumSource = vtkSmartPointer::New(); + frustumSource->SetPlanes(planes); + + vtkSmartPointer extract_edges = vtkSmartPointer::New(); + extract_edges->SetInputConnection(frustumSource->GetOutputPort()); + extract_edges->Update(); + + return extract_edges->GetOutput(); + } + + static Mat ensureColorImage(InputArray image) + { + Mat color(image.size(), CV_8UC3); + if (image.channels() == 1) + { + Vec3b *drow = color.ptr(); + for(int y = 0; y < color.rows; ++y) + { + const unsigned char *srow = image.getMat().ptr(y); + const unsigned char *send = srow + color.cols; + for(;srow < send;) + *drow++ = Vec3b::all(*srow++); + } + } + else + image.copyTo(color); + return color; + } + }; +}}} + +cv::viz::WCameraPosition::WCameraPosition(double scale) +{ + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, getPolyData(WCoordinateSystem(scale))); + mapper->SetScalarModeToUsePointData(); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WCameraPosition::WCameraPosition(const Matx33d &K, double scale, const Color &color) +{ + double f_x = K(0,0), f_y = K(1,1), c_y = K(1,2); + + // Assuming that this is an ideal camera (c_y and c_x are at the center of the image) + double fovy = 2.0 * atan2(c_y, f_y) * 180 / CV_PI; + double aspect_ratio = f_y / f_x; + + vtkSmartPointer polydata = CameraPositionUtils::createFrustum(aspect_ratio, fovy, scale); + VtkUtils::FillScalars(polydata, color); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WCameraPosition::WCameraPosition(const Vec2d &fov, double scale, const Color &color) +{ + double aspect_ratio = tan(fov[0] * 0.5) / tan(fov[1] * 0.5); + double fovy = fov[1] * 180 / CV_PI; + + vtkSmartPointer polydata = CameraPositionUtils::createFrustum(aspect_ratio, fovy, scale); + VtkUtils::FillScalars(polydata, color); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WCameraPosition::WCameraPosition(const Matx33d &K, InputArray _image, double scale, const Color &color) +{ + CV_Assert(!_image.empty() && _image.depth() == CV_8U); + Mat image = CameraPositionUtils::ensureColorImage(_image); + image.at(0, 0) = Vec3d(color.val); //workaround of VTK limitation + + double f_y = K(1,1), c_y = K(1,2); + // Assuming that this is an ideal camera (c_y and c_x are at the center of the image) + double fovy = 2.0 * atan2(c_y, f_y) * 180.0 / CV_PI; + double far_end_height = 2.00 * c_y * scale / f_y; + double aspect_ratio = image.cols/(double)image.rows; + double image_scale = far_end_height/image.rows; + + WImage3D image_widget(image, Size2d(image.size()) * image_scale); + image_widget.applyTransform(Affine3d().translate(Vec3d(0, 0, scale))); + vtkSmartPointer plane = getPolyData(image_widget); + + vtkSmartPointer frustum = CameraPositionUtils::createFrustum(aspect_ratio, fovy, scale); + + // Frustum needs to be textured or else it can't be combined with image + vtkSmartPointer frustum_texture = vtkSmartPointer::New(); + VtkUtils::SetInputData(frustum_texture, frustum); + frustum_texture->SetSRange(0.0, 0.0); // Texture mapping with only one pixel + frustum_texture->SetTRange(0.0, 0.0); // from the image to have constant color + + vtkSmartPointer append_filter = vtkSmartPointer::New(); + append_filter->AddInputConnection(frustum_texture->GetOutputPort()); + VtkUtils::AddInputData(append_filter, plane); + + vtkSmartPointer actor = getActor(image_widget); + actor->GetMapper()->SetInputConnection(append_filter->GetOutputPort()); + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WCameraPosition::WCameraPosition(const Vec2d &fov, InputArray _image, double scale, const Color &color) +{ + CV_Assert(!_image.empty() && _image.depth() == CV_8U); + Mat image = CameraPositionUtils::ensureColorImage(_image); + image.at(0, 0) = Vec3d(color.val); //workaround of VTK limitation + + double fovy = fov[1] * 180.0 / CV_PI; + double far_end_height = 2.0 * scale * tan(fov[1] * 0.5); + double aspect_ratio = image.cols/(double)image.rows; + double image_scale = far_end_height/image.rows; + + WImage3D image_widget(image, Size2d(image.size()) * image_scale); + image_widget.applyTransform(Affine3d().translate(Vec3d(0, 0, scale))); + vtkSmartPointer plane = getPolyData(image_widget); + + vtkSmartPointer frustum = CameraPositionUtils::createFrustum(aspect_ratio, fovy, scale); + + // Frustum needs to be textured or else it can't be combined with image + vtkSmartPointer frustum_texture = vtkSmartPointer::New(); + VtkUtils::SetInputData(frustum_texture, frustum); + frustum_texture->SetSRange(0.0, 0.0); // Texture mapping with only one pixel + frustum_texture->SetTRange(0.0, 0.0); // from the image to have constant color + + vtkSmartPointer append_filter = vtkSmartPointer::New(); + append_filter->AddInputConnection(frustum_texture->GetOutputPort()); + VtkUtils::AddInputData(append_filter, plane); + + vtkSmartPointer actor = getActor(image_widget); + actor->GetMapper()->SetInputConnection(append_filter->GetOutputPort()); + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WCameraPosition cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// trajectory widget implementation + +cv::viz::WTrajectory::WTrajectory(InputArray _path, int display_mode, double scale, const Color &color) +{ + vtkSmartPointer append_filter = vtkSmartPointer::New(); + + // Bitwise and with 3 in order to limit the domain to 2 bits + if (display_mode & WTrajectory::PATH) + { + Mat points = vtkTrajectorySource::ExtractPoints(_path); + vtkSmartPointer polydata = getPolyData(WPolyLine(points, color)); + VtkUtils::AddInputData(append_filter, polydata); + } + + if (display_mode & WTrajectory::FRAMES) + { + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetTrajectory(_path); + + vtkSmartPointer glyph = getPolyData(WCoordinateSystem(scale)); + + vtkSmartPointer tensor_glyph = vtkSmartPointer::New(); + tensor_glyph->SetInputConnection(source->GetOutputPort()); + VtkUtils::SetSourceData(tensor_glyph, glyph); + tensor_glyph->ExtractEigenvaluesOff(); // Treat as a rotation matrix, not as something with eigenvalues + tensor_glyph->ThreeGlyphsOff(); + tensor_glyph->SymmetricOff(); + tensor_glyph->ColorGlyphsOff(); + + append_filter->AddInputConnection(tensor_glyph->GetOutputPort()); + } + append_filter->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, append_filter->GetOutput()); + mapper->SetScalarModeToUsePointData(); + mapper->SetScalarRange(0, 255); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WTrajectory cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// WTrajectoryFrustums widget implementation + +cv::viz::WTrajectoryFrustums::WTrajectoryFrustums(InputArray _path, const Matx33d &K, double scale, const Color &color) +{ + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetTrajectory(_path); + + vtkSmartPointer glyph = getPolyData(WCameraPosition(K, scale)); + VtkUtils::FillScalars(glyph, color); + + vtkSmartPointer tensor_glyph = vtkSmartPointer::New(); + tensor_glyph->SetInputConnection(source->GetOutputPort()); + VtkUtils::SetSourceData(tensor_glyph, glyph); + tensor_glyph->ExtractEigenvaluesOff(); // Treat as a rotation matrix, not as something with eigenvalues + tensor_glyph->ThreeGlyphsOff(); + tensor_glyph->SymmetricOff(); + tensor_glyph->ColorGlyphsOff(); + tensor_glyph->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, tensor_glyph->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WTrajectoryFrustums::WTrajectoryFrustums(InputArray _path, const Vec2d &fov, double scale, const Color &color) +{ + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetTrajectory(_path); + + vtkSmartPointer glyph = getPolyData(WCameraPosition(fov, scale)); + VtkUtils::FillScalars(glyph, color); + + vtkSmartPointer tensor_glyph = vtkSmartPointer::New(); + tensor_glyph->SetInputConnection(source->GetOutputPort()); + VtkUtils::SetSourceData(tensor_glyph, glyph); + tensor_glyph->ExtractEigenvaluesOff(); // Treat as a rotation matrix, not as something with eigenvalues + tensor_glyph->ThreeGlyphsOff(); + tensor_glyph->SymmetricOff(); + tensor_glyph->ColorGlyphsOff(); + tensor_glyph->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, tensor_glyph->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WTrajectoryFrustums cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// WTrajectorySpheres widget implementation + +cv::viz::WTrajectorySpheres::WTrajectorySpheres(InputArray _path, double line_length, double radius, const Color &from, const Color &to) +{ + CV_Assert(_path.kind() == _InputArray::STD_VECTOR || _path.kind() == _InputArray::MAT); + CV_Assert(_path.type() == CV_32FC(16) || _path.type() == CV_64FC(16)); + + Mat path64; + _path.getMat().convertTo(path64, CV_64F); + Affine3d *traj = path64.ptr(); + size_t total = path64.total(); + + vtkSmartPointer append_filter = vtkSmartPointer::New(); + + for(size_t i = 0; i < total; ++i) + { + Vec3d curr = traj[i].translation(); + + vtkSmartPointer sphere_source = vtkSmartPointer::New(); + sphere_source->SetCenter(curr.val); + sphere_source->SetRadius( (i == 0) ? 2 * radius : radius ); + sphere_source->Update(); + + double alpha = static_cast(i)/total; + Color c = from * (1 - alpha) + to * alpha; + + vtkSmartPointer polydata = sphere_source->GetOutput(); + polydata->GetCellData()->SetScalars(VtkUtils::FillScalars(polydata->GetNumberOfCells(), c)); + VtkUtils::AddInputData(append_filter, polydata); + + if (i > 0) + { + Vec3d prev = traj[i-1].translation(); + Vec3d lvec = prev - curr; + + if(norm(lvec) > line_length) + lvec = normalize(lvec) * line_length; + + Vec3d lend = curr + lvec; + + vtkSmartPointer line_source = vtkSmartPointer::New(); + line_source->SetPoint1(curr.val); + line_source->SetPoint2(lend.val); + line_source->Update(); + vtkSmartPointer polydata_ = line_source->GetOutput(); + polydata_->GetCellData()->SetScalars(VtkUtils::FillScalars(polydata_->GetNumberOfCells(), c)); + VtkUtils::AddInputData(append_filter, polydata_); + } + } + append_filter->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetScalarModeToUseCellData(); + VtkUtils::SetInputData(mapper, append_filter->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WTrajectorySpheres cv::viz::Widget::cast() const +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} diff --git a/modules/viz/src/types.cpp b/modules/viz/src/types.cpp new file mode 100644 index 000000000..65571a192 --- /dev/null +++ b/modules/viz/src/types.cpp @@ -0,0 +1,228 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +//////////////////////////////////////////////////////////////////// +/// Events + +cv::viz::KeyboardEvent::KeyboardEvent(Action _action, const String& _symbol, unsigned char _code, int _modifiers) + : action(_action), symbol(_symbol), code(_code), modifiers(_modifiers) {} + +cv::viz::MouseEvent::MouseEvent(const Type& _type, const MouseButton& _button, const Point& _pointer, int _modifiers) + : type(_type), button(_button), pointer(_pointer), modifiers(_modifiers) {} + +//////////////////////////////////////////////////////////////////// +/// cv::viz::Mesh3d + +cv::viz::Mesh cv::viz::Mesh::load(const String& file, int type) +{ + vtkSmartPointer reader = vtkSmartPointer::New(); + switch (type) { + case LOAD_AUTO: + { + CV_Error(Error::StsError, "cv::viz::Mesh::LOAD_AUTO: Not implemented yet"); + } + case LOAD_PLY: + { + vtkSmartPointer ply_reader = vtkSmartPointer::New(); + ply_reader->SetFileName(file.c_str()); + ply_reader->Update(); + reader = ply_reader; + break; + } + case LOAD_OBJ: + { + vtkSmartPointer obj_reader = vtkSmartPointer::New(); + obj_reader->SetFileName(file.c_str()); + obj_reader->Update(); + reader = obj_reader; + break; + } + default: + CV_Error(Error::StsError, "cv::viz::Mesh::load: Unknown file type"); + } + + vtkSmartPointer polydata = reader->GetOutput(); + CV_Assert("File does not exist or file format is not supported." && polydata); + + Mesh mesh; + vtkSmartPointer sink = vtkSmartPointer::New(); + sink->SetOutput(mesh.cloud, mesh.colors, mesh.normals, mesh.tcoords); + sink->SetInputConnection(reader->GetOutputPort()); + sink->Write(); + + // Now handle the polygons + vtkSmartPointer polygons = polydata->GetPolys(); + mesh.polygons.create(1, polygons->GetSize(), CV_32SC1); + int* poly_ptr = mesh.polygons.ptr(); + + polygons->InitTraversal(); + vtkIdType nr_cell_points, *cell_points; + while (polygons->GetNextCell(nr_cell_points, cell_points)) + { + *poly_ptr++ = nr_cell_points; + for (vtkIdType i = 0; i < nr_cell_points; ++i) + *poly_ptr++ = (int)cell_points[i]; + } + + return mesh; +} + +//////////////////////////////////////////////////////////////////// +/// Camera implementation + +cv::viz::Camera::Camera(double fx, double fy, double cx, double cy, const Size &window_size) +{ + init(fx, fy, cx, cy, window_size); +} + +cv::viz::Camera::Camera(const Vec2d &fov, const Size &window_size) +{ + CV_Assert(window_size.width > 0 && window_size.height > 0); + setClip(Vec2d(0.01, 1000.01)); // Default clipping + setFov(fov); + window_size_ = window_size; + // Principal point at the center + principal_point_ = Vec2f(static_cast(window_size.width)*0.5f, static_cast(window_size.height)*0.5f); + focal_ = Vec2f(principal_point_[0] / tan(fov_[0]*0.5f), principal_point_[1] / tan(fov_[1]*0.5f)); +} + +cv::viz::Camera::Camera(const cv::Matx33d & K, const Size &window_size) +{ + double f_x = K(0,0); + double f_y = K(1,1); + double c_x = K(0,2); + double c_y = K(1,2); + init(f_x, f_y, c_x, c_y, window_size); +} + +cv::viz::Camera::Camera(const Matx44d &proj, const Size &window_size) +{ + CV_Assert(window_size.width > 0 && window_size.height > 0); + + double near = proj(2,3) / (proj(2,2) - 1.0); + double far = near * (proj(2,2) - 1.0) / (proj(2,2) + 1.0); + double left = near * (proj(0,2)-1) / proj(0,0); + double right = 2.0 * near / proj(0,0) + left; + double bottom = near * (proj(1,2)-1) / proj(1,1); + double top = 2.0 * near / proj(1,1) + bottom; + + double epsilon = 2.2204460492503131e-16; + + principal_point_[0] = fabs(left-right) < epsilon ? window_size.width * 0.5 : (left * window_size.width) / (left - right); + principal_point_[1] = fabs(top-bottom) < epsilon ? window_size.height * 0.5 : (top * window_size.height) / (top - bottom); + + focal_[0] = -near * principal_point_[0] / left; + focal_[1] = near * principal_point_[1] / top; + + setClip(Vec2d(near, far)); + fov_[0] = atan2(principal_point_[0], focal_[0]) + atan2(window_size.width-principal_point_[0], focal_[0]); + fov_[1] = atan2(principal_point_[1], focal_[1]) + atan2(window_size.height-principal_point_[1], focal_[1]); + + window_size_ = window_size; +} + +void cv::viz::Camera::init(double fx, double fy, double cx, double cy, const Size &window_size) +{ + CV_Assert(window_size.width > 0 && window_size.height > 0); + setClip(Vec2d(0.01, 1000.01));// Default clipping + + fov_[0] = atan2(cx, fx) + atan2(window_size.width - cx, fx); + fov_[1] = atan2(cy, fy) + atan2(window_size.height - cy, fy); + + principal_point_[0] = cx; + principal_point_[1] = cy; + + focal_[0] = fx; + focal_[1] = fy; + + window_size_ = window_size; +} + +void cv::viz::Camera::setWindowSize(const Size &window_size) +{ + CV_Assert(window_size.width > 0 && window_size.height > 0); + + // Get the scale factor and update the principal points + float scalex = static_cast(window_size.width) / static_cast(window_size_.width); + float scaley = static_cast(window_size.height) / static_cast(window_size_.height); + + principal_point_[0] *= scalex; + principal_point_[1] *= scaley; + focal_ *= scaley; + // Vertical field of view is fixed! Update horizontal field of view + fov_[0] = (atan2(principal_point_[0],focal_[0]) + atan2(window_size.width-principal_point_[0],focal_[0])); + + window_size_ = window_size; +} + +void cv::viz::Camera::computeProjectionMatrix(Matx44d &proj) const +{ + double top = clip_[0] * principal_point_[1] / focal_[1]; + double left = -clip_[0] * principal_point_[0] / focal_[0]; + double right = clip_[0] * (window_size_.width - principal_point_[0]) / focal_[0]; + double bottom = -clip_[0] * (window_size_.height - principal_point_[1]) / focal_[1]; + + double temp1 = 2.0 * clip_[0]; + double temp2 = 1.0 / (right - left); + double temp3 = 1.0 / (top - bottom); + double temp4 = 1.0 / (clip_[0] - clip_[1]); + + proj = Matx44d::zeros(); + proj(0,0) = temp1 * temp2; + proj(1,1) = temp1 * temp3; + proj(0,2) = (right + left) * temp2; + proj(1,2) = (top + bottom) * temp3; + proj(2,2) = (clip_[1]+clip_[0]) * temp4; + proj(3,2) = -1.0; + proj(2,3) = (temp1 * clip_[1]) * temp4; +} + +cv::viz::Camera cv::viz::Camera::KinectCamera(const Size &window_size) +{ + Matx33d K(525.0, 0.0, 320.0, 0.0, 525.0, 240.0, 0.0, 0.0, 1.0); + return Camera(K, window_size); +} diff --git a/modules/viz/src/viz3d.cpp b/modules/viz/src/viz3d.cpp new file mode 100644 index 000000000..62e570e65 --- /dev/null +++ b/modules/viz/src/viz3d.cpp @@ -0,0 +1,156 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +cv::viz::Viz3d::Viz3d(const String& window_name) : impl_(0) { create(window_name); } + +cv::viz::Viz3d::Viz3d(const Viz3d& other) : impl_(other.impl_) +{ + if (impl_) + CV_XADD(&impl_->ref_counter, 1); +} + +cv::viz::Viz3d& cv::viz::Viz3d::operator=(const Viz3d& other) +{ + if (this != &other) + { + release(); + impl_ = other.impl_; + if (impl_) + CV_XADD(&impl_->ref_counter, 1); + } + return *this; +} + +cv::viz::Viz3d::~Viz3d() { release(); } + +void cv::viz::Viz3d::create(const String &window_name) +{ + if (impl_) + release(); + + if (VizStorage::windowExists(window_name)) + *this = VizStorage::get(window_name); + else + { + impl_ = new VizImpl(window_name); + impl_->ref_counter = 1; + + // Register the window + VizStorage::add(*this); + } +} + +void cv::viz::Viz3d::release() +{ + if (impl_ && CV_XADD(&impl_->ref_counter, -1) == 1) + { + delete impl_; + impl_ = 0; + } + + if (impl_ && impl_->ref_counter == 1) + VizStorage::removeUnreferenced(); + + impl_ = 0; +} + +void cv::viz::Viz3d::spin() { impl_->spin(); } +void cv::viz::Viz3d::spinOnce(int time, bool force_redraw) { impl_->spinOnce(time, force_redraw); } +void cv::viz::Viz3d::setOffScreenRendering() { impl_->setOffScreenRendering(); } +void cv::viz::Viz3d::removeAllLights() { impl_->removeAllLights(); } +void cv::viz::Viz3d::addLight(const Vec3d &position, const Vec3d &focalPoint, const Color &color, + const Color &diffuseColor, const Color &ambientColor, const Color &specularColor) +{ impl_->addLight(position, focalPoint, color, diffuseColor, ambientColor, specularColor); } +bool cv::viz::Viz3d::wasStopped() const { return impl_->wasStopped(); } +void cv::viz::Viz3d::close() { impl_->close(); } + +void cv::viz::Viz3d::registerKeyboardCallback(KeyboardCallback callback, void* cookie) +{ impl_->registerKeyboardCallback(callback, cookie); } + +void cv::viz::Viz3d::registerMouseCallback(MouseCallback callback, void* cookie) +{ impl_->registerMouseCallback(callback, cookie); } + +void cv::viz::Viz3d::showWidget(const String &id, const Widget &widget, const Affine3d &pose) { impl_->showWidget(id, widget, pose); } +void cv::viz::Viz3d::removeWidget(const String &id) { impl_->removeWidget(id); } +cv::viz::Widget cv::viz::Viz3d::getWidget(const String &id) const { return impl_->getWidget(id); } +void cv::viz::Viz3d::removeAllWidgets() { impl_->removeAllWidgets(); } + +void cv::viz::Viz3d::showImage(InputArray image, const Size& window_size) { impl_->showImage(image, window_size); } + +void cv::viz::Viz3d::setWidgetPose(const String &id, const Affine3d &pose) { impl_->setWidgetPose(id, pose); } +void cv::viz::Viz3d::updateWidgetPose(const String &id, const Affine3d &pose) { impl_->updateWidgetPose(id, pose); } +cv::Affine3d cv::viz::Viz3d::getWidgetPose(const String &id) const { return impl_->getWidgetPose(id); } + +void cv::viz::Viz3d::setCamera(const Camera &camera) { impl_->setCamera(camera); } +cv::viz::Camera cv::viz::Viz3d::getCamera() const { return impl_->getCamera(); } +void cv::viz::Viz3d::setViewerPose(const Affine3d &pose) { impl_->setViewerPose(pose); } +cv::Affine3d cv::viz::Viz3d::getViewerPose() const { return impl_->getViewerPose(); } + +void cv::viz::Viz3d::resetCameraViewpoint(const String &id) { impl_->resetCameraViewpoint(id); } +void cv::viz::Viz3d::resetCamera() { impl_->resetCamera(); } + +void cv::viz::Viz3d::convertToWindowCoordinates(const Point3d &pt, Point3d &window_coord) { impl_->convertToWindowCoordinates(pt, window_coord); } +void cv::viz::Viz3d::converTo3DRay(const Point3d &window_coord, Point3d &origin, Vec3d &direction) { impl_->converTo3DRay(window_coord, origin, direction); } + +cv::Size cv::viz::Viz3d::getWindowSize() const { return impl_->getWindowSize(); } +void cv::viz::Viz3d::setWindowSize(const Size &window_size) { impl_->setWindowSize(window_size); } +cv::String cv::viz::Viz3d::getWindowName() const { return impl_->getWindowName(); } +cv::Mat cv::viz::Viz3d::getScreenshot() const { return impl_->getScreenshot(); } +void cv::viz::Viz3d::saveScreenshot(const String &file) { impl_->saveScreenshot(file); } +void cv::viz::Viz3d::setWindowPosition(const Point& window_position) { impl_->setWindowPosition(window_position); } +void cv::viz::Viz3d::setFullScreen(bool mode) { impl_->setFullScreen(mode); } +void cv::viz::Viz3d::setBackgroundColor(const Color& color, const Color& color2) { impl_->setBackgroundColor(color, color2); } + +void cv::viz::Viz3d::setBackgroundTexture(InputArray image) { impl_->setBackgroundTexture(image); } +void cv::viz::Viz3d::setBackgroundMeshLab() {impl_->setBackgroundMeshLab(); } + +void cv::viz::Viz3d::setRenderingProperty(const String &id, int property, double value) { getWidget(id).setRenderingProperty(property, value); } +double cv::viz::Viz3d::getRenderingProperty(const String &id, int property) { return getWidget(id).getRenderingProperty(property); } + +void cv::viz::Viz3d::setRepresentation(int representation) { impl_->setRepresentation(representation); } + +void cv::viz::Viz3d::setGlobalWarnings(bool enabled) { vtkObject::SetGlobalWarningDisplay(enabled ? 1 : 0); } diff --git a/modules/viz/src/vizcore.cpp b/modules/viz/src/vizcore.cpp new file mode 100644 index 000000000..a0ca4980a --- /dev/null +++ b/modules/viz/src/vizcore.cpp @@ -0,0 +1,352 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +cv::Affine3d cv::viz::makeTransformToGlobal(const Vec3d& axis_x, const Vec3d& axis_y, const Vec3d& axis_z, const Vec3d& origin) +{ + Affine3d::Mat3 R(axis_x[0], axis_y[0], axis_z[0], + axis_x[1], axis_y[1], axis_z[1], + axis_x[2], axis_y[2], axis_z[2]); + + return Affine3d(R, origin); +} + +cv::Affine3d cv::viz::makeCameraPose(const Vec3d& position, const Vec3d& focal_point, const Vec3d& y_dir) +{ + // Compute the transformation matrix for drawing the camera frame in a scene + Vec3d n = normalize(focal_point - position); + Vec3d u = normalize(y_dir.cross(n)); + Vec3d v = n.cross(u); + + return makeTransformToGlobal(u, v, n, position); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// VizStorage implementation + +#if defined(_WIN32) && !defined(__CYGWIN__) + + #include + + static BOOL WINAPI ConsoleHandlerRoutine(DWORD /*dwCtrlType*/) + { + vtkObject::GlobalWarningDisplayOff(); + return FALSE; + } + + static void register_console_handler() + { + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO hOutInfo; + if (GetConsoleScreenBufferInfo(hOut, &hOutInfo)) + SetConsoleCtrlHandler(ConsoleHandlerRoutine, TRUE); + } + +#else + + void register_console_handler(); + void register_console_handler() {} + +#endif + + +cv::viz::VizStorage cv::viz::VizStorage::init; +cv::viz::VizMap cv::viz::VizStorage::storage; + +void cv::viz::VizMap::replace_clear() { type().swap(m); } +cv::viz::VizMap::~VizMap() { replace_clear(); } + +cv::viz::VizStorage::VizStorage() +{ + register_console_handler(); +} +void cv::viz::VizStorage::unregisterAll() { storage.replace_clear(); } + +cv::viz::Viz3d& cv::viz::VizStorage::get(const String &window_name) +{ + String name = generateWindowName(window_name); + VizMap::iterator vm_itr = storage.m.find(name); + CV_Assert(vm_itr != storage.m.end()); + return vm_itr->second; +} + +void cv::viz::VizStorage::add(const Viz3d& window) +{ + String window_name = window.getWindowName(); + VizMap::iterator vm_itr = storage.m.find(window_name); + CV_Assert(vm_itr == storage.m.end()); + storage.m.insert(std::make_pair(window_name, window)); +} + +bool cv::viz::VizStorage::windowExists(const String &window_name) +{ + String name = generateWindowName(window_name); + return storage.m.find(name) != storage.m.end(); +} + +void cv::viz::VizStorage::removeUnreferenced() +{ + for(VizMap::iterator pos = storage.m.begin(); pos != storage.m.end();) + if(pos->second.impl_->ref_counter == 1) + storage.m.erase(pos++); + else + ++pos; +} + +cv::String cv::viz::VizStorage::generateWindowName(const String &window_name) +{ + String output = "Viz"; + // Already is Viz + if (window_name == output) + return output; + + String prefixed = output + " - "; + if (window_name.substr(0, prefixed.length()) == prefixed) + output = window_name; // Already has "Viz - " + else if (window_name.substr(0, output.length()) == output) + output = prefixed + window_name; // Doesn't have prefix + else + output = (window_name == "" ? output : prefixed + window_name); + + return output; +} + +cv::viz::Viz3d cv::viz::getWindowByName(const String &window_name) { return Viz3d (window_name); } +void cv::viz::unregisterAllWindows() { VizStorage::unregisterAll(); } + +cv::viz::Viz3d cv::viz::imshow(const String& window_name, InputArray image, const Size& window_size) +{ + Viz3d viz = getWindowByName(window_name); + viz.showImage(image, window_size); + return viz; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Read/write clouds. Supported formats: ply, stl, xyz, obj + +void cv::viz::writeCloud(const String& file, InputArray cloud, InputArray colors, InputArray normals, bool binary) +{ + CV_Assert(file.size() > 4 && "Extension is required"); + String extension = file.substr(file.size()-4); + + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetColorCloudNormals(cloud, colors, normals); + + vtkSmartPointer writer; + if (extension == ".xyz") + { + writer = vtkSmartPointer::New(); + vtkXYZWriter::SafeDownCast(writer)->SetFileName(file.c_str()); + } + else if (extension == ".ply") + { + writer = vtkSmartPointer::New(); + vtkPLYWriter::SafeDownCast(writer)->SetFileName(file.c_str()); + vtkPLYWriter::SafeDownCast(writer)->SetFileType(binary ? VTK_BINARY : VTK_ASCII); + vtkPLYWriter::SafeDownCast(writer)->SetArrayName("Colors"); + } + else if (extension == ".obj") + { + writer = vtkSmartPointer::New(); + vtkOBJWriter::SafeDownCast(writer)->SetFileName(file.c_str()); + } + else + CV_Error(Error::StsError, "Unsupported format"); + + writer->SetInputConnection(source->GetOutputPort()); + writer->Write(); +} + +cv::Mat cv::viz::readCloud(const String& file, OutputArray colors, OutputArray normals) +{ + CV_Assert(file.size() > 4 && "Extension is required"); + String extension = file.substr(file.size()-4); + + vtkSmartPointer reader; + if (extension == ".xyz") + { + reader = vtkSmartPointer::New(); + vtkXYZReader::SafeDownCast(reader)->SetFileName(file.c_str()); + } + else if (extension == ".ply") + { + reader = vtkSmartPointer::New(); + CV_Assert(vtkPLYReader::CanReadFile(file.c_str())); + vtkPLYReader::SafeDownCast(reader)->SetFileName(file.c_str()); + } + else if (extension == ".obj") + { + reader = vtkSmartPointer::New(); + vtkOBJReader::SafeDownCast(reader)->SetFileName(file.c_str()); + } + else if (extension == ".stl") + { + reader = vtkSmartPointer::New(); + vtkSTLReader::SafeDownCast(reader)->SetFileName(file.c_str()); + } + else + CV_Error(Error::StsError, "Unsupported format"); + + cv::Mat cloud; + + vtkSmartPointer sink = vtkSmartPointer::New(); + sink->SetInputConnection(reader->GetOutputPort()); + sink->SetOutput(cloud, colors, normals); + sink->Write(); + + return cloud; +} + +cv::viz::Mesh cv::viz::readMesh(const String& file) { return Mesh::load(file); } + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Read/write poses and trajectories + +bool cv::viz::readPose(const String& file, Affine3d& pose, const String& tag) +{ + FileStorage fs(file, FileStorage::READ); + if (!fs.isOpened()) + return false; + + Mat hdr(pose.matrix, false); + fs[tag] >> hdr; + if (hdr.empty() || hdr.cols != pose.matrix.cols || hdr.rows != pose.matrix.rows) + return false; + + hdr.convertTo(pose.matrix, CV_64F); + return true; +} + +void cv::viz::writePose(const String& file, const Affine3d& pose, const String& tag) +{ + FileStorage fs(file, FileStorage::WRITE); + fs << tag << Mat(pose.matrix, false); +} + +void cv::viz::readTrajectory(OutputArray _traj, const String& files_format, int start, int end, const String& tag) +{ + CV_Assert(_traj.kind() == _InputArray::STD_VECTOR || _traj.kind() == _InputArray::MAT); + + start = max(0, std::min(start, end)); + end = std::max(start, end); + + std::vector traj; + + for(int i = start; i < end; ++i) + { + Affine3d affine; + bool ok = readPose(cv::format(files_format.c_str(), i), affine, tag); + if (!ok) + break; + + traj.push_back(affine); + } + + Mat(traj).convertTo(_traj, _traj.depth()); +} + +void cv::viz::writeTrajectory(InputArray _traj, const String& files_format, int start, const String& tag) +{ + if (_traj.kind() == _InputArray::STD_VECTOR_MAT) + { +#if CV_MAJOR_VERSION < 3 + std::vector& v = *(std::vector*)_traj.obj; +#else + std::vector& v = *(std::vector*)_traj.getObj(); +#endif + + for(size_t i = 0, index = max(0, start); i < v.size(); ++i, ++index) + { + Affine3d affine; + Mat pose = v[i]; + CV_Assert(pose.type() == CV_32FC(16) || pose.type() == CV_64FC(16)); + pose.copyTo(affine.matrix); + writePose(cv::format(files_format.c_str(), index), affine, tag); + } + return; + } + + if (_traj.kind() == _InputArray::STD_VECTOR || _traj.kind() == _InputArray::MAT) + { + CV_Assert(_traj.type() == CV_32FC(16) || _traj.type() == CV_64FC(16)); + + Mat traj = _traj.getMat(); + + if (traj.depth() == CV_32F) + for(size_t i = 0, index = max(0, start); i < traj.total(); ++i, ++index) + writePose(cv::format(files_format.c_str(), index), traj.at((int)i), tag); + + if (traj.depth() == CV_64F) + for(size_t i = 0, index = max(0, start); i < traj.total(); ++i, ++index) + writePose(cv::format(files_format.c_str(), index), traj.at((int)i), tag); + return; + } + + CV_Error(Error::StsError, "Unsupported array kind"); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Computing normals for mesh + +void cv::viz::computeNormals(const Mesh& mesh, OutputArray _normals) +{ + vtkSmartPointer polydata = getPolyData(WMesh(mesh)); + vtkSmartPointer with_normals = VtkUtils::ComputeNormals(polydata); + + vtkSmartPointer generic_normals = with_normals->GetPointData()->GetNormals(); + if(generic_normals) + { + Mat normals(1, generic_normals->GetNumberOfTuples(), CV_64FC3); + Vec3d *optr = normals.ptr(); + + for(int i = 0; i < generic_normals->GetNumberOfTuples(); ++i, ++optr) + generic_normals->GetTuple(i, optr->val); + + normals.convertTo(_normals, mesh.cloud.type()); + } + else + _normals.release(); +} diff --git a/modules/viz/src/vizimpl.cpp b/modules/viz/src/vizimpl.cpp new file mode 100644 index 000000000..2c291c056 --- /dev/null +++ b/modules/viz/src/vizimpl.cpp @@ -0,0 +1,622 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + + +///////////////////////////////////////////////////////////////////////////////////////////// +cv::viz::Viz3d::VizImpl::VizImpl(const String &name) : spin_once_state_(false), + window_position_(Vec2i(std::numeric_limits::min())), widget_actor_map_(new WidgetActorMap) +{ + renderer_ = vtkSmartPointer::New(); + window_name_ = VizStorage::generateWindowName(name); + + // Create render window + window_ = vtkSmartPointer::New(); + cv::Vec2i window_size = cv::Vec2i(window_->GetScreenSize()) / 2; + window_->SetSize(window_size.val); + window_->AddRenderer(renderer_); + + // Create the interactor style + style_ = vtkSmartPointer::New(); + style_->setWidgetActorMap(widget_actor_map_); + style_->UseTimersOn(); + + timer_callback_ = vtkSmartPointer::New(); + exit_callback_ = vtkSmartPointer::New(); + exit_callback_->viz = this; + + offScreenMode_ = false; + + setBackgroundMeshLab(); +} + +cv::viz::Viz3d::VizImpl::~VizImpl() { close(); } + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::TimerCallback::Execute(vtkObject* caller, unsigned long event_id, void* cookie) +{ + if (event_id == vtkCommand::TimerEvent && timer_id == *reinterpret_cast(cookie)) + { + vtkSmartPointer interactor = vtkRenderWindowInteractor::SafeDownCast(caller); + interactor->TerminateApp(); + } +} + +void cv::viz::Viz3d::VizImpl::ExitCallback::Execute(vtkObject*, unsigned long event_id, void*) +{ + if (event_id == vtkCommand::ExitEvent && viz->interactor_) + { + viz->interactor_->TerminateApp(); + viz->interactor_ = 0; + } +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +bool cv::viz::Viz3d::VizImpl::wasStopped() const +{ + bool stopped = spin_once_state_ ? interactor_ == 0 : false; + spin_once_state_ &= !stopped; + return stopped; +} + +void cv::viz::Viz3d::VizImpl::close() +{ + if (!interactor_) + return; + interactor_->GetRenderWindow()->Finalize(); + interactor_->TerminateApp(); // This tends to close the window... + interactor_ = 0; +} + +void cv::viz::Viz3d::VizImpl::recreateRenderWindow() +{ +#if !defined _MSC_VER && !defined __APPLE__ + //recreating is workaround for Ubuntu -- a crash in x-server + Vec2i window_size(window_->GetSize()); + int fullscreen = window_->GetFullScreen(); + + window_->Finalize(); + window_ = vtkSmartPointer::New(); + if (window_position_[0] != std::numeric_limits::min()) //also workaround + window_->SetPosition(window_position_.val); + + window_->SetSize(window_size.val); + window_->SetFullScreen(fullscreen); + window_->AddRenderer(renderer_); +#endif +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::spin() +{ + recreateRenderWindow(); +#if defined __APPLE__ + interactor_ = vtkCocoaRenderWindowInteractorNew(); +#else + interactor_ = vtkSmartPointer::New(); +#endif + interactor_->SetRenderWindow(window_); + interactor_->SetInteractorStyle(style_); + window_->AlphaBitPlanesOff(); + window_->PointSmoothingOff(); + window_->LineSmoothingOff(); + window_->PolygonSmoothingOff(); + window_->SwapBuffersOn(); + window_->SetStereoTypeToAnaglyph(); + window_->Render(); + window_->SetWindowName(window_name_.c_str()); + interactor_->Start(); + interactor_ = 0; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::spinOnce(int time, bool force_redraw) +{ + if (interactor_ == 0) + { + spin_once_state_ = true; + recreateRenderWindow(); +#if defined __APPLE__ + interactor_ = vtkCocoaRenderWindowInteractorNew(); +#else + interactor_ = vtkSmartPointer::New(); +#endif + interactor_->SetRenderWindow(window_); + interactor_->SetInteractorStyle(style_); + interactor_->AddObserver(vtkCommand::TimerEvent, timer_callback_); + interactor_->AddObserver(vtkCommand::ExitEvent, exit_callback_); + window_->AlphaBitPlanesOff(); + window_->PointSmoothingOff(); + window_->LineSmoothingOff(); + window_->PolygonSmoothingOff(); + window_->SwapBuffersOn(); + window_->SetStereoTypeToAnaglyph(); + window_->Render(); + window_->SetWindowName(window_name_.c_str()); + } + + vtkSmartPointer local = interactor_; + + if (force_redraw) + local->Render(); + + timer_callback_->timer_id = local->CreateRepeatingTimer(std::max(1, time)); + local->Start(); + local->DestroyTimer(timer_callback_->timer_id); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::setOffScreenRendering() +{ + window_->SetOffScreenRendering(1); + offScreenMode_ = true; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::removeAllLights() +{ + renderer_->RemoveAllLights(); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::addLight(Vec3d position, Vec3d focalPoint, const Color &color, const Color &diffuseColor, + const Color &ambientColor, const Color &specularColor) +{ + Color color_ = vtkcolor(color); + Color diffuseColor_ = vtkcolor(diffuseColor); + Color ambientColor_ = vtkcolor(ambientColor); + Color specularColor_ = vtkcolor(specularColor); + + vtkSmartPointer light = vtkSmartPointer::New(); + light->SetPosition(position.val); + light->SetFocalPoint(focalPoint.val); + light->SetColor(color_.val); + light->SetDiffuseColor(diffuseColor_.val); + light->SetAmbientColor(ambientColor_.val); + light->SetSpecularColor(specularColor_.val); + + renderer_->AddLight(light); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::showWidget(const String &id, const Widget &widget, const Affine3d &pose) +{ + WidgetActorMap::iterator wam_itr = widget_actor_map_->find(id); + bool exists = wam_itr != widget_actor_map_->end(); + if (exists) + { + // Remove it if it exists and add it again + removeActorFromRenderer(wam_itr->second); + } + // Get the actor and set the user matrix + vtkProp3D *actor = vtkProp3D::SafeDownCast(WidgetAccessor::getProp(widget)); + if (actor) + { + // If the actor is 3D, apply pose + vtkSmartPointer matrix = vtkmatrix(pose.matrix); + actor->SetUserMatrix(matrix); + actor->Modified(); + } + // If the actor is a vtkFollower, then it should always face the camera + vtkFollower *follower = vtkFollower::SafeDownCast(actor); + if (follower) + { + follower->SetCamera(renderer_->GetActiveCamera()); + } + + renderer_->AddActor(WidgetAccessor::getProp(widget)); + (*widget_actor_map_)[id] = WidgetAccessor::getProp(widget); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::removeWidget(const String &id) +{ + WidgetActorMap::iterator wam_itr = widget_actor_map_->find(id); + bool exists = wam_itr != widget_actor_map_->end(); + CV_Assert("Widget does not exist." && exists); + CV_Assert("Widget could not be removed." && removeActorFromRenderer(wam_itr->second)); + widget_actor_map_->erase(wam_itr); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +cv::viz::Widget cv::viz::Viz3d::VizImpl::getWidget(const String &id) const +{ + WidgetActorMap::const_iterator wam_itr = widget_actor_map_->find(id); + bool exists = wam_itr != widget_actor_map_->end(); + CV_Assert("Widget does not exist." && exists); + + Widget widget; + WidgetAccessor::setProp(widget, wam_itr->second); + return widget; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::setWidgetPose(const String &id, const Affine3d &pose) +{ + WidgetActorMap::iterator wam_itr = widget_actor_map_->find(id); + bool exists = wam_itr != widget_actor_map_->end(); + CV_Assert("Widget does not exist." && exists); + + vtkProp3D *actor = vtkProp3D::SafeDownCast(wam_itr->second); + CV_Assert("Widget is not 3D." && actor); + + vtkSmartPointer matrix = vtkmatrix(pose.matrix); + actor->SetUserMatrix(matrix); + actor->Modified(); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::updateWidgetPose(const String &id, const Affine3d &pose) +{ + WidgetActorMap::iterator wam_itr = widget_actor_map_->find(id); + bool exists = wam_itr != widget_actor_map_->end(); + CV_Assert("Widget does not exist." && exists); + + vtkProp3D *actor = vtkProp3D::SafeDownCast(wam_itr->second); + CV_Assert("Widget is not 3D." && actor); + + vtkSmartPointer matrix = actor->GetUserMatrix(); + if (!matrix) + { + setWidgetPose(id, pose); + return ; + } + Affine3d updated_pose = pose * Affine3d(*matrix->Element); + matrix = vtkmatrix(updated_pose.matrix); + + actor->SetUserMatrix(matrix); + actor->Modified(); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +cv::Affine3d cv::viz::Viz3d::VizImpl::getWidgetPose(const String &id) const +{ + WidgetActorMap::const_iterator wam_itr = widget_actor_map_->find(id); + bool exists = wam_itr != widget_actor_map_->end(); + CV_Assert("Widget does not exist." && exists); + + vtkProp3D *actor = vtkProp3D::SafeDownCast(wam_itr->second); + CV_Assert("Widget is not 3D." && actor); + + return Affine3d(*actor->GetUserMatrix()->Element); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::saveScreenshot(const String &file) { style_->saveScreenshot(file.c_str()); } + +///////////////////////////////////////////////////////////////////////////////////////////// +cv::Mat cv::viz::Viz3d::VizImpl::getScreenshot() const +{ + vtkSmartPointer windowToImageFilter = + vtkSmartPointer::New(); + windowToImageFilter->SetInput(window_); + windowToImageFilter->ReadFrontBufferOff(); // read from the back buffer + windowToImageFilter->Update(); + + vtkImageData *resultImage = windowToImageFilter->GetOutput(); + int * dim = resultImage->GetDimensions(); + cv::Mat image(dim[1], dim[0], CV_8UC3); + + Vec3b* dptr = reinterpret_cast(resultImage->GetScalarPointer()); + size_t elem_step = resultImage->GetIncrements()[1]/sizeof(Vec3b); + + for (int y = 0; y < image.rows; ++y) + { + const Vec3b* drow = dptr + elem_step * y; + unsigned char *srow = image.ptr(image.rows - y - 1); + for (int x = 0; x < image.cols; ++x, srow += image.channels()) + { + srow[0] = drow[x][2]; + srow[1] = drow[x][1]; + srow[2] = drow[x][0]; + } + } + + resultImage = 0; + + return image; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::registerMouseCallback(MouseCallback callback, void* cookie) +{ style_->registerMouseCallback(callback, cookie); } + +void cv::viz::Viz3d::VizImpl::registerKeyboardCallback(KeyboardCallback callback, void* cookie) +{ style_->registerKeyboardCallback(callback, cookie); } + + +////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::removeAllWidgets() +{ + widget_actor_map_->clear(); + renderer_->RemoveAllViewProps(); +} +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::showImage(InputArray image, const Size& window_size) +{ + removeAllWidgets(); + if (window_size.width > 0 && window_size.height > 0) + setWindowSize(window_size); + + showWidget("showImage", WImageOverlay(image, Rect(Point(0,0), getWindowSize()))); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +bool cv::viz::Viz3d::VizImpl::removeActorFromRenderer(vtkSmartPointer actor) +{ + vtkPropCollection* actors = renderer_->GetViewProps(); + actors->InitTraversal(); + vtkProp* current_actor = NULL; + while ((current_actor = actors->GetNextProp()) != NULL) + if (current_actor == actor) + { + renderer_->RemoveActor(actor); + return true; + } + return false; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::setBackgroundColor(const Color& color, const Color& color2) +{ + Color c = vtkcolor(color), c2 = vtkcolor(color2); + bool gradient = color2[0] >= 0 && color2[1] >= 0 && color2[2] >= 0; + + if (gradient) + { + renderer_->SetBackground(c2.val); + renderer_->SetBackground2(c.val); + renderer_->GradientBackgroundOn(); + } + else + { + renderer_->SetBackground(c.val); + renderer_->GradientBackgroundOff(); + } +} + +void cv::viz::Viz3d::VizImpl::setBackgroundMeshLab() +{ setBackgroundColor(Color(2, 1, 1), Color(240, 120, 120)); } + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::setBackgroundTexture(InputArray image) +{ + if (image.empty()) + { + renderer_->SetBackgroundTexture(0); + renderer_->TexturedBackgroundOff(); + return; + } + + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetImage(image); + + vtkSmartPointer image_flip = vtkSmartPointer::New(); + image_flip->SetFilteredAxis(1); // Vertical flip + image_flip->SetInputConnection(source->GetOutputPort()); + + vtkSmartPointer texture = vtkSmartPointer::New(); + texture->SetInputConnection(image_flip->GetOutputPort()); + //texture->Update(); + + renderer_->SetBackgroundTexture(texture); + renderer_->TexturedBackgroundOn(); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::setCamera(const Camera &camera) +{ + vtkSmartPointer active_camera = renderer_->GetActiveCamera(); + + // Set the intrinsic parameters of the camera + window_->SetSize(camera.getWindowSize().width, camera.getWindowSize().height); + double aspect_ratio = static_cast(camera.getWindowSize().width)/static_cast(camera.getWindowSize().height); + + Matx44d proj_mat; + camera.computeProjectionMatrix(proj_mat); + + // Use the intrinsic parameters of the camera to simulate more realistically + vtkSmartPointer vtk_matrix = active_camera->GetProjectionTransformMatrix(aspect_ratio, -1.0, 1.0); + Matx44d old_proj_mat(*vtk_matrix->Element); + + // This is a hack around not being able to set Projection Matrix + vtkSmartPointer transform = vtkSmartPointer::New(); + transform->SetMatrix(vtkmatrix(proj_mat * old_proj_mat.inv())); + active_camera->SetUserTransform(transform); + + renderer_->ResetCameraClippingRange(); + renderer_->Render(); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +cv::viz::Camera cv::viz::Viz3d::VizImpl::getCamera() const +{ + vtkSmartPointer active_camera = renderer_->GetActiveCamera(); + + Size window_size(renderer_->GetRenderWindow()->GetSize()[0], + renderer_->GetRenderWindow()->GetSize()[1]); + double aspect_ratio = window_size.width / (double)window_size.height; + + vtkSmartPointer proj_matrix = active_camera->GetProjectionTransformMatrix(aspect_ratio, -1.0f, 1.0f); + return Camera(Matx44d(*proj_matrix->Element), window_size); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::setViewerPose(const Affine3d &pose) +{ + vtkCamera& camera = *renderer_->GetActiveCamera(); + + // Position = extrinsic translation + cv::Vec3d pos_vec = pose.translation(); + + // Rotate the view vector + cv::Matx33d rotation = pose.rotation(); + cv::Vec3d y_axis(0.0, -1.0, 0.0); // In Computer Vision Camera Y-axis is oriented down + cv::Vec3d up_vec(rotation * y_axis); + + // Compute the new focal point + cv::Vec3d z_axis(0.0, 0.0, 1.0); + cv::Vec3d focal_vec = pose * z_axis; + + camera.SetPosition(pos_vec.val); + camera.SetFocalPoint(focal_vec.val); + camera.SetViewUp(up_vec.val); + + renderer_->ResetCameraClippingRange(); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +cv::Affine3d cv::viz::Viz3d::VizImpl::getViewerPose() const +{ + vtkCamera& camera = *renderer_->GetActiveCamera(); + + Vec3d pos(camera.GetPosition()); + Vec3d view_up(camera.GetViewUp()); + Vec3d focal(camera.GetFocalPoint()); + + Vec3d y_axis = normalized(-view_up); // In Computer Vision Camera Y-axis is oriented down + Vec3d z_axis = normalized(focal - pos); + Vec3d x_axis = normalized(y_axis.cross(z_axis)); + + return makeTransformToGlobal(x_axis, y_axis, z_axis, pos); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::convertToWindowCoordinates(const Point3d &pt, Point3d &window_coord) +{ + Vec3d window_pt; + vtkInteractorObserver::ComputeWorldToDisplay(renderer_, pt.x, pt.y, pt.z, window_pt.val); + window_coord = window_pt; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::converTo3DRay(const Point3d &window_coord, Point3d &origin, Vec3d &direction) +{ + Vec4d world_pt; + vtkInteractorObserver::ComputeDisplayToWorld(renderer_, window_coord.x, window_coord.y, window_coord.z, world_pt.val); + Vec3d cam_pos(renderer_->GetActiveCamera()->GetPosition()); + origin = cam_pos; + direction = normalize(Vec3d(world_pt.val) - cam_pos); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::resetCameraViewpoint(const String &id) +{ + vtkSmartPointer camera_pose; + static WidgetActorMap::iterator it = widget_actor_map_->find(id); + if (it != widget_actor_map_->end()) + { + vtkProp3D *actor = vtkProp3D::SafeDownCast(it->second); + CV_Assert("Widget is not 3D." && actor); + camera_pose = actor->GetUserMatrix(); + } + else + return; + + // Prevent a segfault + if (!camera_pose) return; + + vtkSmartPointer cam = renderer_->GetActiveCamera(); + cam->SetPosition(camera_pose->GetElement(0, 3), + camera_pose->GetElement(1, 3), + camera_pose->GetElement(2, 3)); + + cam->SetFocalPoint(camera_pose->GetElement(0, 3) - camera_pose->GetElement(0, 2), + camera_pose->GetElement(1, 3) - camera_pose->GetElement(1, 2), + camera_pose->GetElement(2, 3) - camera_pose->GetElement(2, 2)); + + cam->SetViewUp(camera_pose->GetElement(0, 1), + camera_pose->GetElement(1, 1), + camera_pose->GetElement(2, 1)); + + renderer_->SetActiveCamera(cam); + renderer_->ResetCameraClippingRange(); + renderer_->ResetCamera(); + renderer_->Render(); +} + +/////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::resetCamera() +{ + renderer_->ResetCamera(); +} + +/////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::setRepresentation(int representation) +{ + vtkActorCollection * actors = renderer_->GetActors(); + actors->InitTraversal(); + vtkActor * actor; + switch (representation) + { + case REPRESENTATION_POINTS: + { + while ((actor = actors->GetNextActor()) != NULL) + actor->GetProperty()->SetRepresentationToPoints(); + break; + } + case REPRESENTATION_SURFACE: + { + while ((actor = actors->GetNextActor()) != NULL) + actor->GetProperty()->SetRepresentationToSurface(); + break; + } + case REPRESENTATION_WIREFRAME: + { + while ((actor = actors->GetNextActor()) != NULL) + actor->GetProperty()->SetRepresentationToWireframe(); + break; + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +cv::String cv::viz::Viz3d::VizImpl::getWindowName() const { return window_name_; } +void cv::viz::Viz3d::VizImpl::setFullScreen(bool mode) { window_->SetFullScreen(mode); } +void cv::viz::Viz3d::VizImpl::setWindowPosition(const Point& position) { window_position_ = position; window_->SetPosition(position.x, position.y); } +void cv::viz::Viz3d::VizImpl::setWindowSize(const Size& window_size) { window_->SetSize(window_size.width, window_size.height); } +cv::Size cv::viz::Viz3d::VizImpl::getWindowSize() const { return Size(Point(Vec2i(window_->GetSize()))); } diff --git a/modules/viz/src/vizimpl.hpp b/modules/viz/src/vizimpl.hpp new file mode 100644 index 000000000..b39bc4978 --- /dev/null +++ b/modules/viz/src/vizimpl.hpp @@ -0,0 +1,146 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __OPENCV_VIZ_VIZ3D_IMPL_HPP__ +#define __OPENCV_VIZ_VIZ3D_IMPL_HPP__ + +struct cv::viz::Viz3d::VizImpl +{ +public: + typedef Viz3d::KeyboardCallback KeyboardCallback; + typedef Viz3d::MouseCallback MouseCallback; + + int ref_counter; + + VizImpl(const String &name); + virtual ~VizImpl(); + + bool wasStopped() const; + void close(); + + void spin(); + void spinOnce(int time = 1, bool force_redraw = false); + void setOffScreenRendering(); + + void removeAllLights(); + void addLight(Vec3d position, Vec3d focalPoint, const Color &color, const Color &diffuseColor, + const Color &ambientColor, const Color &specularColor); + + void showWidget(const String &id, const Widget &widget, const Affine3d &pose = Affine3d::Identity()); + void removeWidget(const String &id); + Widget getWidget(const String &id) const; + void removeAllWidgets(); + + void showImage(InputArray image, const Size& window_size); + + void setWidgetPose(const String &id, const Affine3d &pose); + void updateWidgetPose(const String &id, const Affine3d &pose); + Affine3d getWidgetPose(const String &id) const; + + void setRepresentation(int representation); + + void setCamera(const Camera &camera); + Camera getCamera() const; + + /** \brief Reset the camera to a given widget */ + void resetCameraViewpoint(const String& id); + void resetCamera(); + + void setViewerPose(const Affine3d &pose); + Affine3d getViewerPose() const; + + void convertToWindowCoordinates(const Point3d &pt, Point3d &window_coord); + void converTo3DRay(const Point3d &window_coord, Point3d &origin, Vec3d &direction); + + Mat getScreenshot() const; + void saveScreenshot(const String &file); + void setWindowPosition(const Point& position); + Size getWindowSize() const; + void setWindowSize(const Size& window_size); + void setFullScreen(bool mode); + String getWindowName() const; + void setBackgroundColor(const Color& color, const Color& color2); + void setBackgroundTexture(InputArray image); + void setBackgroundMeshLab(); + + void registerKeyboardCallback(KeyboardCallback callback, void* cookie = 0); + void registerMouseCallback(MouseCallback callback, void* cookie = 0); + +private: + struct TimerCallback : public vtkCommand + { + static TimerCallback* New() { return new TimerCallback; } + virtual void Execute(vtkObject* caller, unsigned long event_id, void* cookie); + int timer_id; + }; + + struct ExitCallback : public vtkCommand + { + static ExitCallback* New() { return new ExitCallback; } + virtual void Execute(vtkObject*, unsigned long event_id, void*); + VizImpl* viz; + }; + + mutable bool spin_once_state_; + vtkSmartPointer interactor_; + + vtkSmartPointer window_; + String window_name_; + Vec2i window_position_; + + vtkSmartPointer timer_callback_; + vtkSmartPointer exit_callback_; + + vtkSmartPointer renderer_; + vtkSmartPointer style_; + Ptr widget_actor_map_; + + bool offScreenMode_; + + bool removeActorFromRenderer(vtkSmartPointer actor); + void recreateRenderWindow(); +}; + +#endif diff --git a/modules/viz/src/vtk/vtkCloudMatSink.cpp b/modules/viz/src/vtk/vtkCloudMatSink.cpp new file mode 100644 index 000000000..aa3d34ca4 --- /dev/null +++ b/modules/viz/src/vtk/vtkCloudMatSink.cpp @@ -0,0 +1,174 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "../precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkCloudMatSink); +}} + +cv::viz::vtkCloudMatSink::vtkCloudMatSink() {} +cv::viz::vtkCloudMatSink::~vtkCloudMatSink() {} + +void cv::viz::vtkCloudMatSink::SetOutput(OutputArray _cloud, OutputArray _colors, OutputArray _normals, OutputArray _tcoords) +{ + cloud = _cloud; + colors = _colors; + normals = _normals; + tcoords = _tcoords; +} + +void cv::viz::vtkCloudMatSink::WriteData() +{ + vtkPolyData *input = this->GetInput(); + if (!input) + return; + + vtkSmartPointer points_Data = input->GetPoints(); + + if (cloud.needed() && points_Data) + { + int vtktype = points_Data->GetDataType(); + CV_Assert(vtktype == VTK_FLOAT || vtktype == VTK_DOUBLE); + + cloud.create(1, points_Data->GetNumberOfPoints(), vtktype == VTK_FLOAT ? CV_32FC3 : CV_64FC3); + Vec3d *ddata = cloud.getMat().ptr(); + Vec3f *fdata = cloud.getMat().ptr(); + + if (cloud.depth() == CV_32F) + for(size_t i = 0; i < cloud.total(); ++i) + *fdata++ = Vec3d(points_Data->GetPoint((vtkIdType)i)); + + if (cloud.depth() == CV_64F) + for(size_t i = 0; i < cloud.total(); ++i) + *ddata++ = Vec3d(points_Data->GetPoint((vtkIdType)i)); + } + else + cloud.release(); + + vtkSmartPointer scalars_data = input->GetPointData() ? input->GetPointData()->GetScalars() : 0; + + if (colors.needed() && scalars_data) + { + int channels = scalars_data->GetNumberOfComponents(); + int vtktype = scalars_data->GetDataType(); + + CV_Assert((channels == 3 || channels == 4) && "Only 3- or 4-channel color data support is implemented"); + CV_Assert(cloud.total() == (size_t)scalars_data->GetNumberOfTuples()); + + Mat buffer(cloud.size(), CV_64FC(channels)); + Vec3d *cptr = buffer.ptr(); + for(size_t i = 0; i < buffer.total(); ++i) + *cptr++ = Vec3d(scalars_data->GetTuple((vtkIdType)i)); + + buffer.convertTo(colors, CV_8U, vtktype == VTK_FLOAT || VTK_FLOAT == VTK_DOUBLE ? 255.0 : 1.0); + } + else + colors.release(); + + vtkSmartPointer normals_data = input->GetPointData() ? input->GetPointData()->GetNormals() : 0; + + if (normals.needed() && normals_data) + { + int channels = normals_data->GetNumberOfComponents(); + int vtktype = normals_data->GetDataType(); + + CV_Assert((vtktype == VTK_FLOAT || VTK_FLOAT == VTK_DOUBLE) && (channels == 3 || channels == 4)); + CV_Assert(cloud.total() == (size_t)normals_data->GetNumberOfTuples()); + + Mat buffer(cloud.size(), CV_64FC(channels)); + Vec3d *cptr = buffer.ptr(); + for(size_t i = 0; i < buffer.total(); ++i) + *cptr++ = Vec3d(normals_data->GetTuple((vtkIdType)i)); + + buffer.convertTo(normals, vtktype == VTK_FLOAT ? CV_32F : CV_64F); + } + else + normals.release(); + + vtkSmartPointer coords_data = input->GetPointData() ? input->GetPointData()->GetTCoords() : 0; + + if (tcoords.needed() && coords_data) + { + int vtktype = coords_data->GetDataType(); + + CV_Assert(vtktype == VTK_FLOAT || VTK_FLOAT == VTK_DOUBLE); + CV_Assert(cloud.total() == (size_t)coords_data->GetNumberOfTuples()); + + Mat buffer(cloud.size(), CV_64FC2); + Vec2d *cptr = buffer.ptr(); + for(size_t i = 0; i < buffer.total(); ++i) + *cptr++ = Vec2d(coords_data->GetTuple((vtkIdType)i)); + + buffer.convertTo(tcoords, vtktype == VTK_FLOAT ? CV_32F : CV_64F); + + } + else + tcoords.release(); +} + +void cv::viz::vtkCloudMatSink::PrintSelf(ostream& os, vtkIndent indent) +{ + Superclass::PrintSelf(os, indent); + os << indent << "Cloud: " << cloud.needed() << "\n"; + os << indent << "Colors: " << colors.needed() << "\n"; + os << indent << "Normals: " << normals.needed() << "\n"; +} + +int cv::viz::vtkCloudMatSink::FillInputPortInformation(int, vtkInformation *info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); + return 1; +} + +vtkPolyData* cv::viz::vtkCloudMatSink::GetInput() +{ + return vtkPolyData::SafeDownCast(this->Superclass::GetInput()); +} + +vtkPolyData* cv::viz::vtkCloudMatSink::GetInput(int port) +{ + return vtkPolyData::SafeDownCast(this->Superclass::GetInput(port)); +} diff --git a/modules/viz/src/vtk/vtkCloudMatSink.h b/modules/viz/src/vtk/vtkCloudMatSink.h new file mode 100644 index 000000000..997e6984a --- /dev/null +++ b/modules/viz/src/vtk/vtkCloudMatSink.h @@ -0,0 +1,88 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __vtkCloudMatSink_h +#define __vtkCloudMatSink_h + +#include +#include + +namespace cv +{ + namespace viz + { + class vtkCloudMatSink : public vtkWriter + { + public: + static vtkCloudMatSink *New(); + vtkTypeMacro(vtkCloudMatSink,vtkWriter) + void PrintSelf(ostream& os, vtkIndent indent); + + void SetOutput(OutputArray cloud, OutputArray colors = noArray(), OutputArray normals = noArray(), OutputArray tcoords = noArray()); + + // Description: + // Get the input to this writer. + vtkPolyData* GetInput(); + vtkPolyData* GetInput(int port); + + protected: + vtkCloudMatSink(); + ~vtkCloudMatSink(); + + void WriteData(); + int FillInputPortInformation(int port, vtkInformation *info); + + _OutputArray cloud; //!< point coordinates of type CV_32FC3 or CV_64FC3 with only 1 row + _OutputArray colors; //!< point color of type CV_8UC3 or CV_8UC4 with only 1 row + _OutputArray normals; //!< point normal of type CV_32FC3, CV_32FC4, CV_64FC3 or CV_64FC4 with only 1 row + _OutputArray tcoords; //!< texture coordinates of type CV_32FC2 or CV_64FC2 with only 1 row + + private: + vtkCloudMatSink(const vtkCloudMatSink&); // Not implemented. + void operator=(const vtkCloudMatSink&); // Not implemented. + }; + } // end namespace viz +} // end namespace cv + +#endif diff --git a/modules/viz/src/vtk/vtkCloudMatSource.cpp b/modules/viz/src/vtk/vtkCloudMatSource.cpp new file mode 100644 index 000000000..5f1cecbf2 --- /dev/null +++ b/modules/viz/src/vtk/vtkCloudMatSource.cpp @@ -0,0 +1,286 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "../precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkCloudMatSource); + + template struct VtkDepthTraits; + + template<> struct VtkDepthTraits + { + const static int data_type = VTK_FLOAT; + typedef vtkFloatArray array_type; + }; + + template<> struct VtkDepthTraits + { + const static int data_type = VTK_DOUBLE; + typedef vtkDoubleArray array_type; + }; +}} + +cv::viz::vtkCloudMatSource::vtkCloudMatSource() { SetNumberOfInputPorts(0); } +cv::viz::vtkCloudMatSource::~vtkCloudMatSource() {} + +int cv::viz::vtkCloudMatSource::SetCloud(InputArray _cloud) +{ + CV_Assert(_cloud.depth() == CV_32F || _cloud.depth() == CV_64F); + CV_Assert(_cloud.channels() == 3 || _cloud.channels() == 4); + + Mat cloud = _cloud.getMat(); + + int total = _cloud.depth() == CV_32F ? filterNanCopy(cloud) : filterNanCopy(cloud); + + vertices = vtkSmartPointer::New(); + vertices->Allocate(vertices->EstimateSize(1, total)); + vertices->InsertNextCell(total); + for(int i = 0; i < total; ++i) + vertices->InsertCellPoint(i); + + return total; +} + +int cv::viz::vtkCloudMatSource::SetColorCloud(InputArray _cloud, InputArray _colors) +{ + int total = SetCloud(_cloud); + + if (_colors.empty()) + return total; + + CV_Assert(_colors.depth() == CV_8U && _colors.channels() <= 4 && _colors.channels() != 2); + CV_Assert(_colors.size() == _cloud.size()); + + Mat cloud = _cloud.getMat(); + Mat colors = _colors.getMat(); + + if (cloud.depth() == CV_32F) + filterNanColorsCopy(colors, cloud, total); + else if (cloud.depth() == CV_64F) + filterNanColorsCopy(colors, cloud, total); + + return total; +} + +int cv::viz::vtkCloudMatSource::SetColorCloudNormals(InputArray _cloud, InputArray _colors, InputArray _normals) +{ + int total = SetColorCloud(_cloud, _colors); + + if (_normals.empty()) + return total; + + CV_Assert(_normals.depth() == CV_32F || _normals.depth() == CV_64F); + CV_Assert(_normals.channels() == 3 || _normals.channels() == 4); + CV_Assert(_normals.size() == _cloud.size()); + + Mat c = _cloud.getMat(); + Mat n = _normals.getMat(); + + if (n.depth() == CV_32F && c.depth() == CV_32F) + filterNanNormalsCopy(n, c, total); + else if (n.depth() == CV_32F && c.depth() == CV_64F) + filterNanNormalsCopy(n, c, total); + else if (n.depth() == CV_64F && c.depth() == CV_32F) + filterNanNormalsCopy(n, c, total); + else if (n.depth() == CV_64F && c.depth() == CV_64F) + filterNanNormalsCopy(n, c, total); + else + CV_Error(Error::StsError, "Unsupported normals/cloud type"); + + return total; +} + +int cv::viz::vtkCloudMatSource::SetColorCloudNormalsTCoords(InputArray _cloud, InputArray _colors, InputArray _normals, InputArray _tcoords) +{ + int total = SetColorCloudNormals(_cloud, _colors, _normals); + + if (_tcoords.empty()) + return total; + + CV_Assert(_tcoords.depth() == CV_32F || _tcoords.depth() == CV_64F); + CV_Assert(_tcoords.channels() == 2 && _tcoords.size() == _cloud.size()); + + Mat cl = _cloud.getMat(); + Mat tc = _tcoords.getMat(); + + if (tc.depth() == CV_32F && cl.depth() == CV_32F) + filterNanTCoordsCopy(tc, cl, total); + else if (tc.depth() == CV_32F && cl.depth() == CV_64F) + filterNanTCoordsCopy(tc, cl, total); + else if (tc.depth() == CV_64F && cl.depth() == CV_32F) + filterNanTCoordsCopy(tc, cl, total); + else if (tc.depth() == CV_64F && cl.depth() == CV_64F) + filterNanTCoordsCopy(tc, cl, total); + else + CV_Error(Error::StsError, "Unsupported tcoords/cloud type"); + + return total; +} + +int cv::viz::vtkCloudMatSource::RequestData(vtkInformation *vtkNotUsed(request), vtkInformationVector **vtkNotUsed(inputVector), vtkInformationVector *outputVector) +{ + vtkInformation *outInfo = outputVector->GetInformationObject(0); + vtkPolyData *output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); + + output->SetPoints(points); + output->SetVerts(vertices); + if (scalars) + output->GetPointData()->SetScalars(scalars); + + if (normals) + output->GetPointData()->SetNormals(normals); + + if (tcoords) + output->GetPointData()->SetTCoords(tcoords); + + return 1; +} + +template +int cv::viz::vtkCloudMatSource::filterNanCopy(const Mat& cloud) +{ + CV_DbgAssert(DataType<_Tp>::depth == cloud.depth()); + points = vtkSmartPointer::New(); + points->SetDataType(VtkDepthTraits<_Tp>::data_type); + points->Allocate((vtkIdType)cloud.total()); + points->SetNumberOfPoints((vtkIdType)cloud.total()); + + int s_chs = cloud.channels(); + int total = 0; + for (int y = 0; y < cloud.rows; ++y) + { + const _Tp* srow = cloud.ptr<_Tp>(y); + const _Tp* send = srow + cloud.cols * s_chs; + + for (; srow != send; srow += s_chs) + if (!isNan(srow)) + points->SetPoint(total++, srow); + } + points->SetNumberOfPoints(total); + points->Squeeze(); + return total; +} + +template +void cv::viz::vtkCloudMatSource::filterNanColorsCopy(const Mat& cloud_colors, const Mat& mask, int total) +{ + Vec3b* array = new Vec3b[total]; + Vec3b* pos = array; + + int s_chs = cloud_colors.channels(); + int m_chs = mask.channels(); + for (int y = 0; y < cloud_colors.rows; ++y) + { + const unsigned char* srow = cloud_colors.ptr(y); + const unsigned char* send = srow + cloud_colors.cols * s_chs; + const _Msk* mrow = mask.ptr<_Msk>(y); + + if (cloud_colors.channels() == 1) + { + for (; srow != send; srow += s_chs, mrow += m_chs) + if (!isNan(mrow)) + *pos++ = Vec3b(srow[0], srow[0], srow[0]); + } + else + for (; srow != send; srow += s_chs, mrow += m_chs) + if (!isNan(mrow)) + *pos++ = Vec3b(srow[2], srow[1], srow[0]); + + } + + scalars = vtkSmartPointer::New(); + scalars->SetName("Colors"); + scalars->SetNumberOfComponents(3); + scalars->SetNumberOfTuples(total); + scalars->SetArray(array->val, total * 3, 0, vtkUnsignedCharArray::VTK_DATA_ARRAY_DELETE); +} + +template +void cv::viz::vtkCloudMatSource::filterNanNormalsCopy(const Mat& cloud_normals, const Mat& mask, int total) +{ + normals = vtkSmartPointer< typename VtkDepthTraits<_Tn>::array_type >::New(); + normals->SetName("Normals"); + normals->SetNumberOfComponents(3); + normals->SetNumberOfTuples(total); + + int s_chs = cloud_normals.channels(); + int m_chs = mask.channels(); + + int pos = 0; + for (int y = 0; y < cloud_normals.rows; ++y) + { + const _Tn* srow = cloud_normals.ptr<_Tn>(y); + const _Tn* send = srow + cloud_normals.cols * s_chs; + + const _Msk* mrow = mask.ptr<_Msk>(y); + + for (; srow != send; srow += s_chs, mrow += m_chs) + if (!isNan(mrow)) + normals->SetTuple(pos++, srow); + } +} + +template +void cv::viz::vtkCloudMatSource::filterNanTCoordsCopy(const Mat& _tcoords, const Mat& mask, int total) +{ + typedef Vec<_Tn, 2> Vec2; + tcoords = vtkSmartPointer< typename VtkDepthTraits<_Tn>::array_type >::New(); + tcoords->SetName("TextureCoordinates"); + tcoords->SetNumberOfComponents(2); + tcoords->SetNumberOfTuples(total); + + int pos = 0; + for (int y = 0; y < mask.rows; ++y) + { + const Vec2* srow = _tcoords.ptr(y); + const Vec2* send = srow + _tcoords.cols; + const _Msk* mrow = mask.ptr<_Msk>(y); + + for (; srow != send; ++srow, mrow += mask.channels()) + if (!isNan(mrow)) + tcoords->SetTuple(pos++, srow->val); + } +} diff --git a/modules/viz/src/vtk/vtkCloudMatSource.h b/modules/viz/src/vtk/vtkCloudMatSource.h new file mode 100644 index 000000000..56bd93e06 --- /dev/null +++ b/modules/viz/src/vtk/vtkCloudMatSource.h @@ -0,0 +1,96 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __vtkCloudMatSource_h +#define __vtkCloudMatSource_h + +#include +#include +#include +#include +#include + +namespace cv +{ + namespace viz + { + class vtkCloudMatSource : public vtkPolyDataAlgorithm + { + public: + static vtkCloudMatSource *New(); + vtkTypeMacro(vtkCloudMatSource,vtkPolyDataAlgorithm) + + virtual int SetCloud(InputArray cloud); + virtual int SetColorCloud(InputArray cloud, InputArray colors); + virtual int SetColorCloudNormals(InputArray cloud, InputArray colors, InputArray normals); + virtual int SetColorCloudNormalsTCoords(InputArray cloud, InputArray colors, InputArray normals, InputArray tcoords); + + protected: + vtkCloudMatSource(); + ~vtkCloudMatSource(); + + int RequestData(vtkInformation *, vtkInformationVector **, vtkInformationVector *); + + vtkSmartPointer points; + vtkSmartPointer vertices; + vtkSmartPointer scalars; + vtkSmartPointer normals; + vtkSmartPointer tcoords; + private: + vtkCloudMatSource(const vtkCloudMatSource&); // Not implemented. + void operator=(const vtkCloudMatSource&); // Not implemented. + + template int filterNanCopy(const Mat& cloud); + template void filterNanColorsCopy(const Mat& cloud_colors, const Mat& mask, int total); + + template + void filterNanNormalsCopy(const Mat& cloud_normals, const Mat& mask, int total); + + template + void filterNanTCoordsCopy(const Mat& tcoords, const Mat& mask, int total); + }; + } +} + +#endif diff --git a/modules/viz/src/vtk/vtkCocoaInteractorFix.mm b/modules/viz/src/vtk/vtkCocoaInteractorFix.mm new file mode 100644 index 000000000..b15316031 --- /dev/null +++ b/modules/viz/src/vtk/vtkCocoaInteractorFix.mm @@ -0,0 +1,226 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +// This workaround code was taken from PCL library(www.pointclouds.org) +// +//M*/ + +#import +#include +#include +#include +#include + +namespace cv { namespace viz { + vtkSmartPointer vtkCocoaRenderWindowInteractorNew(); +}} // namespace + +#if ((VTK_MAJOR_VERSION < 6) || ((VTK_MAJOR_VERSION == 6) && (VTK_MINOR_VERSION < 2))) + + +//---------------------------------------------------------------------------- +@interface vtkCocoaServerFix : NSObject +{ + vtkCocoaRenderWindow* renWin; +} + ++ (id)cocoaServerWithRenderWindow:(vtkCocoaRenderWindow*)inRenderWindow; + +- (void)start; +- (void)stop; +- (void)breakEventLoop; + +@end + +//---------------------------------------------------------------------------- +@implementation vtkCocoaServerFix + +//---------------------------------------------------------------------------- +- (id)initWithRenderWindow:(vtkCocoaRenderWindow *)inRenderWindow +{ + self = [super init]; + if (self) + renWin = inRenderWindow; + return self; +} + +//---------------------------------------------------------------------------- ++ (id)cocoaServerWithRenderWindow:(vtkCocoaRenderWindow *)inRenderWindow +{ + vtkCocoaServerFix *server = [[[vtkCocoaServerFix alloc] initWithRenderWindow:inRenderWindow] autorelease]; + return server; +} + +//---------------------------------------------------------------------------- +- (void)start +{ + // Retrieve the NSWindow. + NSWindow *win = nil; + if (renWin) + { + win = reinterpret_cast (renWin->GetRootWindow ()); + + // We don't want to be informed of every window closing, so check for nil. + if (win != nil) + { + // Register for the windowWillClose notification in order to stop the run loop if the window closes. + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:win]; + } + } + // Start the NSApplication's run loop + NSApplication* application = [NSApplication sharedApplication]; + [application run]; +} + +//---------------------------------------------------------------------------- +- (void)stop +{ + [self breakEventLoop]; +} + +//---------------------------------------------------------------------------- +- (void)breakEventLoop +{ + NSApplication* application = [NSApplication sharedApplication]; + [application stop:application]; + + NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined + location:NSMakePoint(0.0,0.0) + modifierFlags:0 + timestamp:0 + windowNumber:-1 + context:nil + subtype:0 + data1:0 + data2:0]; + [application postEvent:event atStart:YES]; +} + +//---------------------------------------------------------------------------- +- (void)windowWillClose:(NSNotification*)aNotification +{ + (void)aNotification; + + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc removeObserver:self name:NSWindowWillCloseNotification object:nil]; + + if (renWin) + { + int windowCreated = renWin->GetWindowCreated (); + if (windowCreated) + { + [self breakEventLoop]; + + // The NSWindow is closing, so prevent anyone from accidentally using it + renWin->SetRootWindow(NULL); + } + } +} + +@end + +//---------------------------------------------------------------------------- + +namespace cv { namespace viz +{ + class vtkCocoaRenderWindowInteractorFix : public vtkCocoaRenderWindowInteractor + { + public: + static vtkCocoaRenderWindowInteractorFix *New (); + vtkTypeMacro (vtkCocoaRenderWindowInteractorFix, vtkCocoaRenderWindowInteractor) + + virtual void Start (); + virtual void TerminateApp (); + + protected: + vtkCocoaRenderWindowInteractorFix () {} + ~vtkCocoaRenderWindowInteractorFix () {} + + private: + vtkCocoaRenderWindowInteractorFix (const vtkCocoaRenderWindowInteractorFix&); // Not implemented. + void operator = (const vtkCocoaRenderWindowInteractorFix&); // Not implemented. + }; + + vtkStandardNewMacro (vtkCocoaRenderWindowInteractorFix) +}} + +void cv::viz::vtkCocoaRenderWindowInteractorFix::Start () +{ + vtkCocoaRenderWindow* renWin = vtkCocoaRenderWindow::SafeDownCast(this->GetRenderWindow ()); + if (renWin != NULL) + { + vtkCocoaServerFix *server = reinterpret_cast (this->GetCocoaServer ()); + if (!this->GetCocoaServer ()) + { + server = [vtkCocoaServerFix cocoaServerWithRenderWindow:renWin]; + this->SetCocoaServer (reinterpret_cast (server)); + } + + [server start]; + } +} + +void cv::viz::vtkCocoaRenderWindowInteractorFix::TerminateApp () +{ + vtkCocoaRenderWindow *renWin = vtkCocoaRenderWindow::SafeDownCast (this->RenderWindow); + if (renWin) + { + vtkCocoaServerFix *server = reinterpret_cast (this->GetCocoaServer ()); + [server stop]; + } +} + +vtkSmartPointer cv::viz::vtkCocoaRenderWindowInteractorNew() +{ + return vtkSmartPointer::New(); +} + + +#else + +vtkSmartPointer cv::viz::vtkCocoaRenderWindowInteractorNew() +{ + return vtkSmartPointer::New(); +} + +#endif diff --git a/modules/viz/src/vtk/vtkImageMatSource.cpp b/modules/viz/src/vtk/vtkImageMatSource.cpp new file mode 100644 index 000000000..d9de698df --- /dev/null +++ b/modules/viz/src/vtk/vtkImageMatSource.cpp @@ -0,0 +1,143 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "../precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkImageMatSource); +}} + +cv::viz::vtkImageMatSource::vtkImageMatSource() +{ + this->SetNumberOfInputPorts(0); + this->ImageData = vtkSmartPointer::New(); +} + +int cv::viz::vtkImageMatSource::RequestInformation(vtkInformation *, vtkInformationVector**, vtkInformationVector *outputVector) +{ + vtkInformation* outInfo = outputVector->GetInformationObject(0); + + outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), this->ImageData->GetExtent(), 6); + outInfo->Set(vtkDataObject::SPACING(), 1.0, 1.0, 1.0); + outInfo->Set(vtkDataObject::ORIGIN(), 0.0, 0.0, 0.0); + + vtkDataObject::SetPointDataActiveScalarInfo(outInfo, this->ImageData->GetScalarType(), this->ImageData->GetNumberOfScalarComponents()); + return 1; +} + +int cv::viz::vtkImageMatSource::RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector *outputVector) +{ + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + vtkImageData *output = vtkImageData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()) ); + output->ShallowCopy(this->ImageData); + return 1; +} + +void cv::viz::vtkImageMatSource::SetImage(InputArray _image) +{ + CV_Assert(_image.depth() == CV_8U && (_image.channels() == 1 || _image.channels() == 3 || _image.channels() == 4)); + + Mat image = _image.getMat(); + + this->ImageData->SetDimensions(image.cols, image.rows, 1); +#if VTK_MAJOR_VERSION <= 5 + this->ImageData->SetNumberOfScalarComponents(image.channels()); + this->ImageData->SetScalarTypeToUnsignedChar(); + this->ImageData->AllocateScalars(); +#else + this->ImageData->AllocateScalars(VTK_UNSIGNED_CHAR, image.channels()); +#endif + + switch(image.channels()) + { + case 1: copyGrayImage(image, this->ImageData); break; + case 3: copyRGBImage (image, this->ImageData); break; + case 4: copyRGBAImage(image, this->ImageData); break; + } + this->ImageData->Modified(); +} + +void cv::viz::vtkImageMatSource::copyGrayImage(const Mat &source, vtkSmartPointer output) +{ + unsigned char* dptr = reinterpret_cast(output->GetScalarPointer()); + size_t elem_step = output->GetIncrements()[1]/sizeof(unsigned char); + + for (int y = 0; y < source.rows; ++y) + { + unsigned char* drow = dptr + elem_step * y; + const unsigned char *srow = source.ptr(y); + for (int x = 0; x < source.cols; ++x) + drow[x] = *srow++; + } +} + +void cv::viz::vtkImageMatSource::copyRGBImage(const Mat &source, vtkSmartPointer output) +{ + Vec3b* dptr = reinterpret_cast(output->GetScalarPointer()); + size_t elem_step = output->GetIncrements()[1]/sizeof(Vec3b); + + for (int y = 0; y < source.rows; ++y) + { + Vec3b* drow = dptr + elem_step * y; + const unsigned char *srow = source.ptr(y); + for (int x = 0; x < source.cols; ++x, srow += source.channels()) + drow[x] = Vec3b(srow[2], srow[1], srow[0]); + } +} + +void cv::viz::vtkImageMatSource::copyRGBAImage(const Mat &source, vtkSmartPointer output) +{ + Vec4b* dptr = reinterpret_cast(output->GetScalarPointer()); + size_t elem_step = output->GetIncrements()[1]/sizeof(Vec4b); + + for (int y = 0; y < source.rows; ++y) + { + Vec4b* drow = dptr + elem_step * y; + const unsigned char *srow = source.ptr(y); + for (int x = 0; x < source.cols; ++x, srow += source.channels()) + drow[x] = Vec4b(srow[2], srow[1], srow[0], srow[3]); + } +} diff --git a/modules/viz/src/vtk/vtkImageMatSource.h b/modules/viz/src/vtk/vtkImageMatSource.h new file mode 100644 index 000000000..a7a41e082 --- /dev/null +++ b/modules/viz/src/vtk/vtkImageMatSource.h @@ -0,0 +1,82 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "../precomp.hpp" + +#ifndef __vtkImageMatSource_h +#define __vtkImageMatSource_h + +namespace cv +{ + namespace viz + { + class vtkImageMatSource : public vtkImageAlgorithm + { + public: + static vtkImageMatSource *New(); + vtkTypeMacro(vtkImageMatSource,vtkImageAlgorithm); + + void SetImage(InputArray image); + + protected: + vtkImageMatSource(); + ~vtkImageMatSource() {} + + vtkSmartPointer ImageData; + + int RequestInformation(vtkInformation*, vtkInformationVector**, vtkInformationVector*); + int RequestData (vtkInformation*, vtkInformationVector**, vtkInformationVector*); + private: + vtkImageMatSource(const vtkImageMatSource&); // Not implemented. + void operator=(const vtkImageMatSource&); // Not implemented. + + static void copyGrayImage(const Mat &source, vtkSmartPointer output); + static void copyRGBImage (const Mat &source, vtkSmartPointer output); + static void copyRGBAImage(const Mat &source, vtkSmartPointer output); + }; + } +} + + +#endif diff --git a/modules/viz/src/vtk/vtkOBJWriter.cpp b/modules/viz/src/vtk/vtkOBJWriter.cpp new file mode 100644 index 000000000..296b6eb06 --- /dev/null +++ b/modules/viz/src/vtk/vtkOBJWriter.cpp @@ -0,0 +1,270 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "../precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkOBJWriter); +}} + +cv::viz::vtkOBJWriter::vtkOBJWriter() +{ + std::ofstream fout; // only used to extract the default precision + this->DecimalPrecision = fout.precision(); + this->FileName = NULL; +} + +cv::viz::vtkOBJWriter::~vtkOBJWriter(){} + +void cv::viz::vtkOBJWriter::WriteData() +{ + vtkPolyData *input = this->GetInput(); + if (!input) + return; + + if (!this->FileName ) + { + vtkErrorMacro(<< "No FileName specified! Can't write!"); + this->SetErrorCode(vtkErrorCode::NoFileNameError); + return; + } + + vtkDebugMacro(<<"Opening vtk file for writing..."); + ostream *outfilep = new ofstream(this->FileName, ios::out); + if (outfilep->fail()) + { + vtkErrorMacro(<< "Unable to open file: "<< this->FileName); + this->SetErrorCode(vtkErrorCode::CannotOpenFileError); + delete outfilep; + return; + } + + std::ostream& outfile = *outfilep; + + //write header + outfile << "# wavefront obj file written by opencv viz module" << std::endl << std::endl; + outfile << "mtllib NONE" << std::endl << std::endl; + + // write out the points + for (int i = 0; i < input->GetNumberOfPoints(); i++) + { + Vec3d p; + input->GetPoint(i, p.val); + outfile << std::setprecision(this->DecimalPrecision) << "v " << p[0] << " " << p[1] << " " << p[2] << std::endl; + } + + const int idStart = 1; + + // write out the point data + vtkSmartPointer normals = input->GetPointData()->GetNormals(); + if(normals) + { + for (int i = 0; i < normals->GetNumberOfTuples(); i++) + { + Vec3d p; + normals->GetTuple(i, p.val); + outfile << std::setprecision(this->DecimalPrecision) << "vn " << p[0] << " " << p[1] << " " << p[2] << std::endl; + } + } + + vtkSmartPointer tcoords = input->GetPointData()->GetTCoords(); + if (tcoords) + { + for (int i = 0; i < tcoords->GetNumberOfTuples(); i++) + { + Vec2d p; + tcoords->GetTuple(i, p.val); + outfile << std::setprecision(this->DecimalPrecision) << "vt " << p[0] << " " << p[1] << std::endl; + } + } + + // write out a group name and material + outfile << std::endl << "g grp" << idStart << std::endl; + outfile << "usemtl mtlNONE" << std::endl; + + // write out verts if any + if (input->GetNumberOfVerts() > 0) + { + vtkIdType npts = 0, *index = 0; + vtkCellArray *cells = input->GetVerts(); + for (cells->InitTraversal(); cells->GetNextCell(npts, index); ) + { + outfile << "p "; + for (int i = 0; i < npts; i++) + outfile << index[i] + idStart << " "; + outfile << std::endl; + } + } + + // write out lines if any + if (input->GetNumberOfLines() > 0) + { + vtkIdType npts = 0, *index = 0; + vtkCellArray *cells = input->GetLines(); + for (cells->InitTraversal(); cells->GetNextCell(npts, index); ) + { + outfile << "l "; + if (tcoords) + { + for (int i = 0; i < npts; i++) + outfile << index[i] + idStart << "/" << index[i] + idStart << " "; + } + else + for (int i = 0; i < npts; i++) + outfile << index[i] + idStart << " "; + + outfile << std::endl; + } + } + + // write out polys if any + if (input->GetNumberOfPolys() > 0) + { + vtkIdType npts = 0, *index = 0; + vtkCellArray *cells = input->GetPolys(); + for (cells->InitTraversal(); cells->GetNextCell(npts, index); ) + { + outfile << "f "; + for (int i = 0; i < npts; i++) + { + if (normals) + { + if (tcoords) + outfile << index[i] + idStart << "/" << index[i] + idStart << "/" << index[i] + idStart << " "; + else + outfile << index[i] + idStart << "//" << index[i] + idStart << " "; + } + else + { + if (tcoords) + outfile << index[i] + idStart << " " << index[i] + idStart << " "; + else + outfile << index[i] + idStart << " "; + } + } + outfile << std::endl; + } + } + + // write out tstrips if any + if (input->GetNumberOfStrips() > 0) + { + vtkIdType npts = 0, *index = 0; + vtkCellArray *cells = input->GetStrips(); + for (cells->InitTraversal(); cells->GetNextCell(npts, index); ) + { + for (int i = 2, i1, i2; i < npts; ++i) + { + if (i % 2) + { + i1 = i - 1; + i2 = i - 2; + } + else + { + i1 = i - 1; + i2 = i - 2; + } + + if(normals) + { + if (tcoords) + { + outfile << "f " << index[i1] + idStart << "/" << index[i1] + idStart << "/" << index[i1] + idStart << " " + << index[i2]+ idStart << "/" << index[i2] + idStart << "/" << index[i2] + idStart << " " + << index[i] + idStart << "/" << index[i] + idStart << "/" << index[i] + idStart << std::endl; + } + else + { + outfile << "f " << index[i1] + idStart << "//" << index[i1] + idStart << " " << index[i2] + idStart + << "//" << index[i2] + idStart << " " << index[i] + idStart << "//" << index[i] + idStart << std::endl; + } + } + else + { + if (tcoords) + { + outfile << "f " << index[i1] + idStart << "/" << index[i1] + idStart << " " << index[i2] + idStart + << "/" << index[i2] + idStart << " " << index[i] + idStart << "/" << index[i] + idStart << std::endl; + } + else + outfile << "f " << index[i1] + idStart << " " << index[i2] + idStart << " " << index[i] + idStart << std::endl; + } + } /* for (int i = 2; i < npts; ++i) */ + } + } /* if (input->GetNumberOfStrips() > 0) */ + + vtkDebugMacro(<<"Closing vtk file\n"); + delete outfilep; + + // Delete the file if an error occurred + if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError) + { + vtkErrorMacro("Ran out of disk space; deleting file: " << this->FileName); + unlink(this->FileName); + } +} + +void cv::viz::vtkOBJWriter::PrintSelf(ostream& os, vtkIndent indent) +{ + Superclass::PrintSelf(os, indent); + os << indent << "DecimalPrecision: " << DecimalPrecision << "\n"; +} + +int cv::viz::vtkOBJWriter::FillInputPortInformation(int, vtkInformation *info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); + return 1; +} + +vtkPolyData* cv::viz::vtkOBJWriter::GetInput() +{ + return vtkPolyData::SafeDownCast(this->Superclass::GetInput()); +} + +vtkPolyData* cv::viz::vtkOBJWriter::GetInput(int port) +{ + return vtkPolyData::SafeDownCast(this->Superclass::GetInput(port)); +} diff --git a/modules/viz/src/vtk/vtkOBJWriter.h b/modules/viz/src/vtk/vtkOBJWriter.h new file mode 100644 index 000000000..7ad0f17b1 --- /dev/null +++ b/modules/viz/src/vtk/vtkOBJWriter.h @@ -0,0 +1,91 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __vtkOBJWriter_h +#define __vtkOBJWriter_h + +#include + +namespace cv +{ + namespace viz + { + class vtkOBJWriter : public vtkWriter + { + public: + static vtkOBJWriter *New(); + vtkTypeMacro(vtkOBJWriter,vtkWriter) + void PrintSelf(ostream& os, vtkIndent indent); + + vtkGetMacro(DecimalPrecision, int) + vtkSetMacro(DecimalPrecision, int) + + // Description: + // Specify file name of data file to write. + vtkSetStringMacro(FileName) + vtkGetStringMacro(FileName) + + // Description: + // Get the input to this writer. + vtkPolyData* GetInput(); + vtkPolyData* GetInput(int port); + + protected: + vtkOBJWriter(); + ~vtkOBJWriter(); + + void WriteData(); + int FillInputPortInformation(int port, vtkInformation *info); + + int DecimalPrecision; + char *FileName; + + private: + vtkOBJWriter(const vtkOBJWriter&); // Not implemented. + void operator=(const vtkOBJWriter&); // Not implemented. + }; + } +} + +#endif diff --git a/modules/viz/src/vtk/vtkTrajectorySource.cpp b/modules/viz/src/vtk/vtkTrajectorySource.cpp new file mode 100644 index 000000000..d0e180a9c --- /dev/null +++ b/modules/viz/src/vtk/vtkTrajectorySource.cpp @@ -0,0 +1,110 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "../precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkTrajectorySource); +}} + +cv::viz::vtkTrajectorySource::vtkTrajectorySource() { SetNumberOfInputPorts(0); } +cv::viz::vtkTrajectorySource::~vtkTrajectorySource() {} + +void cv::viz::vtkTrajectorySource::SetTrajectory(InputArray _traj) +{ + CV_Assert(_traj.kind() == _InputArray::STD_VECTOR || _traj.kind() == _InputArray::MAT); + CV_Assert(_traj.type() == CV_32FC(16) || _traj.type() == CV_64FC(16)); + + Mat traj; + _traj.getMat().convertTo(traj, CV_64F); + const Affine3d* dpath = traj.ptr(); + size_t total = traj.total(); + + points = vtkSmartPointer::New(); + points->SetDataType(VTK_DOUBLE); + points->SetNumberOfPoints((vtkIdType)total); + + tensors = vtkSmartPointer::New(); + tensors->SetNumberOfComponents(9); + tensors->SetNumberOfTuples((vtkIdType)total); + + for(size_t i = 0; i < total; ++i, ++dpath) + { + Matx33d R = dpath->rotation().t(); // transposed because of + tensors->SetTuple((vtkIdType)i, R.val); // column major order + + Vec3d p = dpath->translation(); + points->SetPoint((vtkIdType)i, p.val); + } +} + +cv::Mat cv::viz::vtkTrajectorySource::ExtractPoints(InputArray _traj) +{ + CV_Assert(_traj.kind() == _InputArray::STD_VECTOR || _traj.kind() == _InputArray::MAT); + CV_Assert(_traj.type() == CV_32FC(16) || _traj.type() == CV_64FC(16)); + + Mat points(1, (int)_traj.total(), CV_MAKETYPE(_traj.depth(), 3)); + const Affine3d* dpath = _traj.getMat().ptr(); + const Affine3f* fpath = _traj.getMat().ptr(); + + if (_traj.depth() == CV_32F) + for(int i = 0; i < points.cols; ++i) + points.at(i) = fpath[i].translation(); + + if (_traj.depth() == CV_64F) + for(int i = 0; i < points.cols; ++i) + points.at(i) = dpath[i].translation(); + + return points; +} + +int cv::viz::vtkTrajectorySource::RequestData(vtkInformation *vtkNotUsed(request), vtkInformationVector **vtkNotUsed(inputVector), vtkInformationVector *outputVector) +{ + vtkInformation *outInfo = outputVector->GetInformationObject(0); + vtkPolyData *output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); + output->SetPoints(points); + output->GetPointData()->SetTensors(tensors); + return 1; +} diff --git a/modules/viz/src/vtk/vtkTrajectorySource.h b/modules/viz/src/vtk/vtkTrajectorySource.h new file mode 100644 index 000000000..f6c9c77b9 --- /dev/null +++ b/modules/viz/src/vtk/vtkTrajectorySource.h @@ -0,0 +1,84 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __vtkTrajectorySource_h +#define __vtkTrajectorySource_h + +#include +#include +#include +#include +#include + +namespace cv +{ + namespace viz + { + class vtkTrajectorySource : public vtkPolyDataAlgorithm + { + public: + static vtkTrajectorySource *New(); + vtkTypeMacro(vtkTrajectorySource,vtkPolyDataAlgorithm) + + virtual void SetTrajectory(InputArray trajectory); + + static Mat ExtractPoints(InputArray trajectory); + + protected: + vtkTrajectorySource(); + ~vtkTrajectorySource(); + + vtkSmartPointer points; + vtkSmartPointer tensors; + + int RequestData(vtkInformation *, vtkInformationVector **, vtkInformationVector *); + private: + vtkTrajectorySource(const vtkTrajectorySource&); // Not implemented. + void operator=(const vtkTrajectorySource&); // Not implemented. + + }; + } +} + +#endif diff --git a/modules/viz/src/vtk/vtkVizInteractorStyle.cpp b/modules/viz/src/vtk/vtkVizInteractorStyle.cpp new file mode 100644 index 000000000..e2d338066 --- /dev/null +++ b/modules/viz/src/vtk/vtkVizInteractorStyle.cpp @@ -0,0 +1,1076 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "../precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkVizInteractorStyle) +}} + +////////////////////////////////////////////////////////////////////////////////////////////// + +cv::viz::vtkVizInteractorStyle::vtkVizInteractorStyle() +{ + FlyMode = false; + MotionFactor = 10.0; + + keyboardCallback_ = 0; + keyboard_callback_cookie_ = 0; + + mouseCallback_ = 0; + mouse_callback_cookie_ = 0; + + // Set windows size (width, height) to unknown (-1) + win_size_ = Vec2i(-1, -1); + win_pos_ = Vec2i(0, 0); + max_win_size_ = Vec2i(-1, -1); + + stereo_anaglyph_redblue_ = true; + + //from fly + KeysDown = 0; + UseTimers = 1; + + DiagonalLength = 1.0; + MotionStepSize = 1.0/100.0; + MotionUserScale = 1.0; // +/- key adjustment + MotionAccelerationFactor = 10.0; + AngleStepSize = 1.0; +} + +cv::viz::vtkVizInteractorStyle::~vtkVizInteractorStyle() {} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::saveScreenshot(const String &file) +{ + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + + vtkSmartPointer wif = vtkSmartPointer::New(); + wif->SetInput(Interactor->GetRenderWindow()); + + vtkSmartPointer snapshot_writer = vtkSmartPointer::New(); + snapshot_writer->SetInputConnection(wif->GetOutputPort()); + snapshot_writer->SetFileName(file.c_str()); + snapshot_writer->Write(); + + cout << "Screenshot successfully captured (" << file.c_str() << ")" << endl; +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::exportScene(const String &file) +{ + vtkSmartPointer exporter; + if (file.size() > 5 && file.substr(file.size() - 5) == ".vrml") + { + exporter = vtkSmartPointer::New(); + vtkVRMLExporter::SafeDownCast(exporter)->SetFileName(file.c_str()); + } + else + { + exporter = vtkSmartPointer::New(); + vtkOBJExporter::SafeDownCast(exporter)->SetFilePrefix(file.c_str()); + } + + exporter->SetInput(Interactor->GetRenderWindow()); + exporter->Write(); + + cout << "Scene successfully exported (" << file.c_str() << ")" << endl; +} + +void cv::viz::vtkVizInteractorStyle::exportScene() +{ + // Export scene as in obj or vrml format + String format = Interactor->GetAltKey() ? "scene-%d.vrml" : "scene-%d"; + exportScene(cv::format(format.c_str(), (unsigned int)time(0))); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::changePointsSize(float delta) +{ + vtkSmartPointer ac = CurrentRenderer->GetActors(); + vtkCollectionSimpleIterator ait; + + for (ac->InitTraversal(ait); vtkActor* actor = ac->GetNextActor(ait); ) + for (actor->InitPathTraversal(); vtkAssemblyPath* path = actor->GetNextPath(); ) + { + vtkActor* apart = vtkActor::SafeDownCast(path->GetLastNode()->GetViewProp()); + float psize = apart->GetProperty()->GetPointSize() + delta; + psize = std::max(1.f, std::min(63.f, psize)); + apart->GetProperty()->SetPointSize(psize); + } +} + +void cv::viz::vtkVizInteractorStyle::setRepresentationToPoints() +{ + vtkSmartPointer ac = CurrentRenderer->GetActors(); + vtkCollectionSimpleIterator ait; + for (ac->InitTraversal(ait); vtkActor* actor = ac->GetNextActor(ait); ) + for (actor->InitPathTraversal(); vtkAssemblyPath* path = actor->GetNextPath(); ) + { + vtkActor* apart = vtkActor::SafeDownCast(path->GetLastNode()->GetViewProp()); + apart->GetProperty()->SetRepresentationToPoints(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::printCameraParams() +{ + vtkSmartPointer cam = Interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->GetActiveCamera(); + + Vec2d clip(cam->GetClippingRange()); + Vec3d focal(cam->GetFocalPoint()), pos(cam->GetPosition()), view(cam->GetViewUp()); + Vec2i win_pos(Interactor->GetRenderWindow()->GetPosition()); + Vec2i win_size(Interactor->GetRenderWindow()->GetSize()); + double angle = cam->GetViewAngle () / 180.0 * CV_PI; + + String data = cv::format("clip(%f,%f) focal(%f,%f,%f) pos(%f,%f,%f) view(%f,%f,%f) angle(%f) winsz(%d,%d) winpos(%d,%d)", + clip[0], clip[1], focal[0], focal[1], focal[2], pos[0], pos[1], pos[2], view[0], view[1], view[2], + angle, win_size[0], win_size[1], win_pos[0], win_pos[1]); + + std::cout << data.c_str() << std::endl; +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::toggleFullScreen() +{ + Vec2i screen_size(Interactor->GetRenderWindow()->GetScreenSize()); + Vec2i win_size(Interactor->GetRenderWindow()->GetSize()); + + // Is window size = max? + if (win_size == max_win_size_) + { + Interactor->GetRenderWindow()->SetSize(win_size_.val); + Interactor->GetRenderWindow()->SetPosition(win_pos_.val); + Interactor->Render(); + } + // Set to max + else + { + win_pos_ = Vec2i(Interactor->GetRenderWindow()->GetPosition()); + win_size_ = win_size; + + Interactor->GetRenderWindow()->SetSize(screen_size.val); + Interactor->Render(); + max_win_size_ = Vec2i(Interactor->GetRenderWindow()->GetSize()); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::resetViewerPose() +{ + WidgetActorMap::iterator it = widget_actor_map_->begin(); + // it might be that some actors don't have a valid transformation set -> we skip them to avoid a seg fault. + for (; it != widget_actor_map_->end(); ++it) + { + vtkProp3D * actor = vtkProp3D::SafeDownCast(it->second); + if (actor && actor->GetUserMatrix()) + break; + } + + vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); + + // if a valid transformation was found, use it otherwise fall back to default view point. + if (it != widget_actor_map_->end()) + { + vtkMatrix4x4* m = vtkProp3D::SafeDownCast(it->second)->GetUserMatrix(); + + cam->SetFocalPoint(m->GetElement(0, 3) - m->GetElement(0, 2), + m->GetElement(1, 3) - m->GetElement(1, 2), + m->GetElement(2, 3) - m->GetElement(2, 2)); + + cam->SetViewUp (m->GetElement(0, 1), m->GetElement(1, 1), m->GetElement(2, 1)); + cam->SetPosition(m->GetElement(0, 3), m->GetElement(1, 3), m->GetElement(2, 3)); + } + else + { + cam->SetPosition(0, 0, 0); + cam->SetFocalPoint(0, 0, 1); + cam->SetViewUp(0, -1, 0); + } + + // go to the next actor for the next key-press event. + if (it != widget_actor_map_->end()) + ++it; + else + it = widget_actor_map_->begin(); + + CurrentRenderer->SetActiveCamera(cam); + CurrentRenderer->ResetCameraClippingRange(); + Interactor->Render(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::toggleStereo() +{ + vtkSmartPointer window = Interactor->GetRenderWindow(); + if (!window->GetStereoRender()) + { + static Vec2i red_blue(4, 3), magenta_green(2, 5); + window->SetAnaglyphColorMask (stereo_anaglyph_redblue_ ? red_blue.val : magenta_green.val); + stereo_anaglyph_redblue_ = !stereo_anaglyph_redblue_; + } + window->SetStereoRender(!window->GetStereoRender()); + Interactor->Render(); + +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::printHelp() +{ + std::cout << "| Help:\n" + "-------\n" + " p, P : switch to a point-based representation\n" + " w, W : switch to a wireframe-based representation (where available)\n" + " s, S : switch to a surface-based representation (where available)\n" + "\n" + " j, J : take a .PNG snapshot of the current window view\n" + " k, K : export scene to Wavefront .obj format\n" + " ALT + k, K : export scene to VRML format\n" + " c, C : display current camera/window parameters\n" + " F5 : enable/disable fly mode (changes control style)\n" + "\n" + " e, E : exit the interactor\n" + " q, Q : stop and call VTK's TerminateApp\n" + "\n" + " +/- : increment/decrement overall point size\n" + " +/- [+ ALT] : zoom in/out \n" + "\n" + " r, R [+ ALT] : reset camera [to viewpoint = {0, 0, 0} -> center_{x, y, z}]\n" + "\n" + " ALT + s, S : turn stereo mode on/off\n" + " ALT + f, F : switch between maximized window mode and original size\n" + "\n" + << std::endl; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::zoomIn() +{ + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + // Zoom in + StartDolly(); + double factor = 10.0 * 0.2 * .5; + Dolly(std::pow(1.1, factor)); + EndDolly(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::zoomOut() +{ + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + // Zoom out + StartDolly(); + double factor = 10.0 * -0.2 * .5; + Dolly(std::pow(1.1, factor)); + EndDolly(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnChar() +{ + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + + String key(Interactor->GetKeySym()); + if (key.find("XF86ZoomIn") != String::npos) + zoomIn(); + else if (key.find("XF86ZoomOut") != String::npos) + zoomOut(); + + switch (Interactor->GetKeyCode()) + { +// // All of the options below simply exit +// case 'l': case 'L': case 'j': case 'J': case 'c': case 'C': case 'q': case 'Q': +// case 'f': case 'F': case 'g': case 'G': case 'o': case 'O': case 'u': case 'U': + case 'p': case 'P': + break; + + case '+': + if (FlyMode) + MotionUserScale = std::min(16.0, MotionUserScale*2.0); + break; + case '-': + if (FlyMode) + MotionUserScale = std::max(MotionUserScale * 0.5, 0.0625); + break; + + case 'r': case 'R': case 's': case 'S': + if (!Interactor->GetAltKey()) + Superclass::OnChar(); + break; + default: + Superclass::OnChar(); + break; + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::registerMouseCallback(void (*callback)(const MouseEvent&, void*), void* cookie) +{ + mouseCallback_ = callback; + mouse_callback_cookie_ = cookie; +} + +void cv::viz::vtkVizInteractorStyle::registerKeyboardCallback(void (*callback)(const KeyboardEvent&, void*), void *cookie) +{ + keyboardCallback_ = callback; + keyboard_callback_cookie_ = cookie; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +int cv::viz::vtkVizInteractorStyle::getModifiers() +{ + int modifiers = KeyboardEvent::NONE; + + if (Interactor->GetAltKey()) + modifiers |= KeyboardEvent::ALT; + + if (Interactor->GetControlKey()) + modifiers |= KeyboardEvent::CTRL; + + if (Interactor->GetShiftKey()) + modifiers |= KeyboardEvent::SHIFT; + return modifiers; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnKeyDown() +{ + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + + String key(Interactor->GetKeySym()); + if (key.find("XF86ZoomIn") != String::npos) + zoomIn(); + else if (key.find("XF86ZoomOut") != String::npos) + zoomOut(); + else if (key.find("F5") != String::npos) + { + FlyMode = !FlyMode; + std::cout << (FlyMode ? "Fly mode: on" : "Fly mode: off") << std::endl; + } + + // Save the initial windows width/height + if (win_size_[0] == -1 || win_size_[1] == -1) + win_size_ = Vec2i(Interactor->GetRenderWindow()->GetSize()); + + switch (Interactor->GetKeyCode()) + { + case 'a': case 'A' : KeysDown |=16; break; + case 'z': case 'Z' : KeysDown |=32; break; + case 'h': case 'H' : printHelp(); break; + case 'p': case 'P' : setRepresentationToPoints(); break; + case 'k': case 'K' : exportScene(); break; + case 'j': case 'J' : saveScreenshot(cv::format("screenshot-%d.png", (unsigned int)time(0))); break; + case 'c': case 'C' : printCameraParams(); break; + case '=': zoomIn(); break; + case 43: // KEY_PLUS + { + if (FlyMode) + break; + if (Interactor->GetAltKey()) + zoomIn(); + else + changePointsSize(+1.f); + break; + } + case 45: // KEY_MINUS + { + if (FlyMode) + break; + if (Interactor->GetAltKey()) + zoomOut(); + else + changePointsSize(-1.f); + break; + } + // Switch between maximize and original window size + case 'f': case 'F': + { + if (Interactor->GetAltKey()) + toggleFullScreen(); + break; + } + // 's'/'S' w/out ALT + case 's': case 'S': + { + if (Interactor->GetAltKey()) + toggleStereo(); + break; + } + + case 'o': case 'O': + { + vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); + cam->SetParallelProjection(!cam->GetParallelProjection()); + Interactor->Render(); + break; + } + + // Overwrite the camera reset + case 'r': case 'R': + { + if (Interactor->GetAltKey()) + resetViewerPose(); + break; + } + case 'q': case 'Q': + Interactor->ExitCallback(); return; + default: + Superclass::OnKeyDown(); break; + } + + KeyboardEvent event(KeyboardEvent::KEY_DOWN, Interactor->GetKeySym(), Interactor->GetKeyCode(), getModifiers()); + if (keyboardCallback_) + keyboardCallback_(event, keyboard_callback_cookie_); + + if (FlyMode && (KeysDown & (32+16)) == (32+16)) + { + if (State == VTKIS_FORWARDFLY || State == VTKIS_REVERSEFLY) + StopState(); + } + else if (FlyMode && (KeysDown & 32) == 32) + { + if (State == VTKIS_FORWARDFLY) + StopState(); + + if (State == VTKIS_NONE) + StartState(VTKIS_REVERSEFLY); + } + else if (FlyMode && (KeysDown & 16) == 16) + { + if (State == VTKIS_REVERSEFLY) + StopState(); + + if (State == VTKIS_NONE) + StartState(VTKIS_FORWARDFLY); + } + + Interactor->Render(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnKeyUp() +{ + KeyboardEvent event(KeyboardEvent::KEY_UP, Interactor->GetKeySym(), Interactor->GetKeyCode(), getModifiers()); + if (keyboardCallback_) + keyboardCallback_(event, keyboard_callback_cookie_); + + switch (Interactor->GetKeyCode()) + { + case 'a': case 'A' : KeysDown &= ~16; break; + case 'z': case 'Z' : KeysDown &= ~32; break; + } + + if (State == VTKIS_FORWARDFLY && (KeysDown & 16) == 0) + StopState(); + + if (State == VTKIS_REVERSEFLY && (KeysDown & 32) == 0) + StopState(); + + Superclass::OnKeyUp(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnMouseMove() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseMove, MouseEvent::NoButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + FindPokedRenderer(p[0], p[1]); + + if (State == VTKIS_ROTATE || State == VTKIS_PAN || State == VTKIS_DOLLY || State == VTKIS_SPIN) + { + switch (State) + { + case VTKIS_ROTATE: Rotate(); break; + case VTKIS_PAN: Pan(); break; + case VTKIS_DOLLY: Dolly(); break; + case VTKIS_SPIN: Spin(); break; + } + + InvokeEvent(vtkCommand::InteractionEvent, NULL); + } + + if (State == VTKIS_FORWARDFLY || State == VTKIS_REVERSEFLY) + { + vtkCamera *cam = CurrentRenderer->GetActiveCamera(); + Vec2i thispos(Interactor->GetEventPosition()); + Vec2i lastpos(Interactor->GetLastEventPosition()); + + // we want to steer by an amount proportional to window viewangle and size + // compute dx and dy increments relative to last mouse click + Vec2i size(Interactor->GetSize()); + double scalefactor = 5*cam->GetViewAngle()/size[0]; + + double dx = - (thispos[0] - lastpos[0])*scalefactor*AngleStepSize; + double dy = (thispos[1] - lastpos[1])*scalefactor*AngleStepSize; + + // Temporary until I get smooth flight working + DeltaPitch = dy; + DeltaYaw = dx; + + InvokeEvent(vtkCommand::InteractionEvent, NULL); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnLeftButtonDown() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent::Type type = (Interactor->GetRepeatCount() == 0) ? MouseEvent::MouseButtonPress : MouseEvent::MouseDblClick; + MouseEvent event(type, MouseEvent::LeftButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + FindPokedRenderer(p[0], p[1]); + if (!CurrentRenderer) + return; + + GrabFocus(EventCallbackCommand); + + if (FlyMode) + { + if(State == VTKIS_REVERSEFLY) + State = VTKIS_FORWARDFLY; + else + { + SetupMotionVars(); + if (State == VTKIS_NONE) + StartState(VTKIS_FORWARDFLY); + } + } + else + { + if (Interactor->GetShiftKey()) + { + if (Interactor->GetControlKey()) + StartDolly(); + else + StartPan(); + } + else + { + if (Interactor->GetControlKey()) + StartSpin(); + else + StartRotate(); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnLeftButtonUp() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseButtonRelease, MouseEvent::LeftButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + switch (State) + { + case VTKIS_DOLLY: EndDolly(); break; + case VTKIS_PAN: EndPan(); break; + case VTKIS_SPIN: EndSpin(); break; + case VTKIS_ROTATE: EndRotate(); break; + case VTKIS_FORWARDFLY: StopState(); break; + } + + if (Interactor ) + ReleaseFocus(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnMiddleButtonDown() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent::Type type = (Interactor->GetRepeatCount() == 0) ? MouseEvent::MouseButtonPress : MouseEvent::MouseDblClick; + MouseEvent event(type, MouseEvent::MiddleButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + FindPokedRenderer(p[0], p[1]); + if (!CurrentRenderer) + return; + + GrabFocus(EventCallbackCommand); + StartPan(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnMiddleButtonUp() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseButtonRelease, MouseEvent::MiddleButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + if (State == VTKIS_PAN) + { + EndPan(); + if (Interactor) + ReleaseFocus(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnRightButtonDown() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent::Type type = (Interactor->GetRepeatCount() == 0) ? MouseEvent::MouseButtonPress : MouseEvent::MouseDblClick; + MouseEvent event(type, MouseEvent::RightButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + FindPokedRenderer(p[0], p[1]); + if (!CurrentRenderer) + return; + + GrabFocus(EventCallbackCommand); + + if (FlyMode) + { + if (State == VTKIS_FORWARDFLY) + State = VTKIS_REVERSEFLY; + else + { + SetupMotionVars(); + if (State == VTKIS_NONE) + StartState(VTKIS_REVERSEFLY); + } + + } + else + StartDolly(); +} + + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnRightButtonUp() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseButtonRelease, MouseEvent::RightButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + if(State == VTKIS_DOLLY) + { + EndDolly(); + if (Interactor) + ReleaseFocus(); + } + + if (State == VTKIS_REVERSEFLY) + { + StopState(); + if (Interactor) + ReleaseFocus(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnMouseWheelForward() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseScrollUp, MouseEvent::VScroll, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + if (Interactor->GetRepeatCount() && mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + if (Interactor->GetAltKey()) + { + // zoom + vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); + double opening_angle = cam->GetViewAngle(); + if (opening_angle > 15.0) + opening_angle -= 1.0; + + cam->SetViewAngle(opening_angle); + cam->Modified(); + CurrentRenderer->ResetCameraClippingRange(); + CurrentRenderer->Modified(); + Interactor->Render(); + } + else + { + FindPokedRenderer(p[0], p[1]); + if (!CurrentRenderer) + return; + + GrabFocus(EventCallbackCommand); + StartDolly(); + Dolly(pow(1.1, MotionFactor * 0.2 * MouseWheelMotionFactor)); + EndDolly(); + ReleaseFocus(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnMouseWheelBackward() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseScrollDown, MouseEvent::VScroll, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + if (Interactor->GetRepeatCount() && mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + if (Interactor->GetAltKey()) + { + // zoom + vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); + double opening_angle = cam->GetViewAngle(); + if (opening_angle < 170.0) + opening_angle += 1.0; + + cam->SetViewAngle(opening_angle); + cam->Modified(); + CurrentRenderer->ResetCameraClippingRange(); + CurrentRenderer->Modified(); + Interactor->Render(); + } + else + { + FindPokedRenderer(p[0], p[1]); + if (!CurrentRenderer) + return; + + GrabFocus(EventCallbackCommand); + StartDolly(); + Dolly(pow(1.1, MotionFactor * -0.2 * MouseWheelMotionFactor)); + EndDolly(); + ReleaseFocus(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnTimer() +{ + if (State == VTKIS_FORWARDFLY || State == VTKIS_REVERSEFLY) + Fly(); + + Interactor->Render(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::Rotate() +{ + if (!CurrentRenderer) + return; + + Vec2i dxy = Vec2i(Interactor->GetEventPosition()) - Vec2i(Interactor->GetLastEventPosition()); + Vec2i size(CurrentRenderer->GetRenderWindow()->GetSize()); + + double delta_elevation = -20.0 / size[1]; + double delta_azimuth = -20.0 / size[0]; + + double rxf = dxy[0] * delta_azimuth * MotionFactor; + double ryf = dxy[1] * delta_elevation * MotionFactor; + + vtkCamera *camera = CurrentRenderer->GetActiveCamera(); + camera->Azimuth(rxf); + camera->Elevation(ryf); + camera->OrthogonalizeViewUp(); + + if (AutoAdjustCameraClippingRange) + CurrentRenderer->ResetCameraClippingRange(); + + if (Interactor->GetLightFollowCamera()) + CurrentRenderer->UpdateLightsGeometryToFollowCamera(); + + Interactor->Render(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::Spin() +{ + if (!CurrentRenderer) + return; + + vtkRenderWindowInteractor *rwi = Interactor; + + double *center = CurrentRenderer->GetCenter(); + + double newAngle = vtkMath::DegreesFromRadians( atan2( rwi->GetEventPosition()[1] - center[1], rwi->GetEventPosition()[0] - center[0] ) ); + double oldAngle = vtkMath::DegreesFromRadians( atan2( rwi->GetLastEventPosition()[1] - center[1], rwi->GetLastEventPosition()[0] - center[0] ) ); + + vtkCamera *camera = CurrentRenderer->GetActiveCamera(); + camera->Roll( newAngle - oldAngle ); + camera->OrthogonalizeViewUp(); + + rwi->Render(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::Pan() +{ + if (!CurrentRenderer) + return; + + vtkRenderWindowInteractor *rwi = Interactor; + + double viewFocus[4], focalDepth, viewPoint[3]; + double newPickPoint[4], oldPickPoint[4], motionVector[3]; + + // Calculate the focal depth since we'll be using it a lot + + vtkCamera *camera = CurrentRenderer->GetActiveCamera(); + camera->GetFocalPoint(viewFocus); + ComputeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2], viewFocus); + focalDepth = viewFocus[2]; + + ComputeDisplayToWorld(rwi->GetEventPosition()[0], rwi->GetEventPosition()[1], focalDepth, newPickPoint); + + // Has to recalc old mouse point since the viewport has moved, so can't move it outside the loop + ComputeDisplayToWorld(rwi->GetLastEventPosition()[0], rwi->GetLastEventPosition()[1], focalDepth, oldPickPoint); + + // Camera motion is reversed + motionVector[0] = oldPickPoint[0] - newPickPoint[0]; + motionVector[1] = oldPickPoint[1] - newPickPoint[1]; + motionVector[2] = oldPickPoint[2] - newPickPoint[2]; + + camera->GetFocalPoint(viewFocus); + camera->GetPosition(viewPoint); + camera->SetFocalPoint(motionVector[0] + viewFocus[0], motionVector[1] + viewFocus[1], motionVector[2] + viewFocus[2]); + camera->SetPosition( motionVector[0] + viewPoint[0], motionVector[1] + viewPoint[1], motionVector[2] + viewPoint[2]); + + if (Interactor->GetLightFollowCamera()) + CurrentRenderer->UpdateLightsGeometryToFollowCamera(); + + Interactor->Render(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::Dolly() +{ + if (!CurrentRenderer) + return; + + int dy = Interactor->GetEventPosition()[1] - Interactor->GetLastEventPosition()[1]; + Dolly(pow(1.1, MotionFactor * dy / CurrentRenderer->GetCenter()[1])); +} + +void cv::viz::vtkVizInteractorStyle::Dolly(double factor) +{ + if (!CurrentRenderer) + return; + + vtkCamera *camera = CurrentRenderer->GetActiveCamera(); + if (camera->GetParallelProjection()) + camera->SetParallelScale(camera->GetParallelScale() / factor); + else + { + camera->Dolly(factor); + if (AutoAdjustCameraClippingRange) + CurrentRenderer->ResetCameraClippingRange(); + } + + if (Interactor->GetLightFollowCamera()) + CurrentRenderer->UpdateLightsGeometryToFollowCamera(); + + Interactor->Render(); +} +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::Fly() +{ + if (CurrentRenderer == NULL) + return; + + if (KeysDown) + FlyByKey(); + else + FlyByMouse(); + + CurrentRenderer->GetActiveCamera()->OrthogonalizeViewUp(); + + if (AutoAdjustCameraClippingRange) + CurrentRenderer->ResetCameraClippingRange(); + + if (Interactor->GetLightFollowCamera()) + CurrentRenderer->UpdateLightsGeometryToFollowCamera(); +} + +void cv::viz::vtkVizInteractorStyle::SetupMotionVars() +{ + Vec6d bounds; + CurrentRenderer->ComputeVisiblePropBounds(bounds.val); + + if ( !vtkMath::AreBoundsInitialized(bounds.val) ) + DiagonalLength = 1.0; + else + DiagonalLength = norm(Vec3d(bounds[0], bounds[2], bounds[4]) - Vec3d(bounds[1], bounds[3], bounds[5])); +} + +void cv::viz::vtkVizInteractorStyle::MotionAlongVector(const Vec3d& vector, double amount, vtkCamera* cam) +{ + // move camera and focus along DirectionOfProjection + Vec3d campos = Vec3d(cam->GetPosition()) - amount * vector; + Vec3d camfoc = Vec3d(cam->GetFocalPoint()) - amount * vector; + + cam->SetPosition(campos.val); + cam->SetFocalPoint(camfoc.val); +} + +void cv::viz::vtkVizInteractorStyle::FlyByMouse() +{ + vtkCamera* cam = CurrentRenderer->GetActiveCamera(); + double speed = DiagonalLength * MotionStepSize * MotionUserScale; + speed = speed * ( Interactor->GetShiftKey() ? MotionAccelerationFactor : 1.0); + + // Sidestep + if (Interactor->GetAltKey()) + { + if (DeltaYaw!=0.0) + { + vtkMatrix4x4 *vtm = cam->GetViewTransformMatrix(); + Vec3d a_vector(vtm->GetElement(0,0), vtm->GetElement(0,1), vtm->GetElement(0,2)); + + MotionAlongVector(a_vector, -DeltaYaw*speed, cam); + } + if (DeltaPitch!=0.0) + { + Vec3d a_vector(cam->GetViewUp()); + MotionAlongVector(a_vector, DeltaPitch*speed, cam); + } + } + else + { + cam->Yaw(DeltaYaw); + cam->Pitch(DeltaPitch); + DeltaYaw = 0; + DeltaPitch = 0; + } + // + if (!Interactor->GetControlKey()) + { + Vec3d a_vector(cam->GetDirectionOfProjection()); // reversed (use -speed) + switch (State) + { + case VTKIS_FORWARDFLY: MotionAlongVector(a_vector, -speed, cam); break; + case VTKIS_REVERSEFLY: MotionAlongVector(a_vector, speed, cam); break; + } + } +} + +void cv::viz::vtkVizInteractorStyle::FlyByKey() +{ + vtkCamera* cam = CurrentRenderer->GetActiveCamera(); + + double speed = DiagonalLength * MotionStepSize * MotionUserScale; + speed = speed * ( Interactor->GetShiftKey() ? MotionAccelerationFactor : 1.0); + + // Left and right + if (Interactor->GetAltKey()) + { // Sidestep + vtkMatrix4x4 *vtm = cam->GetViewTransformMatrix(); + Vec3d a_vector(vtm->GetElement(0,0), vtm->GetElement(0,1), vtm->GetElement(0,2)); + + if (KeysDown & 1) + MotionAlongVector(a_vector, -speed, cam); + + if (KeysDown & 2) + MotionAlongVector(a_vector, speed, cam); + } + else + { + if (KeysDown & 1) + cam->Yaw( AngleStepSize); + + if (KeysDown & 2) + cam->Yaw(-AngleStepSize); + } + + // Up and Down + if (Interactor->GetControlKey()) + { // Sidestep + Vec3d a_vector = Vec3d(cam->GetViewUp()); + if (KeysDown & 4) + MotionAlongVector(a_vector,-speed, cam); + + if (KeysDown & 8) + MotionAlongVector(a_vector, speed, cam); + } + else + { + if (KeysDown & 4) + cam->Pitch(-AngleStepSize); + + if (KeysDown & 8) + cam->Pitch( AngleStepSize); + } + + // forward and backward + Vec3d a_vector(cam->GetDirectionOfProjection()); + if (KeysDown & 16) + MotionAlongVector(a_vector, speed, cam); + + if (KeysDown & 32) + MotionAlongVector(a_vector,-speed, cam); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::PrintSelf(ostream& os, vtkIndent indent) +{ + Superclass::PrintSelf(os, indent); + os << indent << "MotionFactor: " << MotionFactor << "\n"; + os << indent << "MotionStepSize: " << MotionStepSize << "\n"; + os << indent << "MotionAccelerationFactor: "<< MotionAccelerationFactor << "\n"; + os << indent << "AngleStepSize: " << AngleStepSize << "\n"; + os << indent << "MotionUserScale: "<< MotionUserScale << "\n"; +} diff --git a/modules/viz/src/vtk/vtkVizInteractorStyle.hpp b/modules/viz/src/vtk/vtkVizInteractorStyle.hpp new file mode 100644 index 000000000..c9458ae06 --- /dev/null +++ b/modules/viz/src/vtk/vtkVizInteractorStyle.hpp @@ -0,0 +1,169 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __OPENCV_VIZ_INTERACTOR_STYLE_H__ +#define __OPENCV_VIZ_INTERACTOR_STYLE_H__ + +#include + +namespace cv +{ + namespace viz + { + class vtkVizInteractorStyle : public vtkInteractorStyle + { + public: + static vtkVizInteractorStyle *New(); + vtkTypeMacro(vtkVizInteractorStyle, vtkInteractorStyle) + void PrintSelf(ostream& os, vtkIndent indent); + + virtual void OnChar(); + virtual void OnKeyDown(); + virtual void OnKeyUp(); + + virtual void OnMouseMove(); + virtual void OnLeftButtonDown(); + virtual void OnLeftButtonUp(); + virtual void OnMiddleButtonDown(); + virtual void OnMiddleButtonUp(); + virtual void OnRightButtonDown(); + virtual void OnRightButtonUp(); + virtual void OnMouseWheelForward(); + virtual void OnMouseWheelBackward(); + virtual void OnTimer(); + + virtual void Rotate(); + virtual void Spin(); + virtual void Pan(); + virtual void Dolly(); + + vtkSetMacro(FlyMode,bool) + vtkGetMacro(FlyMode,bool) + + + vtkSetMacro(MotionFactor, double) + vtkGetMacro(MotionFactor, double) + + void registerMouseCallback(void (*callback)(const MouseEvent&, void*), void* cookie = 0); + void registerKeyboardCallback(void (*callback)(const KeyboardEvent&, void*), void * cookie = 0); + + void setWidgetActorMap(const Ptr& actors) { widget_actor_map_ = actors; } + void saveScreenshot(const String &file); + void exportScene(const String &file); + void exportScene(); + void changePointsSize(float delta); + void setRepresentationToPoints(); + void printCameraParams(); + void toggleFullScreen(); + void resetViewerPose(); + void toggleStereo(); + void printHelp(); + + // Set the basic unit step size : by default 1/250 of bounding diagonal + vtkSetMacro(MotionStepSize,double) + vtkGetMacro(MotionStepSize,double) + + // Set acceleration factor when shift key is applied : default 10 + vtkSetMacro(MotionAccelerationFactor,double) + vtkGetMacro(MotionAccelerationFactor,double) + + // Set the basic angular unit for turning : default 1 degree + vtkSetMacro(AngleStepSize,double) + vtkGetMacro(AngleStepSize,double) + + private: + Ptr widget_actor_map_; + + Vec2i win_size_; + Vec2i win_pos_; + Vec2i max_win_size_; + + void zoomIn(); + void zoomOut(); + + protected: + vtkVizInteractorStyle(); + ~vtkVizInteractorStyle(); + + virtual void Dolly(double factor); + + void Fly(); + void FlyByMouse(); + void FlyByKey(); + void SetupMotionVars(); + void MotionAlongVector(const Vec3d& vector, double amount, vtkCamera* cam); + + private: + vtkVizInteractorStyle(const vtkVizInteractorStyle&); + vtkVizInteractorStyle& operator=(const vtkVizInteractorStyle&); + + //! True for red-blue colors, false for magenta-green. + bool stereo_anaglyph_redblue_; + + void (*keyboardCallback_)(const KeyboardEvent&, void*); + void *keyboard_callback_cookie_; + + void (*mouseCallback_)(const MouseEvent&, void*); + void *mouse_callback_cookie_; + + bool FlyMode; + double MotionFactor; + + int getModifiers(); + + // from fly + unsigned char KeysDown; + double DiagonalLength; + double MotionStepSize; + double MotionUserScale; + double MotionAccelerationFactor; + double AngleStepSize; + double DeltaYaw; + double DeltaPitch; + }; + } // end namespace viz +} // end namespace cv + +#endif diff --git a/modules/viz/src/vtk/vtkXYZReader.cpp b/modules/viz/src/vtk/vtkXYZReader.cpp new file mode 100644 index 000000000..57726eae9 --- /dev/null +++ b/modules/viz/src/vtk/vtkXYZReader.cpp @@ -0,0 +1,107 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "../precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkXYZReader); +}} + + +cv::viz::vtkXYZReader::vtkXYZReader() +{ + this->FileName = 0; + this->SetNumberOfInputPorts(0); +} + +cv::viz::vtkXYZReader::~vtkXYZReader() +{ + this->SetFileName(0); +} + +void cv::viz::vtkXYZReader::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + os << indent << "FileName: " << (this->FileName ? this->FileName : "(none)") << "\n"; +} + +int cv::viz::vtkXYZReader::RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector* outputVector) +{ + // Make sure we have a file to read. + if(!this->FileName) + { + vtkErrorMacro("A FileName must be specified."); + return 0; + } + + // Open the input file. + ifstream fin(this->FileName); + if(!fin) + { + vtkErrorMacro("Error opening file " << this->FileName); + return 0; + } + + // Allocate objects to hold points and vertex cells. + vtkSmartPointer points = vtkSmartPointer::New(); + vtkSmartPointer verts = vtkSmartPointer::New(); + + // Read points from the file. + vtkDebugMacro("Reading points from file " << this->FileName); + double x[3]; + while(fin >> x[0] >> x[1] >> x[2]) + { + vtkIdType id = points->InsertNextPoint(x); + verts->InsertNextCell(1, &id); + } + vtkDebugMacro("Read " << points->GetNumberOfPoints() << " points."); + + // Store the points and cells in the output data object. + vtkPolyData* output = vtkPolyData::GetData(outputVector); + output->SetPoints(points); + output->SetVerts(verts); + + return 1; +} diff --git a/modules/viz/src/vtk/vtkXYZReader.h b/modules/viz/src/vtk/vtkXYZReader.h new file mode 100644 index 000000000..13ae048ec --- /dev/null +++ b/modules/viz/src/vtk/vtkXYZReader.h @@ -0,0 +1,80 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __vtkXYZReader_h +#define __vtkXYZReader_h + +#include "vtkPolyDataAlgorithm.h" + +namespace cv +{ + namespace viz + { + class vtkXYZReader : public vtkPolyDataAlgorithm + { + public: + static vtkXYZReader* New(); + vtkTypeMacro(vtkXYZReader,vtkPolyDataAlgorithm) + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Set/Get the name of the file from which to read points. + vtkSetStringMacro(FileName) + vtkGetStringMacro(FileName) + + protected: + vtkXYZReader(); + ~vtkXYZReader(); + + char* FileName; + + int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*); + private: + vtkXYZReader(const vtkXYZReader&); // Not implemented. + void operator=(const vtkXYZReader&); // Not implemented. + }; + } +} + +#endif diff --git a/modules/viz/src/vtk/vtkXYZWriter.cpp b/modules/viz/src/vtk/vtkXYZWriter.cpp new file mode 100644 index 000000000..cf95e3c6a --- /dev/null +++ b/modules/viz/src/vtk/vtkXYZWriter.cpp @@ -0,0 +1,122 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "../precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkXYZWriter); +}} + +cv::viz::vtkXYZWriter::vtkXYZWriter() +{ + std::ofstream fout; // only used to extract the default precision + this->DecimalPrecision = fout.precision(); +} + +void cv::viz::vtkXYZWriter::WriteData() +{ + vtkPolyData *input = this->GetInput(); + if (!input) + return; + + if (!this->FileName ) + { + vtkErrorMacro(<< "No FileName specified! Can't write!"); + this->SetErrorCode(vtkErrorCode::NoFileNameError); + return; + } + + vtkDebugMacro(<<"Opening vtk file for writing..."); + ostream *outfilep = new ofstream(this->FileName, ios::out); + if (outfilep->fail()) + { + vtkErrorMacro(<< "Unable to open file: "<< this->FileName); + this->SetErrorCode(vtkErrorCode::CannotOpenFileError); + delete outfilep; + return; + } + + ostream &outfile = *outfilep; + + for(vtkIdType i = 0; i < input->GetNumberOfPoints(); ++i) + { + Vec3d p; + input->GetPoint(i, p.val); + outfile << std::setprecision(this->DecimalPrecision) << p[0] << " " << p[1] << " " << p[2] << std::endl; + } + + // Close the file + vtkDebugMacro(<<"Closing vtk file\n"); + delete outfilep; + + // Delete the file if an error occurred + if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError) + { + vtkErrorMacro("Ran out of disk space; deleting file: " << this->FileName); + unlink(this->FileName); + } +} + +int cv::viz::vtkXYZWriter::FillInputPortInformation(int, vtkInformation *info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); + return 1; +} + +void cv::viz::vtkXYZWriter::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + os << indent << "DecimalPrecision: " << this->DecimalPrecision << "\n"; +} + +vtkPolyData* cv::viz::vtkXYZWriter::GetInput() +{ + return vtkPolyData::SafeDownCast(this->Superclass::GetInput()); +} + +vtkPolyData* cv::viz::vtkXYZWriter::GetInput(int port) +{ + return vtkPolyData::SafeDownCast(this->Superclass::GetInput(port)); +} diff --git a/modules/viz/src/vtk/vtkXYZWriter.h b/modules/viz/src/vtk/vtkXYZWriter.h new file mode 100644 index 000000000..91d0c8f6b --- /dev/null +++ b/modules/viz/src/vtk/vtkXYZWriter.h @@ -0,0 +1,90 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __vtkXYZWriter_h +#define __vtkXYZWriter_h + +#include "vtkWriter.h" + +namespace cv +{ + namespace viz + { + class vtkXYZWriter : public vtkWriter + { + public: + static vtkXYZWriter *New(); + vtkTypeMacro(vtkXYZWriter,vtkWriter) + void PrintSelf(ostream& os, vtkIndent indent); + + vtkGetMacro(DecimalPrecision, int) + vtkSetMacro(DecimalPrecision, int) + + // Description: + // Specify file name of data file to write. + vtkSetStringMacro(FileName) + vtkGetStringMacro(FileName) + + // Description: + // Get the input to this writer. + vtkPolyData* GetInput(); + vtkPolyData* GetInput(int port); + + protected: + vtkXYZWriter(); + ~vtkXYZWriter(){} + + void WriteData(); + int FillInputPortInformation(int port, vtkInformation *info); + + int DecimalPrecision; + char *FileName; + + private: + vtkXYZWriter(const vtkXYZWriter&); // Not implemented. + void operator=(const vtkXYZWriter&); // Not implemented. + }; + } +} +#endif diff --git a/modules/viz/src/widget.cpp b/modules/viz/src/widget.cpp new file mode 100644 index 000000000..b46d3d1ce --- /dev/null +++ b/modules/viz/src/widget.cpp @@ -0,0 +1,352 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// widget implementation + +class cv::viz::Widget::Impl +{ +public: + vtkSmartPointer prop; + Impl() : prop(0) {} +}; + +cv::viz::Widget::Widget() : impl_( new Impl() ) { } + +cv::viz::Widget::Widget(const Widget& other) : impl_( new Impl() ) +{ + if (other.impl_ && other.impl_->prop) + impl_->prop = other.impl_->prop; +} + +cv::viz::Widget& cv::viz::Widget::operator=(const Widget& other) +{ + if (!impl_) + impl_ = new Impl(); + + if (other.impl_) + impl_->prop = other.impl_->prop; + return *this; +} + +cv::viz::Widget::~Widget() +{ + if (impl_) + { + delete impl_; + impl_ = 0; + } +} + +cv::viz::Widget cv::viz::Widget::fromPlyFile(const String &file_name) +{ + CV_Assert(vtkPLYReader::CanReadFile(file_name.c_str())); + + vtkSmartPointer reader = vtkSmartPointer::New(); + reader->SetFileName(file_name.c_str()); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetInputConnection( reader->GetOutputPort() ); +#if VTK_MAJOR_VERSION < 8 + mapper->ImmediateModeRenderingOff(); +#endif + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + Widget widget; + WidgetAccessor::setProp(widget, actor); + return widget; +} + +void cv::viz::Widget::setRenderingProperty(int property, double value) +{ + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget type is not supported." && actor); + + switch (property) + { + case POINT_SIZE: actor->GetProperty()->SetPointSize(float(value)); break; + case OPACITY: actor->GetProperty()->SetOpacity(value); break; + case LINE_WIDTH: actor->GetProperty()->SetLineWidth(float(value)); break; +#if VTK_MAJOR_VERSION < 8 + case IMMEDIATE_RENDERING: actor->GetMapper()->SetImmediateModeRendering(int(value)); break; +#else + case IMMEDIATE_RENDERING: std::cerr << "this property has no effect" << std::endl; break; +#endif + case AMBIENT: actor->GetProperty()->SetAmbient(float(value)); break; + case LIGHTING: + { + if (value == 0) + actor->GetProperty()->LightingOff(); + else + actor->GetProperty()->LightingOn(); + break; + } + case FONT_SIZE: + { + vtkTextActor* text_actor = vtkTextActor::SafeDownCast(actor); + CV_Assert("Widget does not have text content." && text_actor); + text_actor->GetTextProperty()->SetFontSize(int(value)); + break; + } + case REPRESENTATION: + { + switch (int(value)) + { + case REPRESENTATION_POINTS: actor->GetProperty()->SetRepresentationToPoints(); break; + case REPRESENTATION_WIREFRAME: actor->GetProperty()->SetRepresentationToWireframe(); break; + case REPRESENTATION_SURFACE: actor->GetProperty()->SetRepresentationToSurface(); break; + } + break; + } + case SHADING: + { + switch (int(value)) + { + case SHADING_FLAT: actor->GetProperty()->SetInterpolationToFlat(); break; + case SHADING_GOURAUD: + { + if (!actor->GetMapper()->GetInput()->GetPointData()->GetNormals()) + { + vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + CV_Assert("Can't set shading property for such type of widget" && mapper); + + vtkSmartPointer with_normals = VtkUtils::ComputeNormals(mapper->GetInput()); + VtkUtils::SetInputData(mapper, with_normals); + } + actor->GetProperty()->SetInterpolationToGouraud(); + break; + } + case SHADING_PHONG: + { + if (!actor->GetMapper()->GetInput()->GetPointData()->GetNormals()) + { + vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + CV_Assert("Can't set shading property for such type of widget" && mapper); + + vtkSmartPointer with_normals = VtkUtils::ComputeNormals(mapper->GetInput()); + VtkUtils::SetInputData(mapper, with_normals); + } + actor->GetProperty()->SetInterpolationToPhong(); + break; + } + } + break; + } + default: + CV_Assert("setRenderingProperty: Unknown property"); + } + actor->Modified(); +} + +double cv::viz::Widget::getRenderingProperty(int property) const +{ + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget type is not supported." && actor); + + double value = 0.0; + switch (property) + { + case POINT_SIZE: value = actor->GetProperty()->GetPointSize(); break; + case OPACITY: value = actor->GetProperty()->GetOpacity(); break; + case LINE_WIDTH: value = actor->GetProperty()->GetLineWidth(); break; +#if VTK_MAJOR_VERSION < 8 + case IMMEDIATE_RENDERING: value = actor->GetMapper()->GetImmediateModeRendering(); break; +#else + case IMMEDIATE_RENDERING: std::cerr << "this property has no effect" << std::endl; break; +#endif + case AMBIENT: value = actor->GetProperty()->GetAmbient(); break; + case LIGHTING: value = actor->GetProperty()->GetLighting(); break; + case FONT_SIZE: + { + vtkTextActor* text_actor = vtkTextActor::SafeDownCast(actor); + CV_Assert("Widget does not have text content." && text_actor); + value = text_actor->GetTextProperty()->GetFontSize();; + break; + } + case REPRESENTATION: + { + switch (actor->GetProperty()->GetRepresentation()) + { + case VTK_POINTS: value = REPRESENTATION_POINTS; break; + case VTK_WIREFRAME: value = REPRESENTATION_WIREFRAME; break; + case VTK_SURFACE: value = REPRESENTATION_SURFACE; break; + } + break; + } + case SHADING: + { + switch (actor->GetProperty()->GetInterpolation()) + { + case VTK_FLAT: value = SHADING_FLAT; break; + case VTK_GOURAUD: value = SHADING_GOURAUD; break; + case VTK_PHONG: value = SHADING_PHONG; break; + } + break; + } + default: + CV_Assert("getRenderingProperty: Unknown property"); + } + return value; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// widget accessor implementation + +vtkSmartPointer cv::viz::WidgetAccessor::getProp(const Widget& widget) +{ + return widget.impl_->prop; +} + +void cv::viz::WidgetAccessor::setProp(Widget& widget, vtkSmartPointer prop) +{ + widget.impl_->prop = prop; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// widget3D implementation + +void cv::viz::Widget3D::setPose(const Affine3d &pose) +{ + vtkProp3D *actor = vtkProp3D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget is not 3D." && actor); + + vtkSmartPointer matrix = vtkmatrix(pose.matrix); + actor->SetUserMatrix(matrix); + actor->Modified(); +} + +void cv::viz::Widget3D::updatePose(const Affine3d &pose) +{ + vtkProp3D *actor = vtkProp3D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget is not 3D." && actor); + + vtkSmartPointer matrix = actor->GetUserMatrix(); + if (!matrix) + { + setPose(pose); + return; + } + + Affine3d updated_pose = pose * Affine3d(*matrix->Element); + matrix = vtkmatrix(updated_pose.matrix); + + actor->SetUserMatrix(matrix); + actor->Modified(); +} + +cv::Affine3d cv::viz::Widget3D::getPose() const +{ + vtkProp3D *actor = vtkProp3D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget is not 3D." && actor); + if (!actor->GetUserMatrix()) + { + return Affine3d(); // empty user matrix, return an identity transform. + } + return Affine3d(*actor->GetUserMatrix()->Element); +} + +void cv::viz::Widget3D::applyTransform(const Affine3d &transform) +{ + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget is not 3D actor." && actor); + + vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + CV_Assert("Widget doesn't have a polydata mapper" && mapper); + + mapper->Update(); // #10945 + VtkUtils::SetInputData(mapper, VtkUtils::TransformPolydata(mapper->GetInput(), transform)); + mapper->Update(); +} + +void cv::viz::Widget3D::setColor(const Color &color) +{ + // Cast to actor instead of prop3d since prop3d doesn't provide getproperty + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget type is not supported." && actor); + + Color c = vtkcolor(color); + actor->GetMapper()->ScalarVisibilityOff(); + actor->GetProperty()->SetColor(c.val); + actor->GetProperty()->SetEdgeColor(c.val); + actor->Modified(); +} + +template<> cv::viz::Widget3D cv::viz::Widget::cast() const +{ + vtkProp3D *actor = vtkProp3D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget cannot be cast." && actor); + + Widget3D widget; + WidgetAccessor::setProp(widget, actor); + return widget; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// widget2D implementation + +void cv::viz::Widget2D::setColor(const Color &color) +{ + vtkActor2D *actor = vtkActor2D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget type is not supported." && actor); + Color c = vtkcolor(color); + actor->GetProperty()->SetColor(c.val); + actor->Modified(); +} + +template<> cv::viz::Widget2D cv::viz::Widget::cast() const +{ + vtkActor2D *actor = vtkActor2D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget cannot be cast." && actor); + + Widget2D widget; + WidgetAccessor::setProp(widget, actor); + return widget; +} diff --git a/modules/viz/test/test_common.cpp b/modules/viz/test/test_common.cpp new file mode 100644 index 000000000..ac0cea4b8 --- /dev/null +++ b/modules/viz/test/test_common.cpp @@ -0,0 +1,27 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +#include "test_precomp.hpp" + +cv::String cv::Path::combine(const String& item1, const String& item2) +{ + if (item1.empty()) + return item2; + + if (item2.empty()) + return item1; + + char last = item1[item1.size()-1]; + + bool need_append = last != '/' && last != '\\'; + return item1 + (need_append ? "/" : "") + item2; +} + +cv::String cv::Path::combine(const String& item1, const String& item2, const String& item3) +{ return combine(combine(item1, item2), item3); } + +cv::String cv::Path::change_extension(const String& file, const String& ext) +{ + String::size_type pos = file.find_last_of('.'); + return pos == String::npos ? file : file.substr(0, pos+1) + ext; +} diff --git a/modules/viz/test/test_common.hpp b/modules/viz/test/test_common.hpp new file mode 100644 index 000000000..bfd6d13cd --- /dev/null +++ b/modules/viz/test/test_common.hpp @@ -0,0 +1,87 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef OPENCV_VIZ_TEST_COMMON_HPP +#define OPENCV_VIZ_TEST_COMMON_HPP + +#include + +namespace cv +{ + struct Path + { + static String combine(const String& item1, const String& item2); + static String combine(const String& item1, const String& item2, const String& item3); + static String change_extension(const String& file, const String& ext); + }; + + inline cv::String get_dragon_ply_file_path() + { + return Path::combine(cvtest::TS::ptr()->get_data_path(), "dragon.ply"); + } + + template + inline std::vector< Affine3<_Tp> > generate_test_trajectory() + { + std::vector< Affine3<_Tp> > result; + + for (int i = 0, j = 0; i <= 270; i += 3, j += 10) + { + double x = 2 * cos(i * 3 * CV_PI/180.0) * (1.0 + 0.5 * cos(1.2 + i * 1.2 * CV_PI/180.0)); + double y = 0.25 + i/270.0 + sin(j * CV_PI/180.0) * 0.2 * sin(0.6 + j * 1.5 * CV_PI/180.0); + double z = 2 * sin(i * 3 * CV_PI/180.0) * (1.0 + 0.5 * cos(1.2 + i * CV_PI/180.0)); + result.push_back(viz::makeCameraPose(Vec3d(x, y, z), Vec3d::all(0.0), Vec3d(0.0, 1.0, 0.0))); + } + return result; + } + + inline Mat make_gray(const Mat& image) + { + Mat chs[3]; split(image, chs); + return 0.114 * chs[0] + 0.58 * chs[1] + 0.3 * chs[2]; + } +} + +#endif diff --git a/modules/viz/test/test_main.cpp b/modules/viz/test/test_main.cpp new file mode 100644 index 000000000..656b1d4fe --- /dev/null +++ b/modules/viz/test/test_main.cpp @@ -0,0 +1,6 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +#include "test_precomp.hpp" + +CV_TEST_MAIN("viz") diff --git a/modules/viz/test/test_precomp.hpp b/modules/viz/test/test_precomp.hpp new file mode 100644 index 000000000..954f03354 --- /dev/null +++ b/modules/viz/test/test_precomp.hpp @@ -0,0 +1,10 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +#include "opencv2/ts.hpp" +#include "test_common.hpp" + +namespace opencv_test +{ +using namespace cv::viz; +} diff --git a/modules/viz/test/test_tutorial2.cpp b/modules/viz/test/test_tutorial2.cpp new file mode 100644 index 000000000..6b2972f0a --- /dev/null +++ b/modules/viz/test/test_tutorial2.cpp @@ -0,0 +1,58 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +#include "test_precomp.hpp" + +namespace opencv_test { namespace { + +static void tutorial2() +{ + /// Create a window + viz::Viz3d myWindow("Coordinate Frame"); + + /// Add coordinate axes + myWindow.showWidget("Coordinate Widget", viz::WCoordinateSystem()); + + /// Add line to represent (1,1,1) axis + viz::WLine axis(Point3f(-1.0, -1.0, -1.0), Point3d(1.0, 1.0, 1.0)); + axis.setRenderingProperty(viz::LINE_WIDTH, 4.0); + myWindow.showWidget("Line Widget", axis); + + /// Construct a cube widget + viz::WCube cube_widget(Point3d(0.5, 0.5, 0.0), Point3d(0.0, 0.0, -0.5), true, viz::Color::blue()); + cube_widget.setRenderingProperty(viz::LINE_WIDTH, 4.0); + + /// Display widget (update if already displayed) + myWindow.showWidget("Cube Widget", cube_widget); + + /// Rodrigues vector + Vec3d rot_vec = Vec3d::all(0); + double translation_phase = 0.0, translation = 0.0; + while(!myWindow.wasStopped()) + { + /* Rotation using rodrigues */ + /// Rotate around (1,1,1) + rot_vec[0] += CV_PI * 0.01; + rot_vec[1] += CV_PI * 0.01; + rot_vec[2] += CV_PI * 0.01; + + /// Shift on (1,1,1) + translation_phase += CV_PI * 0.01; + translation = sin(translation_phase); + + /// Construct pose + Affine3d pose(rot_vec, Vec3d(translation, translation, translation)); + + myWindow.setWidgetPose("Cube Widget", pose); + + myWindow.spinOnce(1, true); + } +} + + +TEST(Viz, DISABLED_tutorial2_pose_of_widget) +{ + tutorial2(); +} + +}} // namespace diff --git a/modules/viz/test/test_tutorial3.cpp b/modules/viz/test/test_tutorial3.cpp new file mode 100644 index 000000000..232130f0a --- /dev/null +++ b/modules/viz/test/test_tutorial3.cpp @@ -0,0 +1,64 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +#include "test_precomp.hpp" + +namespace opencv_test { namespace { + +/** + * @function main + */ +static void tutorial3(bool camera_pov) +{ + /// Create a window + viz::Viz3d myWindow("Coordinate Frame"); + + /// Add coordinate axes + myWindow.showWidget("Coordinate Widget", viz::WCoordinateSystem()); + + /// Let's assume camera has the following properties + Point3d cam_origin(3.0, 3.0, 3.0), cam_focal_point(3.0, 3.0, 2.0), cam_y_dir(-1.0, 0.0, 0.0); + + /// We can get the pose of the cam using makeCameraPose + Affine3d camera_pose = viz::makeCameraPose(cam_origin, cam_focal_point, cam_y_dir); + + /// We can get the transformation matrix from camera coordinate system to global using + /// - makeTransformToGlobal. We need the axes of the camera + Affine3d transform = viz::makeTransformToGlobal(Vec3d(0.0, -1.0, 0.0), Vec3d(-1.0, 0.0, 0.0), Vec3d(0.0, 0.0, -1.0), cam_origin); + + /// Create a cloud widget. + Mat dragon_cloud = viz::readCloud(get_dragon_ply_file_path()); + viz::WCloud cloud_widget(dragon_cloud, viz::Color::green()); + + /// Pose of the widget in camera frame + Affine3d cloud_pose = Affine3d().rotate(Vec3d(0.0, CV_PI/2, 0.0)).rotate(Vec3d(0.0, 0.0, CV_PI)).translate(Vec3d(0.0, 0.0, 3.0)); + /// Pose of the widget in global frame + Affine3d cloud_pose_global = transform * cloud_pose; + + /// Visualize camera frame + myWindow.showWidget("CPW_FRUSTUM", viz::WCameraPosition(Vec2f(0.889484f, 0.523599f)), camera_pose); + if (!camera_pov) + myWindow.showWidget("CPW", viz::WCameraPosition(0.5), camera_pose); + + /// Visualize widget + myWindow.showWidget("bunny", cloud_widget, cloud_pose_global); + + /// Set the viewer pose to that of camera + if (camera_pov) + myWindow.setViewerPose(camera_pose); + + /// Start event loop. + myWindow.spin(); +} + +TEST(Viz, tutorial3_global_view) +{ + tutorial3(false); +} + +TEST(Viz, tutorial3_camera_view) +{ + tutorial3(true); +} + +}} // namespace diff --git a/modules/viz/test/test_viz3d.cpp b/modules/viz/test/test_viz3d.cpp new file mode 100644 index 000000000..cdf8a00ad --- /dev/null +++ b/modules/viz/test/test_viz3d.cpp @@ -0,0 +1,65 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// + // + // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. + // + // By downloading, copying, installing or using the software you agree to this license. + // If you do not agree to this license, do not download, install, + // copy or use the software. + // + // + // License Agreement + // For Open Source Computer Vision Library + // + // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. + // Copyright (C) 2008-2013, Willow Garage Inc., all rights reserved. + // Third party copyrights are property of their respective owners. + // + // Redistribution and use in source and binary forms, with or without modification, + // are permitted provided that the following conditions are met: + // + // * Redistribution's of source code must retain the above copyright notice, + // this list of conditions and the following disclaimer. + // + // * Redistribution's in binary form must reproduce the above copyright notice, + // this list of conditions and the following disclaimer in the documentation + // and / or other materials provided with the distribution. + // + // * The name of the copyright holders may not be used to endorse or promote products + // derived from this software without specific prior written permission. + // + // This software is provided by the copyright holders and contributors "as is" and + // any express or implied warranties, including, but not limited to, the implied + // warranties of merchantability and fitness for a particular purpose are disclaimed. + // In no event shall the Intel Corporation or contributors be liable for any direct, + // indirect, incidental, special, exemplary, or consequential damages + // (including, but not limited to, procurement of substitute goods or services; + // loss of use, data, or profits; or business interruption) however caused + // and on any theory of liability, whether in contract, strict liability, + // or tort (including negligence or otherwise) arising in any way out of + // the use of this software, even if advised of the possibility of such damage. + // + //M*/ +#include "test_precomp.hpp" + +namespace opencv_test { namespace { + +TEST(Viz_viz3d, DISABLED_develop) +{ + cv::Mat cloud = cv::viz::readCloud(get_dragon_ply_file_path()); + + cv::viz::Viz3d viz("abc"); + viz.setBackgroundMeshLab(); + viz.showWidget("coo", cv::viz::WCoordinateSystem(1)); + viz.showWidget("cloud", cv::viz::WPaintedCloud(cloud)); + + //---->>>>> + //std::vector gt, es; + //cv::viz::readTrajectory(gt, "d:/Datasets/trajs/gt%05d.xml"); + //cv::viz::readTrajectory(es, "d:/Datasets/trajs/es%05d.xml"); + //cv::Mat cloud = cv::viz::readCloud(get_dragon_ply_file_path()); + //---->>>>> + + viz.spin(); +} + +}} // namespace diff --git a/modules/viz/test/tests_simple.cpp b/modules/viz/test/tests_simple.cpp new file mode 100644 index 000000000..12d696dfb --- /dev/null +++ b/modules/viz/test/tests_simple.cpp @@ -0,0 +1,454 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// + // + // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. + // + // By downloading, copying, installing or using the software you agree to this license. + // If you do not agree to this license, do not download, install, + // copy or use the software. + // + // + // License Agreement + // For Open Source Computer Vision Library + // + // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. + // Copyright (C) 2008-2013, Willow Garage Inc., all rights reserved. + // Third party copyrights are property of their respective owners. + // + // Redistribution and use in source and binary forms, with or without modification, + // are permitted provided that the following conditions are met: + // + // * Redistribution's of source code must retain the above copyright notice, + // this list of conditions and the following disclaimer. + // + // * Redistribution's in binary form must reproduce the above copyright notice, + // this list of conditions and the following disclaimer in the documentation + // and / or other materials provided with the distribution. + // + // * The name of the copyright holders may not be used to endorse or promote products + // derived from this software without specific prior written permission. + // + // This software is provided by the copyright holders and contributors "as is" and + // any express or implied warranties, including, but not limited to, the implied + // warranties of merchantability and fitness for a particular purpose are disclaimed. + // In no event shall the Intel Corporation or contributors be liable for any direct, + // indirect, incidental, special, exemplary, or consequential damages + // (including, but not limited to, procurement of substitute goods or services; + // loss of use, data, or profits; or business interruption) however caused + // and on any theory of liability, whether in contract, strict liability, + // or tort (including negligence or otherwise) arising in any way out of + // the use of this software, even if advised of the possibility of such damage. + // + //M*/ + +#include "test_precomp.hpp" + +namespace opencv_test { namespace { + +TEST(Viz, show_cloud_bluberry) +{ + Mat dragon_cloud = readCloud(get_dragon_ply_file_path()); + + Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); + + Viz3d viz("show_cloud_bluberry"); + viz.setBackgroundColor(Color::black()); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("dragon", WCloud(dragon_cloud, Color::bluberry()), pose); + + viz.showWidget("text2d", WText("Bluberry cloud", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_cloud_random_color) +{ + Mat dragon_cloud = readCloud(get_dragon_ply_file_path()); + + Mat colors(dragon_cloud.size(), CV_8UC3); + theRNG().fill(colors, RNG::UNIFORM, 0, 255); + + Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); + + Viz3d viz("show_cloud_random_color"); + viz.setBackgroundMeshLab(); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("dragon", WCloud(dragon_cloud, colors), pose); + viz.showWidget("text2d", WText("Random color cloud", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_cloud_masked) +{ + Mat dragon_cloud = readCloud(get_dragon_ply_file_path()); + + Vec3f qnan = Vec3f::all(std::numeric_limits::quiet_NaN()); + for(int i = 0; i < (int)dragon_cloud.total(); ++i) + if (i % 15 != 0) + dragon_cloud.at(i) = qnan; + + Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); + + Viz3d viz("show_cloud_masked"); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("dragon", WCloud(dragon_cloud), pose); + viz.showWidget("text2d", WText("Nan masked cloud", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_cloud_collection) +{ + Mat cloud = readCloud(get_dragon_ply_file_path()); + + WCloudCollection ccol; + ccol.addCloud(cloud, Color::white(), Affine3d().translate(Vec3d(0, 0, 0)).rotate(Vec3d(CV_PI/2, 0, 0))); + ccol.addCloud(cloud, Color::blue(), Affine3d().translate(Vec3d(1, 0, 0))); + ccol.addCloud(cloud, Color::red(), Affine3d().translate(Vec3d(2, 0, 0))); + ccol.finalize(); + + Viz3d viz("show_cloud_collection"); + viz.setBackgroundColor(Color::mlab()); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("ccol", ccol); + viz.showWidget("text2d", WText("Cloud collection", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_painted_clouds) +{ + Mat cloud = readCloud(get_dragon_ply_file_path()); + + Viz3d viz("show_painted_clouds"); + viz.setBackgroundMeshLab(); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("cloud1", WPaintedCloud(cloud), Affine3d(Vec3d(0.0, -CV_PI/2, 0.0), Vec3d(-1.5, 0.0, 0.0))); + viz.showWidget("cloud2", WPaintedCloud(cloud, Vec3d(0.0, -0.75, -1.0), Vec3d(0.0, 0.75, 0.0)), Affine3d(Vec3d(0.0, CV_PI/2, 0.0), Vec3d(1.5, 0.0, 0.0))); + viz.showWidget("cloud3", WPaintedCloud(cloud, Vec3d(0.0, 0.0, -1.0), Vec3d(0.0, 0.0, 1.0), Color::blue(), Color::red())); + viz.showWidget("arrow", WArrow(Vec3d(0.0, 1.0, -1.0), Vec3d(0.0, 1.0, 1.0), 0.009, Color::raspberry())); + viz.showWidget("text2d", WText("Painted clouds", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_mesh) +{ + Mesh mesh = Mesh::load(get_dragon_ply_file_path()); + + Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); + + Viz3d viz("show_mesh"); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("mesh", WMesh(mesh), pose); + viz.showWidget("text2d", WText("Just mesh", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_mesh_random_colors) +{ + Mesh mesh = Mesh::load(get_dragon_ply_file_path()); + theRNG().fill(mesh.colors, RNG::UNIFORM, 0, 255); + + Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); + + Viz3d viz("show_mesh_random_color"); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("mesh", WMesh(mesh), pose); + viz.setRenderingProperty("mesh", SHADING, SHADING_PHONG); + viz.showWidget("text2d", WText("Random color mesh", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_widget_merger) +{ + WWidgetMerger merger; + merger.addWidget(WCube(Vec3d::all(0.0), Vec3d::all(1.0), true, Color::gold())); + + RNG& rng = theRNG(); + for(int i = 0; i < 77; ++i) + { + Vec3b c; + rng.fill(c, RNG::NORMAL, Scalar::all(128), Scalar::all(48), true); + merger.addWidget(WSphere(Vec3d(c)*(1.0/255.0), 7.0/255.0, 10, Color(c[2], c[1], c[0]))); + } + merger.finalize(); + + Viz3d viz("show_mesh_random_color"); + viz.showWidget("coo", WCoordinateSystem()); + viz.showWidget("merger", merger); + viz.showWidget("text2d", WText("Widget merger", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_textured_mesh) +{ + Mat lena = imread(Path::combine(cvtest::TS::ptr()->get_data_path(), "lena.png")); + + std::vector points; + std::vector tcoords; + std::vector polygons; + for(size_t i = 0; i < 64; ++i) + { + double angle = CV_PI/2 * i/64.0; + points.push_back(Vec3d(0.00, cos(angle), sin(angle))*0.75); + points.push_back(Vec3d(1.57, cos(angle), sin(angle))*0.75); + tcoords.push_back(Vec2d(0.0, i/64.0)); + tcoords.push_back(Vec2d(1.0, i/64.0)); + } + + for(int i = 0; i < (int)points.size()/2-1; ++i) + { + int polys[] = {3, 2*i, 2*i+1, 2*i+2, 3, 2*i+1, 2*i+2, 2*i+3}; + polygons.insert(polygons.end(), polys, polys + sizeof(polys)/sizeof(polys[0])); + } + + cv::viz::Mesh mesh; + mesh.cloud = Mat(points, true).reshape(3, 1); + mesh.tcoords = Mat(tcoords, true).reshape(2, 1); + mesh.polygons = Mat(polygons, true).reshape(1, 1); + mesh.texture = lena; + + Viz3d viz("show_textured_mesh"); + viz.setBackgroundMeshLab(); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("mesh", WMesh(mesh)); + viz.setRenderingProperty("mesh", SHADING, SHADING_PHONG); + viz.showWidget("text2d", WText("Textured mesh", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_polyline) +{ + const Color palette[] = { Color::red(), Color::green(), Color::blue(), Color::gold(), Color::raspberry(), Color::bluberry(), Color::lime() }; + size_t palette_size = sizeof(palette)/sizeof(palette[0]); + + Mat polyline(1, 32, CV_64FC3), colors(1, 32, CV_8UC3); + for(int i = 0; i < (int)polyline.total(); ++i) + { + polyline.at(i) = Vec3d(i/16.0, cos(i * CV_PI/6), sin(i * CV_PI/6)); + colors.at(i) = palette[i & palette_size]; + } + + Viz3d viz("show_polyline"); + viz.showWidget("polyline", WPolyLine(polyline, colors)); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("text2d", WText("Polyline", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_sampled_normals) +{ + Mesh mesh = Mesh::load(get_dragon_ply_file_path()); + computeNormals(mesh, mesh.normals); + + Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); + + Viz3d viz("show_sampled_normals"); + viz.showWidget("mesh", WMesh(mesh), pose); + viz.showWidget("normals", WCloudNormals(mesh.cloud, mesh.normals, 30, 0.1f, Color::green()), pose); + viz.setRenderingProperty("normals", LINE_WIDTH, 2.0); + viz.showWidget("text2d", WText("Cloud or mesh normals", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_cloud_shaded_by_normals) +{ + Mesh mesh = Mesh::load(get_dragon_ply_file_path()); + computeNormals(mesh, mesh.normals); + + Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); + + WCloud cloud(mesh.cloud, Color::white(), mesh.normals); + cloud.setRenderingProperty(SHADING, SHADING_GOURAUD); + + Viz3d viz("show_cloud_shaded_by_normals"); + viz.showWidget("cloud", cloud, pose); + viz.showWidget("text2d", WText("Cloud shaded by normals", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_trajectories) +{ + std::vector path = generate_test_trajectory(), sub0, sub1, sub2, sub3, sub4, sub5; + int size =(int)path.size(); + + Mat(path).rowRange(0, size/10+1).copyTo(sub0); + Mat(path).rowRange(size/10, size/5+1).copyTo(sub1); + Mat(path).rowRange(size/5, 11*size/12).copyTo(sub2); + Mat(path).rowRange(11*size/12, size).copyTo(sub3); + Mat(path).rowRange(3*size/4, 33*size/40).copyTo(sub4); + Mat(path).rowRange(33*size/40, 9*size/10).copyTo(sub5); + Matx33d K(1024.0, 0.0, 320.0, 0.0, 1024.0, 240.0, 0.0, 0.0, 1.0); + + Viz3d viz("show_trajectories"); + viz.showWidget("coos", WCoordinateSystem()); + viz.showWidget("sub0", WTrajectorySpheres(sub0, 0.25, 0.07)); + viz.showWidget("sub1", WTrajectory(sub1, WTrajectory::PATH, 0.2, Color::brown())); + viz.showWidget("sub2", WTrajectory(sub2, WTrajectory::FRAMES, 0.2)); + viz.showWidget("sub3", WTrajectory(sub3, WTrajectory::BOTH, 0.2, Color::green())); + viz.showWidget("sub4", WTrajectoryFrustums(sub4, K, 0.3, Color::yellow())); + viz.showWidget("sub5", WTrajectoryFrustums(sub5, Vec2d(0.78, 0.78), 0.15)); + viz.showWidget("text2d", WText("Different kinds of supported trajectories", Point(20, 20), 20, Color::green())); + + int i = 0; + while(!viz.wasStopped()) + { + double a = --i % 360; + Vec3d pose(sin(a * CV_PI/180), 0.7, cos(a * CV_PI/180)); + viz.setViewerPose(makeCameraPose(pose * 7.5, Vec3d(0.0, 0.5, 0.0), Vec3d(0.0, 0.1, 0.0))); + viz.spinOnce(20, true); + } + viz.resetCamera(); + viz.spin(); +} + +TEST(Viz, show_trajectory_reposition) +{ + std::vector path = generate_test_trajectory(); + + Viz3d viz("show_trajectory_reposition_to_origin"); + viz.showWidget("coos", WCoordinateSystem()); + viz.showWidget("sub3", WTrajectory(Mat(path).rowRange(0, (int)path.size()/3), WTrajectory::BOTH, 0.2, Color::brown()), path.front().inv()); + viz.showWidget("text2d", WText("Trajectory resposition to origin", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_camera_positions) +{ + Matx33d K(1024.0, 0.0, 320.0, 0.0, 1024.0, 240.0, 0.0, 0.0, 1.0); + Mat lena = imread(Path::combine(cvtest::TS::ptr()->get_data_path(), "lena.png")); + Mat gray = make_gray(lena); + + Affine3d poses[2]; + for(int i = 0; i < 2; ++i) + { + Vec3d pose = 5 * Vec3d(sin(3.14 + 2.7 + i*60 * CV_PI/180), 0.4 - i*0.3, cos(3.14 + 2.7 + i*60 * CV_PI/180)); + poses[i] = makeCameraPose(pose, Vec3d(0.0, 0.0, 0.0), Vec3d(0.0, -0.1, 0.0)); + } + + Viz3d viz("show_camera_positions"); + viz.showWidget("sphe", WSphere(Point3d(0,0,0), 1.0, 10, Color::orange_red())); + viz.showWidget("coos", WCoordinateSystem(1.5)); + viz.showWidget("pos1", WCameraPosition(0.75), poses[0]); + viz.showWidget("pos2", WCameraPosition(Vec2d(0.78, 0.78), lena, 2.2, Color::green()), poses[0]); + viz.showWidget("pos3", WCameraPosition(0.75), poses[1]); + viz.showWidget("pos4", WCameraPosition(K, gray, 3, Color::indigo()), poses[1]); + viz.showWidget("text2d", WText("Camera positions with images", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_overlay_image) +{ + Mat lena = imread(Path::combine(cvtest::TS::ptr()->get_data_path(), "lena.png")); + Mat gray = make_gray(lena); + + Size2d half_lsize = Size2d(lena.size()) * 0.5; + + Viz3d viz("show_overlay_image"); + viz.setBackgroundMeshLab(); + Size vsz = viz.getWindowSize(); + + viz.showWidget("coos", WCoordinateSystem()); + viz.showWidget("cube", WCube()); + viz.showWidget("img1", WImageOverlay(lena, Rect(Point(10, 10), half_lsize))); + viz.showWidget("img2", WImageOverlay(gray, Rect(Point(vsz.width-10-lena.cols/2, 10), half_lsize))); + viz.showWidget("img3", WImageOverlay(gray, Rect(Point(10, vsz.height-10-lena.rows/2), half_lsize))); + viz.showWidget("img5", WImageOverlay(lena, Rect(Point(vsz.width-10-lena.cols/2, vsz.height-10-lena.rows/2), half_lsize))); + viz.showWidget("text2d", WText("Overlay images", Point(20, 20), 20, Color::green())); + + int i = 0; + while(!viz.wasStopped()) + { + double a = ++i % 360; + Vec3d pose(sin(a * CV_PI/180), 0.7, cos(a * CV_PI/180)); + viz.setViewerPose(makeCameraPose(pose * 3, Vec3d(0.0, 0.5, 0.0), Vec3d(0.0, 0.1, 0.0))); + viz.getWidget("img1").cast().setImage(lena * pow(sin(i*10*CV_PI/180) * 0.5 + 0.5, 1.0)); + viz.spinOnce(1, true); + } + viz.showWidget("text2d", WText("Overlay images (stopped)", Point(20, 20), 20, Color::green())); + viz.spin(); +} + + +TEST(Viz, show_image_method) +{ + Mat lena = imread(Path::combine(cvtest::TS::ptr()->get_data_path(), "lena.png")); + + Viz3d viz("show_image_method"); + viz.showImage(lena); + viz.spinOnce(1500, true); + viz.showImage(lena, lena.size()); + viz.spinOnce(1500, true); + + cv::viz::imshow("show_image_method", make_gray(lena)).spin(); +} + +TEST(Viz, show_image_3d) +{ + Mat lena = imread(Path::combine(cvtest::TS::ptr()->get_data_path(), "lena.png")); + Mat gray = make_gray(lena); + + Viz3d viz("show_image_3d"); + viz.setBackgroundMeshLab(); + viz.showWidget("coos", WCoordinateSystem()); + viz.showWidget("cube", WCube()); + viz.showWidget("arr0", WArrow(Vec3d(0.5, 0.0, 0.0), Vec3d(1.5, 0.0, 0.0), 0.009, Color::raspberry())); + viz.showWidget("img0", WImage3D(lena, Size2d(1.0, 1.0)), Affine3d(Vec3d(0.0, CV_PI/2, 0.0), Vec3d(.5, 0.0, 0.0))); + viz.showWidget("arr1", WArrow(Vec3d(-0.5, -0.5, 0.0), Vec3d(0.2, 0.2, 0.0), 0.009, Color::raspberry())); + viz.showWidget("img1", WImage3D(gray, Size2d(1.0, 1.0), Vec3d(-0.5, -0.5, 0.0), Vec3d(1.0, 1.0, 0.0), Vec3d(0.0, 1.0, 0.0))); + + viz.showWidget("arr3", WArrow(Vec3d::all(-0.5), Vec3d::all(0.5), 0.009, Color::raspberry())); + + viz.showWidget("text2d", WText("Images in 3D", Point(20, 20), 20, Color::green())); + + int i = 0; + while(!viz.wasStopped()) + { + viz.getWidget("img0").cast().setImage(lena * pow(sin(i++*7.5*CV_PI/180) * 0.5 + 0.5, 1.0)); + viz.spinOnce(1, true); + } + viz.showWidget("text2d", WText("Images in 3D (stopped)", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_simple_widgets) +{ + Viz3d viz("show_simple_widgets"); + viz.setBackgroundMeshLab(); + + viz.showWidget("coos", WCoordinateSystem()); + viz.showWidget("cube", WCube()); + viz.showWidget("cub0", WCube(Vec3d::all(-1.0), Vec3d::all(-0.5), false, Color::indigo())); + viz.showWidget("arro", WArrow(Vec3d::all(-0.5), Vec3d::all(0.5), 0.009, Color::raspberry())); + viz.showWidget("cir1", WCircle(0.5, 0.01, Color::bluberry())); + viz.showWidget("cir2", WCircle(0.5, Point3d(0.5, 0.0, 0.0), Vec3d(1.0, 0.0, 0.0), 0.01, Color::apricot())); + + viz.showWidget("cyl0", WCylinder(Vec3d(-0.5, 0.5, -0.5), Vec3d(0.5, 0.5, -0.5), 0.125, 30, Color::brown())); + viz.showWidget("con0", WCone(0.25, 0.125, 6, Color::azure())); + viz.showWidget("con1", WCone(0.125, Point3d(0.5, -0.5, 0.5), Point3d(0.5, -1.0, 0.5), 6, Color::turquoise())); + + viz.showWidget("text2d", WText("Different simple widgets", Point(20, 20), 20, Color::green())); + viz.showWidget("text3d", WText3D("Simple 3D text", Point3d( 0.5, 0.5, 0.5), 0.125, false, Color::green())); + + viz.showWidget("plane1", WPlane(Size2d(0.25, 0.75))); + viz.showWidget("plane2", WPlane(Vec3d(0.5, -0.5, -0.5), Vec3d(0.0, 1.0, 1.0), Vec3d(1.0, 1.0, 0.0), Size2d(1.0, 0.5), Color::gold())); + + viz.showWidget("grid1", WGrid(Vec2i(7,7), Vec2d::all(0.75), Color::gray()), Affine3d().translate(Vec3d(0.0, 0.0, -1.0))); + + viz.spin(); + viz.getWidget("text2d").cast().setText("Different simple widgets (updated)"); + viz.getWidget("text3d").cast().setText("Updated text 3D"); + viz.spin(); +} + +TEST(Viz, show_follower) +{ + Viz3d viz("show_follower"); + + viz.showWidget("coos", WCoordinateSystem()); + viz.showWidget("cube", WCube()); + viz.showWidget("t3d_2", WText3D("Simple 3D follower", Point3d(-0.5, -0.5, 0.5), 0.125, true, Color::green())); + viz.showWidget("text2d", WText("Follower: text always facing camera", Point(20, 20), 20, Color::green())); + viz.setBackgroundMeshLab(); + viz.spin(); + viz.getWidget("t3d_2").cast().setText("Updated follower 3D"); + viz.spin(); +} + +}} // namespace