From 6e5b633edf229077bb088f2e38bbf01f9135a2ec Mon Sep 17 00:00:00 2001
From: swittl <simon.wittl@th-deg.de>
Date: Thu, 27 Jun 2024 14:33:26 +0200
Subject: [PATCH] added volume io

---
 example/volume_example.py                     |  28 +++++
 rq_controller.egg-info/PKG-INFO               |   7 +-
 rq_controller.egg-info/SOURCES.txt            |  23 +++-
 .../__pycache__/__init__.cpython-38.pyc       | Bin 0 -> 153 bytes
 rq_controller/common/__init__.py              |   2 +-
 .../__pycache__/projection.cpython-38.pyc     | Bin 3604 -> 4308 bytes
 .../region_of_intrest.cpython-38.pyc          | Bin 1505 -> 2410 bytes
 .../io/__pycache__/writer.cpython-38.pyc      | Bin 2453 -> 3569 bytes
 rq_controller/common/io/loader.py             |  15 ++-
 .../__pycache__/__init__.cpython-38.pyc       | Bin 179 -> 264 bytes
 .../__pycache__/json_load.cpython-38.pyc      | Bin 0 -> 2445 bytes
 .../__pycache__/json_write.cpython-38.pyc     | Bin 0 -> 2611 bytes
 rq_controller/common/io/rq_json/json_load.py  |  30 ++++--
 rq_controller/common/io/rq_json/json_write.py |  14 ++-
 rq_controller/common/io/writer.py             |  22 +++-
 rq_controller/common/projection.py            |   9 +-
 rq_controller/common/region_of_intrest.py     |  32 +++---
 rq_controller/common/volume.py                | 101 ++++++++++++++++++
 rq_controller/rq_workflow.py                  |   1 +
 19 files changed, 246 insertions(+), 38 deletions(-)
 create mode 100644 example/volume_example.py
 create mode 100644 rq_controller/__pycache__/__init__.cpython-38.pyc
 create mode 100644 rq_controller/common/io/rq_json/__pycache__/json_load.cpython-38.pyc
 create mode 100644 rq_controller/common/io/rq_json/__pycache__/json_write.cpython-38.pyc
 create mode 100644 rq_controller/common/volume.py

diff --git a/example/volume_example.py b/example/volume_example.py
new file mode 100644
index 0000000..b0574ec
--- /dev/null
+++ b/example/volume_example.py
@@ -0,0 +1,28 @@
+from rq_controller.common import PyVolume
+from rq_controller.common.io.rq_json import RqJsonWriter, RqJsonLoader
+from pathlib import Path
+
+
+FOLDER = Path('./example/data')
+
+
+def main():
+    volume = PyVolume.dummy()
+    print(f'Shape (x / y / z): {volume.shape}')
+    msg = volume.as_message()
+    print(f'Number of slices: {len(msg.slices)}')
+
+    volume_2 = PyVolume.from_message(msg)
+    print(f'Shape (x / y / z): {volume_2.shape}')
+
+    writer = RqJsonWriter()
+    writer.write_volume(writer.get_volume_save_path_i(FOLDER, 1), volume_2)
+
+    loader = RqJsonLoader()
+    volume_3 = loader.load_volume(writer.get_volume_save_path_i(FOLDER, 1))
+    print(f'Shape (x / y / z): {volume_3.shape}')
+
+
+if __name__ == '__main__':
+    main()
+
diff --git a/rq_controller.egg-info/PKG-INFO b/rq_controller.egg-info/PKG-INFO
index fd6b18a..1fb26ba 100644
--- a/rq_controller.egg-info/PKG-INFO
+++ b/rq_controller.egg-info/PKG-INFO
@@ -1,8 +1,11 @@
 Metadata-Version: 2.1
-Name: rq_controller
+Name: rq-controller
 Version: 0.0.0
 Summary: TODO: Package description
 Maintainer: root
 Maintainer-email: simon.wittl@th-deg.de
 License: TODO: License declaration
-Requires-Dist: setuptools
+Platform: UNKNOWN
+
+UNKNOWN
+
diff --git a/rq_controller.egg-info/SOURCES.txt b/rq_controller.egg-info/SOURCES.txt
index 14660b4..19eb469 100644
--- a/rq_controller.egg-info/SOURCES.txt
+++ b/rq_controller.egg-info/SOURCES.txt
@@ -1,10 +1,16 @@
+.gitignore
 README.md
 package.xml
 setup.cfg
 setup.py
+.vscode/c_cpp_properties.json
+.vscode/settings.json
+example/projection_example.py
+example/data/projection_00001.geom-json
+example/data/projection_00001.tif
 resource/rq_controller
 rq_controller/__init__.py
-rq_controller/rq_workflow_client.py
+rq_controller/rq_workflow.py
 rq_controller.egg-info/PKG-INFO
 rq_controller.egg-info/SOURCES.txt
 rq_controller.egg-info/dependency_links.txt
@@ -15,12 +21,23 @@ rq_controller/common/__init__.py
 rq_controller/common/projection.py
 rq_controller/common/projection_geometry.py
 rq_controller/common/region_of_intrest.py
+rq_controller/common/volume.py
+rq_controller/common/__pycache__/__init__.cpython-38.pyc
+rq_controller/common/__pycache__/projection.cpython-38.pyc
+rq_controller/common/__pycache__/projection_geometry.cpython-38.pyc
+rq_controller/common/__pycache__/region_of_intrest.cpython-38.pyc
 rq_controller/common/io/__init__.py
 rq_controller/common/io/loader.py
 rq_controller/common/io/writer.py
+rq_controller/common/io/__pycache__/__init__.cpython-38.pyc
+rq_controller/common/io/__pycache__/loader.cpython-38.pyc
+rq_controller/common/io/__pycache__/writer.cpython-38.pyc
 rq_controller/common/io/rq_json/__init__.py
-rq_controller/common/io/rq_json/load.py
-rq_controller/common/io/rq_json/write.py
+rq_controller/common/io/rq_json/json_load.py
+rq_controller/common/io/rq_json/json_write.py
+rq_controller/common/io/rq_json/__pycache__/__init__.cpython-38.pyc
+rq_controller/common/io/rq_json/__pycache__/load.cpython-38.pyc
+rq_controller/common/io/rq_json/__pycache__/write.cpython-38.pyc
 test/test_copyright.py
 test/test_flake8.py
 test/test_pep257.py
\ No newline at end of file
diff --git a/rq_controller/__pycache__/__init__.cpython-38.pyc b/rq_controller/__pycache__/__init__.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..92399741f3d3f4b877595124493573a15902c93f
GIT binary patch
literal 153
zcmWIL<>g`kf(PwIX(0MBh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6wx*(xR_wJfHn
zFupv$C_61DzdWY6C>g|0&d)0;%FoG3EkfhP$7kkcmc+;F6;$5hu*uC&Da}c>1DX37
Gh#3GY$tKSL

literal 0
HcmV?d00001

diff --git a/rq_controller/common/__init__.py b/rq_controller/common/__init__.py
index c737cfd..49190b8 100644
--- a/rq_controller/common/__init__.py
+++ b/rq_controller/common/__init__.py
@@ -1,2 +1,2 @@
 from .projection import PyProjectionGeometry, PyProjection
-from .region_of_intrest import PyRegionOfIntrest
\ No newline at end of file
+from .volume import PyVolume, PyRegionOfIntrest
\ No newline at end of file
diff --git a/rq_controller/common/__pycache__/projection.cpython-38.pyc b/rq_controller/common/__pycache__/projection.cpython-38.pyc
index 2dbfc5085d556677756493e4a428385bae72688f..84787faff2867b84f7673e065471b45fd6f6c070 100644
GIT binary patch
delta 1222
zcmZ`%TTc@~6yCG!Zrg3QT*_V0El@(yA_B$;5>Zs%5YuQvL7S$tkYeeU=>oQ5Ab~{R
z$0sj|#wTNZF#HT(O^iHf<QMqjn<k#KEjQ7b%r|qs^Uaw#Gv{gBr*QDLVdxUJ>8}sx
zCR;xQy#TwS38vSX&T3x(t7D-pCE>L+R?iyHZe&ec$|fXyXp_I0wV+>Tt*&)~wV~y=
z0<7J&PqI_4+Sq4I7Iv8+GCo(+!8%>0dY|cHk*#Vvq~@`tNQv6G6z$&Lg-$s#F+M!Y
ztd&{5lw7s>{kg2YI$PjrveS0X;dVA_aWSC$goNl(C)cCE+c3E_P>Pi%<E-Eqmn0}W
zzy*Uxum()NAh~l@WmeXt1?d&MmYz#YeYwYK$jZl953*IqSnoR0$}!qkJ^-`QCre5!
z7Sl_{RB?*i3s&06*g3t_f~0vsm=l)0XgRz%te3*qEC*#aH>d1E2G!=w;^N(5bFcZ#
zl5C>vhMi7j*Ufm|cK%Y7m8d%ib(3eToRgx%&6%a;6mtWT#0^u9@-GoeR7yaPs=N-L
zaz}IQY^LDwS~PelP1uYOm3bJagfGuCIVXt%-bFqaBDmV|B#BKXJJ2XlC<zqsVsET;
zFO=#Jzfxai!}f~$9<(0Zz|V-NrwD!Gjc0zl(Q89rAF*c%`U%bvP;4F|7$BhA@fg97
z*!D$WP<-)qLQH%&LZU|Z!!6OK$LF|;iX&A-6{X>DtEd$hj)b2LR2@_c^zk^<O*Jm@
zc63KIH%9qk4(`x|F82;^frshPc+<S)B=h^lkesjl{W2;G0R#y&d>Xg^Ds>%slK*TK
zwtija!){I=^$@IyZ~8rLl#VWwcl>p*Au|4yHg-Vf0*3hES71wQ1R|m}Frr;P?5u?+
z=;(zy@h)&>FiNh|1m_7Z5{w}1crlf{#hoH|caM?d3c*#;VDy{4xb(_0)PO8u|J@sq
nLH7g^YdV5mL61lo-Jyh<TVBi;xk>w~2x8)yaS4XRXJhmaT&*Mz

delta 562
zcmYk1KX21O7>E73j$<db<HnE{nm`h0C6}ZW%7ChrKSD4es8ms@ks+9#TRDy6^z2YP
zAO<QiqZg?IEKG=j0p%00FoVS8fp5VG;?6_iPI~U%=l9;7?nmQI)p(%m1%ZuDZM@U3
zd^Qw-uQ&r+FFct{lkz(t7CCV!y@akpPLc|%XUOcK)K$F#IhEC?$y}Bfk4=@-GNUo0
zW<6)f+05w2rcN4}F<uFh&@F8Ub~Dx1lXV*Qz3tc!Cq=O$w(s9sb%^)Wq5HjqknZga
z!UM-2MzK2>ICuRgPUOd4d*p8Kxw~G3ccfpCVkdWR($9Ya*=FCO7qF%#(8MEkSEG4m
zY@k)PO#Y}LEVX&d630c3WsWk3#c_#amZQb7f^UoSa2dZ8&p`{{o0e8^qh8=e5qteM
zO4>#)o$`k<elwc*Q`c})FH0YRHh3>S*Ddhzwf;z6<(G9bsTgGlaL#b$YyV`ur01UX
zf?yv%7?0((|AT25GdV9OEVfO2Sh{I1^85<NRfbd<(QxF^IHB3W?Aq%*y@B6L%hd(8
tRYVa?5Ct}WFKQwb@~mze=docfSZQuJ4n_&B^D|Q%E$o^dSivK6?Js4(i+%tA

diff --git a/rq_controller/common/__pycache__/region_of_intrest.cpython-38.pyc b/rq_controller/common/__pycache__/region_of_intrest.cpython-38.pyc
index f5698e732fe423453feb6decb77378412c1d5826..4107f998cbedcee04cc429fe8e351e74307c759d 100644
GIT binary patch
literal 2410
zcmZuyOK&7K5Vqa!p3Y3q<TY$I%d#vl!5|bN1Q&$ZNQ;Cx5D+UkjDlLdZO`oV_G4oA
z>@uUyDLHWD3fe=$Ek6N?U*ZEo;*`I@fl%e1JZEjWTrQU%<@&1peY@QxQ2q?7!873g
z!NJwyz+fAi{s@R5f~KTTp=T+}oW4U*=A=Ax`z|G462XOgNQ8U9`ktVVNY6Wkb!4YU
z&GL70I4bgQ24Cf+lDY(u_jRP6N)@BZ)#AZm8=C$Zh_naxnV`}+;(}ovI>&Utq?5R|
zc}Ruxg4^TWena}g6OBW1K>JJ5mrdcnpkhfh4>?F$GLUW20!bj+R?-2<vXykivX!jB
z8q`=3tB1V5D%QZ1b+h{I^o9|}D;_5@E{DJUisg?7C)}*WGB2eH$Hgcwb(m$QEk>El
zHCS(l{y;^U3`fGW;US82Qo;=A#V(k8UXW+d9zxT1fmF02A*mQpS~);j#eq74JR&D_
zN8zLOT*I|Y2dV)B<s(@_f<8VWstw)0I}bnI74q4x+6$i->ggaYp6_ZE<2WwBHAR|A
zbz>A4Sytq`%6c#?1|e96KHM5ljUU2jRE8lQr;!lmQLM8&3-7gct{hWToEPNLUTf?7
zz*M9p2|c2M9#g??1H`&FCA>=x7<k16Y@H%XhtQz>{b0^~ygg^$-tIBO^0Dz$l#3z*
z;5l)s?Q7Je1M7?{CRsLJ23F%!64s?_XS0n(6RaL?lIdZLE;=vx=xDz+<9A?6azOT(
zrA|c?ny@d(k3a15iXAy6KXNOccu6Di=l#;WZz8;pDJ7M2q1Uontps&i$)yHQf~39g
z?se^cH0gjI(j`l+-o6LViKnq+YJ1qdax3qEKO(9H*X%7Do{n^BHWnNl>N1L-nnq>=
zRU0oFk7X{@Dl9W@q*OFDd|YU0e4rx)vY|g2IyH1=TpdTLG~N>#38^d+P4g1*y@pzc
zZPY7B@a-&MjiZW@acziL+}`w4Fi=GnW>V{DD7&ysBVsJ(Sim?9s89X>_!)OOZ6XdW
z-<<OvpWRz@>D4VUFDY40RK9@Gi+Bv+z=|Y*Q9(B${(5XjA&7g-8UmI-iN^ATDpb5_
zKqsGMk0C!m7=fw!15nWxMr?L}@oLwV7p{cJw|H9tro>A@nn>6kNPXwtb&Ok1#t_st
zA7Cm_?;*i#aEX8QE(||}c7B(aAm3Vb?FtyR9GbntU%*(f(~?@?L#|4k0}7!3Wgh?!
zGRRnoTREHXk!+G0?xBAnV;8ce3mNFn^_n1SU0EC0m9?Z^8HBj5melKl5LdNfhfdgr
zuv@M;2G-s!$fGn_30Glu&EAD3>vq32fqPw!aG!dF-x;s{e2UIKMW^+;3OjWRh;bpo
z45jL!03B1K`c>pGgR9q&+(Ys@66=rq$k`<Q26CH7Hjvy#V!ibya6Q*z2_mrue%}Y|
zgf?pFNWF!+*Zpal5qvha@+UB@aTkK|z&g;n6e?SzP+1e=#s?`>HoLnB!OM+ydTW^P
zbNs$S(+tC`5R(-7APn~=QCiQ~^q}s*d8UzzI!idZR0^}JX~k)zbtcOvg-{s4^$Wg(
zbN*NrW2wriaqx?=FNxu8(WwxgSIo2qWA0W6LChfbqx31GowI=Qv!KCflQX)q0nLHN
zt!A%bTnMZ2v`%BI@bkf8QVpV5>Mh9X_Cn^?vyldqMzg5?pY^bww4TKiUv?Gae^$TA
O=Liw|llUy4f%88(fG0u#

literal 1505
zcmb7E&2HQ_5ay4h)&AIt)3iy0Hn|nhp?-iOC<^!x6bRg)Msg4qAQX{nudJWVP}^h|
z>(d(OOZd?A=2zmWKu>;!9@@-M8)svmN`W6|#1Uus&5T~|><k!=zkY|wpCMy^ld?S|
zDnH@0-yvwGdC5|at0+Y!Q_0ygrlk(fnGV(>4KzPuNqB{}>@eZ(&adWp+SI>Hey&?E
zw#{i{d!Wni_!lS`e{?GJu8~FHXQ;)6^bW!_GCdvY=$x&28k?T!>)r*|vF@J-_-26m
z0QI3Bod@Yq?_eNz+}?}DjSyVaye>@MUP`xX+YDMYYo>Lps>+Q_tv_k;1hUGgsm_C~
zh)EVW4{+MA5LSG}6k7>IzLJPSvm<uN4}tEM1TL_qoIr#M^ax^vO9n$+{&{uq!&sZ&
z#_&eHZQyiLHgCt%8pA5f@$>2Hv7NT&`&pKsX2-^oRo)<xO<5Wk=S@{L^%y+Es+lOf
z1#!GTTew)EHf@z6kCuEh9^tcmbc>JukES^%v&|rx;`i5lgu;ro><_N_d#=U(Yk4h@
zWqEi-RdV$ojzl<7&(ff&jm7-SytMwN9gNV-g?e693lc36$;9%(?V!939Z=ch+?$ap
z4hrfzu;-{ySSwzLLV655hR5<fKNFk1$nQl3?L|emN7lU}J_}ZqJGlzh@`ynn4@~-S
z7Z(?10NKJP&hdASFC1U`47%vVWZHnwWD>$2TEiCz>2Rx{(*AWdxzLx=O(I4UXew11
zYcadz(%wg4VnC_-HwreZjK%WtZHBf#Mxr7x?HuaQl1RK=*Xju>EnBlQtk(h)`88H<
z!4=jmYGR{d{h}r}8glbLEJI>fA!11chd>o{<$*#-dO%Cp249jyy#o&js3G7XLLz{E
zMy|Kv<R)Q*%zmWnTeP$H5U{SrF2`qs1F`((Hd&uO#J>_y4<{pPlA1bPxq(tuqvs{b
zBc<NVv$EUqm5}yo?Q6kF+XC$1*KU}XnYEQ^PZ|vrM|VBy6nH{F?Y#w5?m%5ay=X~`
z`NL4~ffW4i$nPYf3+s6`TXatMu-mbt4NS7!*nRAS&IKK^b+GqC=ehW4cZVN|bW3-P
H#Ylbti||?f

diff --git a/rq_controller/common/io/__pycache__/writer.cpython-38.pyc b/rq_controller/common/io/__pycache__/writer.cpython-38.pyc
index 76b818b42aeab0bdecf1a52ee916a3bfdd4414f6..def95e0b1ea611bbf747c17bf753baa721ce50b5 100644
GIT binary patch
literal 3569
zcmcInTW{1x6rQoY*KBU&9xj0r0tT8=D6LeX3PD@MOByvom8?jkWoI^Z>b1kM0|e!%
z@Ea;sPzfITkiRsqec~@vs?>AF@vgHrv?Z#onVrw$IsVRk=giDL7#(dgyng+CulozO
z{veRQYy`Kl#CNcgO!9zv-27Ueg}+u{_Z-jROxn^3D!r;#?bW<mukO`3yT@cjR$nk#
zeQtRT$v2o=dy971n#;Ab-cEjeOZHt`Pp|K-L$s|ri64dcRMb-m?CDnish(Wl`(ABf
z|99Q{VFD^nI%F$<IS6iJiIEUfhN)*gK-QkE995ClE#&D{P20hC#jBZi^%lclyk+f`
zE$g!J!tv^=p_;P!%910pg?I#UOO7HQljDd-5s%3U#FO#_;&H?cIfZyy&LE!X<5@X}
zc(RY@<pSapeY_}7BA)8wQ}Q(8={`Oqmk`e&o|R|O{yF&(;<-LPFP9O|_wfa}f_On*
z#QDCY$L_Ra^>sUG{{ViXKsR^#6RpPwZ4tJ6O4qltxu4H<We?MLY>0e43q}eNb%mdb
zVLA_Ny|@#>p^&0KFy8HU{ik{|XY}yVe9=(a(;+dli`o{JyIA5`toHdn6KvnYn(y0K
zV=!*81HJ}h*y}pliB-^raRe|y)g)F245snt&u{O3xhd7-O?V`pMDSBLh@Nc5&>`H3
z(3>a-6bzwG)aymzrXOuSG4r^(v#09<o%a(VT4*({V#O*|Uggi`j?VdN_MU6$M%;d^
z#16*3p32QeHd7r+i0q;^26F0k9UBSz8Ix~=!3hKhTu-e<$^G6=pn57yl>7!D0@sEq
z?1ee1=BXl|(`p7=dcim#N<L2AF-<e$&n^wQ@XkT5m41r&{Rj52fbSpSN(P3xP7HsR
z9Lgvz{R72AW_q#^V#nfnx`gLk%CqpHlR3I8rF08JFh)j9jC7+wV!l>ECA$!Ij4o)7
zE^1S_4bp+VHYxF%@AEfY@;Ua#!gR3K-ctOa$;B;|<R$M%q44!gxZ8WEfO2^d6!C$T
z#?cZQ7?$NUSqs(Ea+7CpM5fWzA*bIvupmn%^YIimDCM`=OI*$OEor@U5b>AnH|HcJ
zzP+Zo@T_Y$QS=5erldbOOa*d5bAQMSIFFCdVad3<C0qv+B63sqAz}c13Y9oNq(qka
zdHZ^)Op<Il^;HZP(AbbadZlb~i{_VeF5fJ1d05w?g%8&WE}BVz5u$GvXSe7GT)}7b
zVkXS>5|%k(iUfs!9EHx&JQniuh(4zV`V?L)7iYOIPGP{n^5vY<H%go~(!6K<@r4;B
zIcTqhGGSJ+<KrC1Yb6}tLr?e^|J8I|gR^L-pi&JVnnjIz6PDm|_8Ry2ZQG<h+jcPF
z?Kk{6-e!5z{IXlocA!G-1b&=oXDf&v4#F#2#6CrHL+Jun@!2&jS(L2~IWv&B7;Bh<
z$Kwvy@yl)nJ|ki`srsC%{?Y|s5Inkb17+?FEE(hFf5G_w;W-GCGM=9xM{+0QdF7ov
z3&9evxr=nJqSL4lx+z32lDh%5TS7eAZ3k(OnJI0@3FvVl+F=+aZTjyJ3z4dC7}7}*
zXtM-5KmzT?Kzj<%egd?E0&P~XLKSTXK+$d1AE3|x3V3bP374Y92ms_=P8p*Sot%Jz
zmr6|Ln!H7~ZE?K+YBeV8vfGSto4SI*h6hS3+|kZ<9EG|@C&a*iXxQs&diqSgFb){*
a^szD~x_&F2wXbORkLh4l=XInrZT}5tFmmhw

literal 2453
zcmb_d-HRJH5SMn>>$T6mv+pDK(T_`LNwR?)eN6}@BrPFO+@nV+EQV#zlHI-Rha;_X
zz2K)L&_ALnB+wWCOL{8wDgQ#D&>7k5+t`~XN2*1$nvpc~o6%_WW3yQ&(Ej{Y^gA{o
ze<4$BCNOuQ%X>f=VKgEh)qle?;BQ1`Y<U(X%w$$niEYo0t6nvBJcp78gjJaRf-w8J
z;nf&DB5w5!=ph}KD(f)Fetv`cT~loyo*jzxg!i&AO&;(x=9xHCjiOYw56^z!1DOB5
z|1ik}mst;W6`KXjUFh-;AY3E$3~u7b=u49s%zOc8d4=1|dTD5MmRAMdVO8KAR)hH(
z@O4%PeuXuFUjg3a71jg|t85J<je?}b+8}8bB<pMgB&#4<W1FC5i(LY~HRre44)ATZ
z3-Q=ftM>xQzX`J7ClO{`OiZ;mOvNk|{$LghUyl0y@E6q{&cl?8c7%?KpZ5K58kgBj
z55ti+92?N@LzkC;jOm#8WNZMXV-qMG=MkCEj&NYswUi}!)E6~i1m=iX0Wu+C6@Gtr
z?%(b*{-i4&`=_aR)Q{5BZkWg{h@$Ry;c-`jD>sKh?@=(|5?A$7fR;uP7u{YO$7#|H
z)9$JE_+WUZ93QZUneU@*35iHZ#illWy8U)^4)S%bp=vUC!u=s+k=iIkC68DSQ!8&c
zrqH$bXd@%fAdQcRXaF;zYNL~85949PW1eK3eJeyNTvIe*R<x0<BSGJ%WDQ1YS3BUB
z7{@IV!%gVZ>kBTtbCYXmQ>uQh@B&VV4Y2kWWIW|X$gM@|o3JQHy8j-eIl%T(#(fmd
zof4eeQ=ChNwhM5tmcm`y0_9~zr%FGK7#Fj=NrIRwTkvcol8d6B%o1Y{fqznh{>Ff3
zWoc2k&Ux;ljy(Ri799F;Ef@7bd{%<`%De2%FB<O3!G86DVCS3v7mWLeap{Qtxsl5S
zga;)E|6?nZ6nmN?;#1Jl)7KXU*l>(hN!JfNvW#SOOkWucs_2RN8~NRMO`pRDCEe&K
zdIRdG8Sz9}Q7AKI4Wjf|m(qzbttnSP$Ku`GPe$=E7g)&8D?pwyA(vmko;>=CzP)Gb
zk~3HkwKG`S$K(|}2d@FVE{84^*AU)SBnu^PtIUwZl{vC67ouLeq2sP&&BsgV`>O8y
zamq#!jvK!Jcoaml8B=CLv=9fLfWSvgY$2&2!D1lrSi~-p&ynmSxeP>^_!??QuA}VS
z5k0~JE~-GVODsW-VNm$~Y1CRKe(U;cnR=C5R~7ICo+=|5D(gh1iK^n;6NSebo3f|x
g+7zRPr;(xhGTX|zGxhmve3vA?Ne*>rgKnDt0%Y1?i~s-t

diff --git a/rq_controller/common/io/loader.py b/rq_controller/common/io/loader.py
index fe8c5b0..4ce8e32 100644
--- a/rq_controller/common/io/loader.py
+++ b/rq_controller/common/io/loader.py
@@ -1,25 +1,30 @@
 import numpy as np
 import json
 from pathlib import Path
-from ...common import PyProjectionGeometry, PyProjection, PyRegionOfIntrest
+from ...common import PyProjectionGeometry, PyProjection, PyRegionOfIntrest, PyVolume
 
 
 class BaseDataLoader():
     def __init__(self, 
                  porjection_geometry_suffix: str, 
                  projection_suffix: str,
-                 region_of_intrest_suffix: str):
+                 region_of_intrest_suffix: str,
+                 volume_suffix: str):
         
         self.porjection_geometry_suffix = porjection_geometry_suffix
         self.projection_suffix = projection_suffix
         self.region_of_intrest_suffix = region_of_intrest_suffix
+        self.volume_suffix = volume_suffix
 
     def load_projection_geometry(self, load_path: Path) -> PyProjectionGeometry:
-        raise NotImplementedError
+        raise NotImplementedError()
     
     def load_projection(self, load_path: Path) -> PyProjection:
-        raise NotImplementedError
+        raise NotImplementedError()
     
     def load_region_of_intrest(self, load_path: Path) -> PyRegionOfIntrest:
-        raise NotImplementedError
+        raise NotImplementedError()
+    
+    def load_volume(self, load_path: Path) -> PyVolume:
+        raise NotImplementedError()
     
\ No newline at end of file
diff --git a/rq_controller/common/io/rq_json/__pycache__/__init__.cpython-38.pyc b/rq_controller/common/io/rq_json/__pycache__/__init__.cpython-38.pyc
index bb5f48d900fd86908f27dd3f2b51e11b9ca06ed7..533f2156e73fb0d56b4a716687557d50ce8233eb 100644
GIT binary patch
literal 264
zcmWIL<>g`kg854-(>#InV-N=!FabFZKwK;XBvKes7;_kM8KW2(L2RZRrd;MIW+0n6
zm_d`}B_mLYCgUxhphB<W{5+rh#FW$`1V6kevm~|1Pm?8zGYcpkp97RH0vR901?HE7
z<(Yv3D;bJdfE1Yc72s?Ylag8%Q&bpVo?n!mmXlu|Q(Tk`;wR_ll@#UY<fIm%@sjg%
sbMy0JGV?*=Aj4zg<1_OzOXB183My}L*yQG?l;)(`f!tdRasdw`09D#ZHUIzs

delta 141
zcmeBR+RRuV%FD~e00a-(i_$>!V-SH17=auIATDMB5-AM944RC7D;bJF!U*D5gtJvl
zN@`h5QDJ;}eo=N>PJVezW?pegVopwsPi9g~ab`)XZb4#lc4B&JF-TQ%eqKpYe$K>8
L`mCRUMlk>Yl=LGp

diff --git a/rq_controller/common/io/rq_json/__pycache__/json_load.cpython-38.pyc b/rq_controller/common/io/rq_json/__pycache__/json_load.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8d3cf5cbc6c20d1760fd51380de06d162393fbbf
GIT binary patch
literal 2445
zcmaJ?&2Jk;6rY*>@Or&Yoi;>GKcG+w_|WhrE(oEDB2`6INCOD8iZoh0<8;$quRAkt
z8_PMRQco3Eq=J-QkorIHm&}z@{{Sw)f%j%z$8id4&GWoBU+=x&`}nTijtP9f|F+k^
z3bQ|ung4uXuEEgX0ue;ef~3^_vy{O<E4<QAeM*EU{30mBG%Oowqm0t1Y^F_+6A=_~
z*-BfK+#n(pjYmW@WO(4EiJ;$*ZuA0fCL3L9{msnWdBK3|`gZAyOv^7blijK^A(c%w
z_cm3vD|;rd26knhyCJJm8ntJaH}|&W4y=FOzd0~UY6G_hH_L2C_VAJU&zmxO8z#~j
zm9odAPX!h1QIvYpmx1tpVZs-|BR>s+Z#W)J@Y4wRrsE^g1U?47<@i{%fKMJ1pQLRt
zFtN$jy{~jNs4eNyIW=$|z(8&E3J^mM$f46gc+Wg^AKc(?jN|nku=46ITr>99P2L|j
z*Hx7}L6_M;kA~o+i1U1q8_v<2S{D81)HXTq6`9tYKPP|PzW(X9koULMJ^r9lcl$;4
zU|Xvm^1TXNRuzR*3tX=%%WANlS13mXwvq4xa9JPjsTOFBF{BYVLdUPo8@oPJh}kBd
z;6uRJhDw@|8uZ)|JO&>|S`)@~82UXRh8Q}b2jGkY_ETq{8FuJR*fYBCeNR3ix5*ay
zflNGb58JR*LCE;kP!6o8jj}$fXnlOK>#GEAQ_Das?c1h+pyDF$8FdQfj&yLbRuE6N
zfz=@pLOXPYj#rP_<m|w0|E>QP9G#FuGNGU^Gql!S&^59Hk}1cIabC?$AmG->bVsm8
zo>37#3HCi_X=B5-`&BP1xE@x9m!&-;q=B5M6dzU^Qv-!-GhwB2IWU=9xHrm#jruAp
zCC`ODKX=jMwr-%#g9YjWl8Z=QNAd;`aOXLkqjxMD3~i7pmF>-YS*_vPnc*y5ImRw#
z=%UBZ)**3n<7v1-BOx?qj4rufNRz+A#`wxfFkbQ0F>Fv-#MH&s&oG(LT^#f&ptQ@L
zl3&?*vd;jqX@nX7Ffid^1Mvd{eG`GCIq_x)*4#@3Ywjh2KI{VwfdYO&L%_4%(Lg*w
z5;=L((RhuB)|1wLFbS|&Y%F-*o}PhuN9H@`4g$P9lRe0V6CD9$<NH-%pyctp_ifx8
zDFqS0N15%&hY%wpCArBV1WMg)Exr}xSS!`r;30Jd$qaV&4zlke!QAY|HXLSnGt?O)
z%k<o^Qq$r*L!0YSzn?#><K+^ZvW-l;Xi*Vfg!N8g^;2&G>4rynnqHjM3jPXgU4W@B
z9J2Az@?vnES_m$*MZX1v&{Z1K&MdAN4c#xP|Njiz;|u>6W5+@Z%NAPWLhBk#UY+*y
zNe>~w1O&@8$zcXb?mzLTNgiz2C2cZgAf<DF4)9Lo04nE<gU(hJqdM6k@!p45)dxr%
zTpz;hWtKKjdI^bh@mwjalXO|Re#xsoG!EA>7rnUZym(^UM{uXcs#zy!LeY82KVdk&
zeA0C%PIs5ERSmEbaT{}9R$^4(JmDP5R8g<E)}pXSSl`v_S>aKaL0E^*I?kM#ALGXM
zwA=M$QE)zI45q(Ag%vg#92&b;L=&$Qbdt`BlXu%T7>r72YnNbAtpc$PY*|JAW35Su
s{*X!`?zIM3T$~1}w|Vnc9ol&R+~Y?*Ci<+l^>gg}Qw=}|b{6#b7h#ZK`v3p{

literal 0
HcmV?d00001

diff --git a/rq_controller/common/io/rq_json/__pycache__/json_write.cpython-38.pyc b/rq_controller/common/io/rq_json/__pycache__/json_write.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..bbd680e22197406156ea2d812a841eadbb940ca4
GIT binary patch
literal 2611
zcma)8-EJF26rP#=@p}DJr%hYhP$*Ce1`#2k5}*npid3~iAp}sXRix3fGfvjs^*Xcb
zB(~hERO%Jt0Sc0HgVa~y5$2Ywz5-XAGrMt|A0Zs=@%hf~jL)3!+jH`BtK}2;us_GW
zqCv>t2o^s!fUn`oe*z<jpa}`6`DOuwZ<bi89oUozOW28%x`CV4f?DbYURn?85GTS(
z{In4?D7jCBD{9Y(s7d$03YvocNV?uD*qN+#skYa{V*3>X)U|c{n=qH(hDG>D#f4P5
zxjtT3Szks)oDKByBDycLR2FKim)6G{atrQ%-+M49l*|j*)p?MHTQb5QS^QWP$Tz`}
z=1>89PI^>O!Jc`6C2i>l>vtw>;XJbg7jR9ufNR17>;bNeI$&Qk0Q-O&q6xSq+JKwS
ziA{nQ9Pol}ZtOhFvq5>1h%Tgoz5^F#lP`lQ$N`y|9E5dfVfkPOdu`&cZow%(>cch@
zd$ovrllrR4ViV{x?c}2&6v^W}9>fLbSj}7}y_ZziIggSs&pCfd{@J+q<))Ckn`(#e
zW$H;U$@VsL6(No?C|Q;yQk_CkmZsTYGtMxM32dU_d&tY`aI8Q!1Vxl1bA(PVFP?UF
zo)OdaJlvIh2qf#e0A{&}qar#Y0vDs&>TunID_;dukb+L>0Tkqb{dVQ4Rj|G_Wm8Kq
zVf_l_c|`7zAIJuId{ih+_Z0331#shmkb?rWWM@M;&{ke3ZDa1*7Nd07wN(?IR7+?s
zg3+|6E+8<bgAw1H(ww>lIL8DD?a;~96Nfy0s>{rN`qFSNR*uY}KF=H#cQZ$Q3=T5?
z6b{#?Gj>Sei1aQ2WL1b6WLL;C$f=NRkPB-Z@W`w-rBmyWRqAoqbgP#|VZ!raR`4{{
zmxL_fMamQ(W;wo37`!$QW-5ldh33Z2C=_~SVfmSxUQdOo<gw^_P*-47y$+|*ZjmK1
zRQv`2^#+<Z(cDCH9n6X9uV54@I9>m1Wo6GPCbE{d!4T?NZEF6Q&nCCeA?BRrM?Cq*
z_fF8`!p3uY{sJ(ieZ1%kC~%*>Ab+qGpsTQeG#hBLXAY2NSERWWDoLwVs3gs+P)S-H
z)*xxD?}O(WuK`}ocunv;<F&x68?OzXZ@dfOHH_B*uL&NRElsVdjno$xXP02t<wN?^
znL4w}Q+h}%LA2HkMW?9IongE$6F!WKXq)e&Bu+%e7`ZqX$+nEQifxQ7&0~A9C_^2&
z56T)T$%`1Yk>=Xp&5{E8Fn_YE{b-~VD2$InqlfMl?G8hXK+`H@s;>_-RdxFL%fs_g
zuNUvDw}6W>{kPFOC9iieb{oxmXx>NDMe_lg57FF2g9=n07+vqFEDe<k$J&W;Qa}#S
zrn>GzbGQW$1~=y2iyv!do?fZmk8#^mEu?c=!uT@em^`6Zj<sv~)chTNvnFfO$&LS1
z^O1;`l$m-w>p}ZBbMJ(fQO9#FKLqgqS`D<5vu%dV+!M{kS<{^en3LE`;Ky@RL`wkc
zgB%(QLQ`?wgmIiDqq5m_YbB4LK{Cqlcn7_kV05c|uxd+Zd&Q^t<axc~YFX$rwfF)S
z<k;ztI)zW$Y;x<I%AQ%;ZR1ECV1MJ<=RD2CC_%r;IdsOPykjPy!s%C@%7_aah<XPN
z&Uf_*ni`tV!RS^slp~oWoG(ZQr;NhUqi_!5mE&ynXw&N0omOYX#Jer+3}A$fH5&}o
zRd_(xaIPltFD4J|np!A@w3i8(d@uoRtv~qAXy0tL@Y}~@&%0Zm^eg=Tq)Gr%V6Q<M
F{{sC(ltusm

literal 0
HcmV?d00001

diff --git a/rq_controller/common/io/rq_json/json_load.py b/rq_controller/common/io/rq_json/json_load.py
index 2727ef3..d414d60 100644
--- a/rq_controller/common/io/rq_json/json_load.py
+++ b/rq_controller/common/io/rq_json/json_load.py
@@ -2,13 +2,15 @@ import numpy as np
 import json 
 from pathlib import Path
 
-from ..loader import BaseDataLoader, PyProjection, PyProjectionGeometry, PyRegionOfIntrest
+from ..loader import BaseDataLoader, PyProjection, PyProjectionGeometry, PyRegionOfIntrest, PyVolume
 from PIL import Image
+import xtiff
+import pyometiff
 
 
 class RqJsonLoader(BaseDataLoader):
     def __init__(self):
-        super().__init__('.geom-json', '.tif', '.roi-json')
+        super().__init__('.geom-json', '.tif', '.roi-json', '.ome.tiff')
 
     def load_json(self, load_path: Path) -> dict:
         with open(str(load_path), 'r') as f:
@@ -51,9 +53,25 @@ class RqJsonLoader(BaseDataLoader):
         data_dict = self.load_json(load_path)
 
         region_of_intrest = PyRegionOfIntrest(
-            start_point_mm=np.array(data_dict['start_point_mm']),
-            end_point_mm=np.array(data_dict['end_point_mm']),
-            frame_id=data_dict['resoluion_mm'],
-            resoluion_mm=np.array(data_dict['resoluion_mm']))
+            center_points_mm=np.array(data_dict['center_points_mm']),
+            dimensions_mm=np.array(data_dict['dimensions_mm']),
+            frame_id=data_dict['frame_id'],
+            resolution_mm=np.array(data_dict['resolution_mm']))
         
         return region_of_intrest
+    
+    def load_volume(self, load_path: Path) -> PyVolume:
+        load_path_roi = load_path.parent / f'{load_path.stem}{self.region_of_intrest_suffix}'
+        roi = self.load_region_of_intrest(load_path_roi)
+        reader = pyometiff.OMETIFFReader(load_path)
+        volume, _, _ = reader.read()
+        
+        if volume.dtype == np.uint16:
+            data_type = 0
+        elif volume.dtype == np.uint8:
+            data_type = 1
+        else:
+            raise ValueError('data type not implemented.')
+        
+        return PyVolume(volume, roi, data_type)
+
diff --git a/rq_controller/common/io/rq_json/json_write.py b/rq_controller/common/io/rq_json/json_write.py
index 4099fa9..2628163 100644
--- a/rq_controller/common/io/rq_json/json_write.py
+++ b/rq_controller/common/io/rq_json/json_write.py
@@ -2,13 +2,15 @@ import numpy as np
 import json 
 from pathlib import Path
 
-from ..writer import BaseDataWriter, PyProjection, PyProjectionGeometry, PyRegionOfIntrest
+from ..writer import BaseDataWriter, PyProjection, PyProjectionGeometry, PyRegionOfIntrest, PyVolume
 from PIL import Image
+import xtiff
+import pyometiff
 
 
 class RqJsonWriter(BaseDataWriter):
     def __init__(self):
-        super().__init__('.geom-json', '.tif', '.roi-json')
+        super().__init__('.geom-json', '.tif', '.roi-json', '.ome.tiff')
 
     def write_json(self, save_path: Path, data_dict: dict):
         with open(str(save_path), 'w') as f:
@@ -61,6 +63,12 @@ class RqJsonWriter(BaseDataWriter):
         data_dict['center_points_mm'] = region_of_intrest.center_points_mm.tolist()
         data_dict['dimensions_mm'] = region_of_intrest.dimensions_mm.tolist()
         data_dict['frame_id'] = region_of_intrest.frame_id
-        data_dict['resoluion_mm'] = region_of_intrest.resolution_mm.tolist()
+        data_dict['resolution_mm'] = region_of_intrest.resolution_mm.tolist()
 
         self.write_json(save_path, data_dict)
+
+    def write_volume(self, save_path: Path, volume: PyVolume):
+        save_path_region_of_intrest = save_path.parent / f'{save_path.stem}{self.region_of_intrest_suffix}'
+        self.write_region_of_intrest(save_path_region_of_intrest, volume.roi)
+        writer = pyometiff.OMETIFFWriter(save_path, volume.array, dict())
+        writer.write()
\ No newline at end of file
diff --git a/rq_controller/common/io/writer.py b/rq_controller/common/io/writer.py
index 73f1c40..be86283 100644
--- a/rq_controller/common/io/writer.py
+++ b/rq_controller/common/io/writer.py
@@ -1,31 +1,36 @@
 import numpy as np
 import json
 from pathlib import Path
-from ...common import PyProjectionGeometry, PyProjection, PyRegionOfIntrest
+from ...common import PyProjectionGeometry, PyProjection, PyRegionOfIntrest, PyVolume
 
 
 class BaseDataWriter():
     projection_name: str = 'projection'
     projection_geometry_name: str ='geometry'
     region_of_intrest_name: str = 'roi'
+    volume_name: str = 'volume'
 
     def __init__(self, 
                  porjection_geometry_suffix: str, 
                  projection_suffix: str,
-                 region_of_intrest_suffix: str):
+                 region_of_intrest_suffix: str,
+                 volume_suffix: str):
         
         self.porjection_geometry_suffix = porjection_geometry_suffix
         self.projection_suffix = projection_suffix
         self.region_of_intrest_suffix = region_of_intrest_suffix
+        self.volume_suffix = volume_suffix
 
     def write_projection_geometry(self, save_path: Path, projection_geometry: PyProjectionGeometry):
-        raise NotImplementedError
+        raise NotImplementedError()
     
     def write_projection(self, save_path: Path, projection: PyProjection):
-        raise NotImplementedError
+        raise NotImplementedError()
     
     def write_region_of_intrest(self, save_path: Path, region_of_intrest: PyRegionOfIntrest):
         raise NotImplementedError
+    def write_volume(save_path: Path, region_of_intrest: PyRegionOfIntrest):
+        raise NotImplementedError()
     
     def get_next_projection_save_path(self, save_folder: Path) -> Path:
         return self.get_projection_save_path_i(save_folder, self.number_of_projections(save_folder) + 1)
@@ -45,6 +50,12 @@ class BaseDataWriter():
     def get_region_of_intrest_save_path_i(self, save_folder: Path, i) -> Path:
         return save_folder / f'{self.region_of_intrest_name}_{i:05}{self.region_of_intrest_suffix}'
     
+    def get_next_volume_save_path(self, save_folder: Path) -> Path:
+        return self.get_volume_save_path_i(save_folder, self.number_of_volumes(save_folder) + 1)
+
+    def get_volume_save_path_i(self, save_folder: Path, i) -> Path:
+        return save_folder / f'{self.volume_name}_{i:05}{self.volume_suffix}'
+    
     def number_of_projection_geometries(self, folder: Path) -> int:
         return len(list(folder.glob(f'{self.projection_geometry_name}*{self.porjection_geometry_suffix}')))
     
@@ -53,3 +64,6 @@ class BaseDataWriter():
     
     def number_of_region_of_intrests(self, folder: Path) -> int:
         return len(list(folder.glob(f'{self.region_of_intrest_name}*{self.region_of_intrest_suffix}')))
+    
+    def number_of_volumes(self, folder: Path) -> int:
+        return len(list(folder.glob(f'{self.volume_name}*{self.volume_suffix}')))
diff --git a/rq_controller/common/projection.py b/rq_controller/common/projection.py
index 979e8f4..ea5c1ee 100644
--- a/rq_controller/common/projection.py
+++ b/rq_controller/common/projection.py
@@ -114,6 +114,13 @@ class PyProjection(PyProjectionGeometry):
 
         return message
     
+    def get_projection_geometry(self) -> PyProjectionGeometry:
+        return PyProjectionGeometry(self.focal_spot_mm,
+                                    self.detector_postion_mm,
+                                    self.detector_orientation_quad,
+                                    self.frame_id,
+                                    self.focal_spot_orientation_quad)
+    
     @property
     def detector_heigth_px(self) -> int:
         return self.image.shape[0]
@@ -128,4 +135,4 @@ class PyProjection(PyProjectionGeometry):
     
     @property
     def pixel_pitch_y_mm(self) -> float:
-        return self.detector_heigth_mm / self.detector_heigth_px
\ No newline at end of file
+        return self.detector_heigth_mm / self.detector_heigth_px
diff --git a/rq_controller/common/region_of_intrest.py b/rq_controller/common/region_of_intrest.py
index 1636edd..6a3115b 100644
--- a/rq_controller/common/region_of_intrest.py
+++ b/rq_controller/common/region_of_intrest.py
@@ -7,15 +7,15 @@ from visualization_msgs.msg import Marker
 class PyRegionOfIntrest():
     def __init__(self, center_points_mm: np.ndarray, dimensions_mm: np.ndarray, frame_id: str = 'object', 
                  resolution_mm: np.ndarray = np.array([0.1, 0.1, 0.1])):
-        self.center_points_mm = center_points_mm
-        self.dimensions_mm = dimensions_mm
+        self.center_points_mm = center_points_mm.reshape((-1, 3))
+        self.dimensions_mm = dimensions_mm.reshape((-1, 3))
         self.frame_id = frame_id
-        self.resolution_mm = resolution_mm
+        self.resolution_mm = resolution_mm.reshape((-1, 3))
 
     @classmethod
     def dummy(cls):
         return cls((np.random.random((3, )) - 0.5) * 20., 
-                   (np.random.random((3, )) - 0.5) * 10.)
+                   np.random.random((3, )) * 10.)
 
     @classmethod
     def from_message(cls, msg: RegionOfIntrest):
@@ -45,6 +45,12 @@ class PyRegionOfIntrest():
     def number_of_rois(self) -> int:
         return self.center_points_mm.shape[0]
     
+    @property
+    def shape(self) -> tuple:
+        shape = self.dimensions_mm[0] // self.resolution_mm[0]
+        return (int(shape[0]), int(shape[1]), int(shape[2]))
+
+    
     def as_message(self) -> RegionOfIntrest:
         message = RegionOfIntrest()
         roi_list = list()
@@ -52,13 +58,13 @@ class PyRegionOfIntrest():
         for i in range(self.number_of_rois):
             roi = Marker()
 
-            roi.pose.position.x = self.center_points_mm[i][0]
-            roi.pose.position.y = self.center_points_mm[i][1]
-            roi.pose.position.z = self.center_points_mm[i][2]
+            roi.pose.position.x = float(self.center_points_mm[i][0])
+            roi.pose.position.y = float(self.center_points_mm[i][1])
+            roi.pose.position.z = float(self.center_points_mm[i][2])
 
-            roi.scale.x = self.dimensions_mm[i][0]
-            roi.scale.y = self.dimensions_mm[i][1]
-            roi.scale.z = self.dimensions_mm[i][2]
+            roi.scale.x = float(self.dimensions_mm[i][0])
+            roi.scale.y = float(self.dimensions_mm[i][1])
+            roi.scale.z = float(self.dimensions_mm[i][2])
 
             roi.header.frame_id = self.frame_id
 
@@ -66,8 +72,8 @@ class PyRegionOfIntrest():
         
         message.region_of_intrest_stack.markers = roi_list
 
-        message.resolution.x = self.resolution_mm[0]
-        message.resolution.x = self.resolution_mm[1]
-        message.resolution.x = self.resolution_mm[2]
+        message.resolution.x = float(self.resolution_mm[0][0])
+        message.resolution.y = float(self.resolution_mm[0][1])
+        message.resolution.z = float(self.resolution_mm[0][2])
 
         return message
diff --git a/rq_controller/common/volume.py b/rq_controller/common/volume.py
new file mode 100644
index 0000000..3ac025e
--- /dev/null
+++ b/rq_controller/common/volume.py
@@ -0,0 +1,101 @@
+from numpy import ndarray
+from numpy.core.multiarray import array as array
+from rq_interfaces.msg import Volume
+from visualization_msgs.msg import Marker
+from .region_of_intrest import PyRegionOfIntrest
+from sensor_msgs.msg import Image
+from enum import IntEnum
+
+import numpy as np
+import ros2_numpy
+
+
+class VOLUME_TYPES(IntEnum):
+    UINT_16 = 0
+    UINT_8 = 1
+
+
+class PyVolume():
+    def __init__(self, array: ndarray, roi: PyRegionOfIntrest, data_type: VOLUME_TYPES = ...):
+        self.roi = roi
+        self.array = array
+        self.data_typ = data_type
+
+    @staticmethod
+    def get_data_type(volume_type: VOLUME_TYPES) -> np.dtype:
+        if volume_type == VOLUME_TYPES.UINT_16:
+            return np.uint16
+        elif volume_type == VOLUME_TYPES.UINT_8:
+            return np.uint8
+        else:
+            raise ValueError('Datatype is not implemented')
+        
+    @staticmethod
+    def enum_to_numpify(volume_type: VOLUME_TYPES) -> str:
+        if volume_type == VOLUME_TYPES.UINT_16:
+            return 'mono16'
+        elif volume_type == VOLUME_TYPES.UINT_8:
+            return 'mono8'
+        else:
+            raise ValueError('Datatype is not implemented')
+        
+    @classmethod
+    def dummy(cls):
+        roi = PyRegionOfIntrest.dummy()
+        array = np.random.randint(0, 255, size=roi.shape)
+        data_type = VOLUME_TYPES.UINT_8
+        return cls(array, roi, data_type)
+
+    @classmethod
+    def from_message(cls, msg: Volume):
+        roi: Marker = msg.grid.region_of_intrest_stack.markers[0]
+        center_points_mm = np.array([
+            roi.pose.position.x,
+            roi.pose.position.y,
+            roi.pose.position.z])
+            
+        dimensions_mm = np.array([
+            roi.scale.x,
+            roi.scale.y,
+            roi.scale.z])
+        
+        frame_id = roi.header.frame_id
+        resolution_mm = np.array([
+            msg.grid.resolution.x,
+            msg.grid.resolution.y,
+            msg.grid.resolution.z])
+        
+        py_roi = PyRegionOfIntrest(center_points_mm, dimensions_mm, frame_id, resolution_mm)
+    
+        data_typ = msg.datatype
+
+        array = np.zeros(py_roi.shape, dtype=cls.get_data_type(data_typ))
+
+        for i, slice in enumerate(msg.slices):
+            array[:, :, i] = ros2_numpy.numpify(slice).reshape((py_roi.shape[0], py_roi.shape[1])).astype(cls.get_data_type(data_typ))
+
+        return cls(array, py_roi, data_typ)
+    
+    def as_message(self) -> Volume:
+        message = Volume()
+
+        message.datatype = self.data_typ
+        message.grid = self.roi.as_message()
+        message.slices = list()
+
+        for z in range(self.array.shape[2]):
+            message.slices.append(
+                ros2_numpy.msgify(Image, 
+                                  self.array[:, :, z].astype(self.get_data_type(self.data_typ)), 
+                                  self.enum_to_numpify(self.data_typ)))
+        
+        return message
+    
+    @property
+    def shape(self):
+        return self.array.shape
+                                                         
+        
+
+        
+
diff --git a/rq_controller/rq_workflow.py b/rq_controller/rq_workflow.py
index 662b0c7..b729565 100644
--- a/rq_controller/rq_workflow.py
+++ b/rq_controller/rq_workflow.py
@@ -51,6 +51,7 @@ class WorkflowNode(Node):
         reached, cost, _ = self.hardware_interface.reachability_response_2_py(response)
         return reached, cost
     
+
 def main():
     rclpy.init()
     
-- 
GitLab