From 0a47c9c8c6e9aa65b83d3b4602867e5560f83b7a Mon Sep 17 00:00:00 2001 From: Cris Luengo Date: Thu, 12 Dec 2024 00:54:26 -0700 Subject: [PATCH] Add a measurement page to the PyDIP User Manual. --- doc/src/PyDIP/00_toc.md | 1 + doc/src/PyDIP/basics-16.png | Bin 8745 -> 0 bytes doc/src/PyDIP/basics.md | 54 +--- doc/src/PyDIP/measurement-1.png | Bin 0 -> 8496 bytes doc/src/PyDIP/measurement-2.png | Bin 0 -> 12427 bytes .../{basics-17.png => measurement-3.png} | Bin doc/src/PyDIP/measurement.md | 297 ++++++++++++++++++ 7 files changed, 300 insertions(+), 52 deletions(-) delete mode 100644 doc/src/PyDIP/basics-16.png create mode 100644 doc/src/PyDIP/measurement-1.png create mode 100644 doc/src/PyDIP/measurement-2.png rename doc/src/PyDIP/{basics-17.png => measurement-3.png} (100%) create mode 100644 doc/src/PyDIP/measurement.md diff --git a/doc/src/PyDIP/00_toc.md b/doc/src/PyDIP/00_toc.md index 8febb1ab..f5da2d99 100644 --- a/doc/src/PyDIP/00_toc.md +++ b/doc/src/PyDIP/00_toc.md @@ -29,6 +29,7 @@ These pages will get you started using *DIPlib* within Python. If you are famili 1. \subpage pum_indexing 1. \subpage pum_tensor_images 1. \subpage pum_filtering +1. \subpage pum_measurement 1. \subpage pum_display 1. \subpage pum_numpy 1. \subpage pum_dipjavaio diff --git a/doc/src/PyDIP/basics-16.png b/doc/src/PyDIP/basics-16.png deleted file mode 100644 index e3654b7f8f35ed605e7d3c129b19e06d249ff2f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8745 zcmZvi1z1z>+sCIMsdNb_GP*+=B}R|j04V_(pg6{;2`UPLfD8nrV}LLiFrwrl6wo}K5MC%*Uh{@e#^WnpxIftLXU0$ngM)`x>YXHb_?(XnY&^9P`(({UBs0j&w#+YzUZs6%Cc9buXtfF zet{Ztaz6jrK-M?lj$E%){Sa^xdOu@_KoE$L{qOq>C_9G-1QJX)(bu)PpR<v_j^fNS4} z#d$4l3AT@c8=vdVj^_4XzU3oH_nZ}s}x$m25(b3UI2RsnEEF*H_1C;Bd&2#G?e!CpaGKc9%bLoYI zXnuXPZLXxE5_=*JG_K^9(bMQCS3=oBKuX0JX%f2qD#DTjFdX=dgM4K3{% zP@Iq{Np?u$l|&@V1vQ`C>c^FG1TQ7tww)c!`l5|wl$X+ShY>5J^V%a;C*Zn@i;Mj> zzZ*O{*e<;h_s+hwe_hv?rgC40&cDxzNJ{Q#1*>WLnQ#j7{as*3Q@Lb4n9G$nz#xi#@`AR$ugJ!uNlB! zaa-HrKG$X#(-L`4`D}&_>)QTCQ&XJS>9OuauYB~kPzpEOVVNI zz9q`1S%w~H^E){1#H^*h(CT)Z%dm-`w85nS`7~*X+SL`ZD+9qW*8S~y88Kz&0tyOl z;uW>Xrk)A@P`wwAjUbd-@|%e`q(?tLN-~>3Fa$bMiK{rthbIN3P6<`;1QCY$R%YHX zXH;{D7_#ong!65xO+5HyzQq^++8-V;YQY0Hztr`v|wPo;G|cS987C!SdnYr-><;X9pPB3Yb> zvT2XmjFE`+^l`GNdrf6Zi4BQkmo*8lnWHslkDOSEIq=)Dmgd0GSXm%-_Lr#IZd0jt znc6tA3o6GZMP7(CcM~u2Yta{HitVM695_`{H7MqP{j0bL81eoDS0Q8LPrY6_GINiq1?nX95{G&n9Tc5 zwi0KDpTf#d2;zc6Nx3xJgx#q`i@RbZgqKj=;En^RIeH1%C9_Feth`JADWMKc2ok;y z+xas$sO*wG$4f9B9Nd|h4vXrbOA24Yh-g+|^HWD8KP-rK$I6&(AsmMpVTwb}NTVV4 zIFF)4u98PaI5adh6_k{I z{JGv!AxmL#_6gYyFI_yhB&(K75S8?alBk76Xr3@@oRyMV%*Vkz$m-y&v-bV2&CZwLZw7$YXrF8{WA5FX zZTxn;o-;0#LmJ#NG=^q-4xY3$I z!4Haq^2^E?u3x`yESgeUS!pPmqHAt`K{4xmSy@@6(uhY5cuUfQA>IS#lZ+mq@4qK; zFmdPQ?7{9zy>`=fpQaZYZ5SM^8vX3q8PKOspD6GA3p3Lv^*cUROQR3SHRD!DeOr&z zic0pW&!5jfeE1LqdW-(j${hAb@7f;4-OJb4$kkO~Z*}5nLxTptnxBrn`=^L6)J9pX z6E>1Yp1W=JTuzT4Uq9J<+tjOh((4<_E@qMQYeYN%2ahNmXnl%1sm<@P;-qDRkcyze z;x&Hw<@|6ap4y`BjT2>tXzC~@B2mTb3S%MJkWLT;$XP%&^)6GJp(LX9;e>p%;*ONs z{aY#WdsUp><(bn*Cm|1ZrYc&;?w5UZR%1aqYGy*Yu772B;DVY$toU{I%@7hvA9pmx23#N|N&PazX5zjkTEpkN8%0wG~2bfj^ zL${2fs0vJxA#MaSKj`~5ox`qv{5>2s_&aGQvL+RFtci4lyn6Z$vA)kKvKtx#v&?QS=;gnq7KQFgGa>%{OYbwWtz zV(f??(s-}R)}~1|uAac{B&R%j!aKteJ)ioI+s(|8Xrge@(Rf^!$!MtfXvU2RgT*&A zAl9B5>4|uIeuvn5Nfr_>eS*86+O=RgaHS)n$cAUbO}Ko5uw<=m_Etg7NRHW%tu3;o zg8dIUyY?Qsv?OFnBQfr$CDS~aP%{ELtbyAiE$DqDmTi#FhVR@gQPI{f@(#n5i4-~8 zaNDcb3`wScHDh&5ABgR};PT_PJdG=?EcQFPv1QZuqDm#-LrcZq4q(l9sM6NJ8nscZwvh`9 z{9-Q&uV!o_3h_*vLoI!5@G30@SnJSipm~VC>k=E^j8-j{NBMIG4oA&>Lw#9T|BJji zQYVHTXKj+D$O$5ve+@YSfm!tEG_XU_fH%W!e&s~ zM$>7D2>Nk5dt|Gi8M?hpXbQN}B2vJOW5|@2Wn;3zt+P&Q0Vbo~hGVL^83ahn!1Q#E zri!L!TxF%Kl9EzBpvrbtF6Th@=}~J_Z-oH2Mj)KK)1S9nH6O_WkBCKtY;}qGAN|=> z?*Q!Iz{CWWNR+??1tGk==sejOqVoLH9y{4xuFkc#b7WqleONR9*WobyX%i9WY) z(>1lMB6@#Dvo*LRqgmzVu!n$G~v;R2t$hDPlDYYC0( z4U8eb*LRO;=md*i#CSTSuNq1xNVhc@m#p57ybQzkycx0?i@$l5>Ji%#mGM~MC3ktI^a%Dz@8vTCcaYO_EKYj8D( zj4r_yngXq;rtkC(B^`z2bz652YVu#})Ef($t=V)!xtu11ZZ!#{J@VOz)v(-HQ?)o} zL`-Y%nmJ=Sc-pZhr#hU_l$w&wq{hlg5KS1!z=O#ilj8X(OooGC5Y!rKE?vOQ2V*RO zA9kIF7ClmD_{^ChYZ*PBWfd3iS>#xnK5{bGdI|ysg_amB9l||M*q2+V1`&Zy2@7)9 z(rcOe<+`ShH8&-dVMH0J*AV?4b6ddopk5x{$HXVfWHH-arszbyxq%XkS|#P5Tig>I zDz<`pMOEP>)x;oD??t^@CInLp@ik%L%*Dx^Z=Wg5K1gghUuu)=I9qSgH-1JH&i>$o z0H7(MZbq3Fk(_U)^<#~>iS=z9_1NTOWWZmQC{Zrye1YSZsIPZf$Pbr@fQJkHr1zxe z2DTVHuRLeo=u;8wF+@*3iG<4e@}GAv#=fU$+rIpCK%`Du za#fp)>p>di9MN-PKR4Z~r$Y&nMX8A~cFaj#VM;4X7m*ezjOEjd6@7!1i5qs7j&(L& zEyNFOxdpH@a<`ba6N|)^7fWSprQJ->qI#rxH@h3D*@2k6NGXQ2!0mLs9K=^RfJ@7COfqdh2H=f1Q=KWClAo`3;IamN2TK8Z~cDh=&eGns9iidPn5xo zvF2wYSkkz46@IdQ0cONk-lw-?hZSZE94D_j!%8A~B*51TD8hvgFG9(>CNx0qoT?X9 zl%7Z@2t&5C{=7`K+p8=MRMIHR9;s2w9X3qrA|McaJUlwKww&2GTwsUGqfG-qeBRX6 z;cII-3kwVD$Ai|VvB3W4l)5pq_V(Le$!K)G>BZ%H$(3JD6m1FVgdw5BnksAL7BQ2O z_h`%HX!VZvr?CpBMgG%yxiIWvUI@C6w7uBxetL4O7QD_c>-zEeGl5VZfk(g2a7o)= zsT++30v;X4A{zxIsE$V6Ft+-BaJ(adB3{_&W|jkD1zuUFzK^vFuH8oLwPnRg&v`rc`de z^E;E0l2S?}w!H4wTKRJwNck1Cv=TNq{U2S3n^tDuwm zu(7DGw!l^1c*Mhp8o@4-X%Hn|&SCgd|Ln=kEoh+d^`t{f?$u(2VwJ;pY)+ynUB~+~ zEl=xNhXGYz{njL)ik@z6i{n;MR*tIm{&XCpK4^t>O_HldC=TnQQ`59<_^(mkKe%d< zcMU6LS9ygi&aETilG=kUVNo$LR;+$Cknp@eIamSxg$Fyky9Fc?Du!M(p*Y@?2tW(UHod|yN&MM8`wI;D`924swktm%6w>WP1R3XwKZ~a z@_<>`dZ~0PxK^f?nL2wi=V+bZ_(MtU^cyxDHxYY)-}1b%`VJa#)otL(bi4OyHZ=SS z9q^kOl)i%Upk}`K^+0R#SPjL*r>3Yl(4nlCsR4Y_=6`rOh_JBthz;G1jT0(ZAAq31 z?r(;F2S`ac1hD6hVJyNlNQAv`bvCJ^2^p8r-~+^O@2oLUI^J_o<^c?HPzZ;(OF(1s zI3DxpS4^!@TUZ%Xp*ng~s)AU()lB_WTtVlTdlZ|apoVCzbaz}lOI4?_H$nf#`n5EX z5E6HxDj6&nW@UEfQTmfM8_BKCm>vN8ZR~-}RBLp`q7j z_!1Vo*2I7(E~lFpHbxCwm%x|ye_brKW=&TFHaM)!S+VRn2$i7DZY7e^ou(YHxLsK7xK_@36sm&5u}w+C78lsiUEgO;E&W3A+K4#(*iCz^)ipOBkDscXbt1FTaxOF zi+L1B6B$7bUDCxErdG)!t6M>Rj43~W^d109Ic!7Oj<8jkCfEB>7-?b{?3|J9g*q$4?Q#UUeBi(=VzuDA8{li z;lp?N_cVSBt_M1(=WkMk$+;!chcl+n&!)5KX17S@Cio4DutPPkl0uRE-$ZLkNJ9J zd%K~Mvhr;0xXb^;FwTI|J>3rX)?zy5yzbs@nRtEY_yZM$P9h&q;gY=>W9RbV++zRB z4hn@QXya@AUp)pqb8c>KW_C8@&K)tpPJ=*zh1d2buBNIfI7tVb1OiQyFIcL=o!HVU9KsI)Ei_p(z zqEE}pBtbg=V!V_8FkZ=qFCruHfeisqk?v`=w!9t%4Au9nJ9%A~HrChM zfBljN-MW9Dhffv#`O>$yz5+laU{cbh9Tcn1iy&w3y*4TtKg=!895Zgys|C_TA)3e6-O6D5QXq(AO{ntw1keZ;oXAXsST_VZ8T8P-G?!ulTXvr^jh zrO4c%xOA&b!h%QYFyKX+av#N{SiWYx6XnABX_KgPr2Nt8*`1)!G(y(ITlzrX_1%DmTj`$i(XoO z;uwG%h%-;qQiouQk_Q<28KHg!+)i2zC_@RwW53?wV5Xw4xZrZRpr*)l>SxXcoQ<+{ z^)Qt9$E_kWgb&&B$9CJy3YHD`w zxOt=mTB|+IoD(yBcB<#jg9+BR92SuV^-+9tZ3{u5E9a${R%Z;Y%8IVT0t%HPbUl80 zKN7%MA8}-lpQvQDcAl=nf6v@GoQ5s~y^0@v>AVRMRB5E5GrPNIC6dGnXP7e`luj$+ zOda_nEP8C^rfw~Q@p69=2_|`VWn_H*qPd!SudPock|Km~NEc~j{a4MT@nV8{a-Qo~ zt;?S3E3_`OXZ3ep6R*Y0+(9rRwVVhKQ2#us!!!PgxQgbC+6X zvOH4%y!fB2v+9z|pttnTg$M}0tBT8Zr`fBlaNFcUzLt8<>=YPGR0VQ#fVBm3xsxU#cCTeH;8|5bY7pDB9d zaSpLvGe0+l=Ba!1SAs0ZufU*#nH~z0X2Y5EmrAxieuLBn?$aDVX z_N24EO~-X%6&au?gO~%GoA9(Qj~87Ye?|LdQ|Kk{vGSGzy=Q+D`$xwst{s8>*Hssf z{)U3>gWXvma3ZJ`}T)s=%&4k&Eh1oz)aPMX}RetDTS@EL(SJ6rT z*PJgs{R_PR)133G=CDAZZl)+z->>?rOz&)hx61ep=?s`hkI0 zLHUi1e22d`I^y|M|JL*VqNg=SEl2_j8$ahE#UfWDO7-h~s#iBEAl}|mpqcp$*LIxm zM{HM@2|$~qT|e?yHJxaH@`*%x0J?agP~^YGKY*~*2my={%)6d%N;i0REok7wHZFMP zX_)DEr*8-8tHpG4bBWpu$KvNVjC^9h^yg-*%B8}=q%%5BPJBRI^HkO2c`r~qdt4en zemLt0`;UcXWrzYeR1NM5$JfNkU!f!IE63u)9RLr&defeOG1aD#b)t9DHXX|=%82C%R9Vqfl9RoJojMf2BfFHd&Fyb*|2 zz>aiZk{UfJG)i<#yuO#Y3yiSi#)sc|wLxo+5qGC=Zp>w zA%KN{TBCLJOYhakk6#-A^!K8-qI13{qXLVi2YOmbvD6{~_g5ac*mw8vQd)yR(zm1$ za=Mu)5L;ocu8Eo(@7ZxGKCPXdF`q^0|Cj$UMnCPY4n2M-?c9LrBf-}ME?#T_+zpVD z4Ga#3dyiN4PO#{ni~V9LfuZo!j@+(l2sP40xk%ZfeqwlQYisLYn)*PA8bCNsytYL* zR_>58buI9JB1psA?3b*|jy1yWGzP6fOidYq;Scb{p?{||O}wK220J+heg7!*^D#>F z*Lvf|_tlBlYC)_2cs}jZ{r*4~09eN8gOeQ>FMXL}Zr*M$m9%;{;jN_+YJ^v<>NP5( zm`$WAPAvfbW@l&TN|qTOm=NyMtN&DjJX8i12$NMS!NrH|r396b(Ou20kZngI!rRxk z12B%VF7IRdwNLqhU11a){Pwe``T~$Mf7)Fc4-5-k%nb%`@ALo3Ee*4JF=oWQ;NW2Q zZyz65OEbE=yW7`zm{jt^FXpNm)My=1T@dxZI!Cn;)r` zQ)Hso|8o>0_umEdia=;XqEMf|d;UU`fFh5wN{n}3nxR3 x2 ``` The `dip.Measurement` object `m` can be indexed in three levels: the measurement name ('Statistics'), @@ -309,18 +273,4 @@ np.asarray(m[30]) # returns a 2D NumPy array for a single row of the measuremen np.asarray(m['Solidity']) # returns a 2D NumPy array for a column ``` -We can paint the objects with one of the measurements, which can be useful for display: -```python -c = dip.ObjectToMeasurement(b, m['Solidity']) -c.Show(colormap='viridis') -``` -![Image where the intensity of each object is its 'Solidity' value](basics-16.png) - -Here we plot size vs solidity: -```python -import matplotlib.pyplot as plt -plt.scatter(np.asarray(m['Size']), np.asarray(m['Solidity'])) -plt.xlabel('Size (μm²)') -plt.ylabel('Solidity') -``` -![2D scatter plot of size vs solidity, there's no correlation](basics-17.png) +See \ref pum_measurement for a more in-depth look at the measurement object. diff --git a/doc/src/PyDIP/measurement-1.png b/doc/src/PyDIP/measurement-1.png new file mode 100644 index 0000000000000000000000000000000000000000..2563705e49e310ad98599398013b05be8dfa8000 GIT binary patch literal 8496 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 literal 0 HcmV?d00001 diff --git a/doc/src/PyDIP/measurement-2.png b/doc/src/PyDIP/measurement-2.png new file mode 100644 index 0000000000000000000000000000000000000000..a3aa239a0108e5de2aa92cb0ca32a1979a70e68b GIT binary patch literal 12427 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

jWmg~zI_BgkvgarHGsIdoHxEN!$ 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 literal 0 HcmV?d00001 diff --git a/doc/src/PyDIP/basics-17.png b/doc/src/PyDIP/measurement-3.png similarity index 100% rename from doc/src/PyDIP/basics-17.png rename to doc/src/PyDIP/measurement-3.png diff --git a/doc/src/PyDIP/measurement.md b/doc/src/PyDIP/measurement.md new file mode 100644 index 00000000..9d925c0b --- /dev/null +++ b/doc/src/PyDIP/measurement.md @@ -0,0 +1,297 @@ +\comment (c)2017-2024, Cris Luengo. + +\comment Licensed under the Apache License, Version 2.0 [the "License"]; +\comment you may not use this file except in compliance with the License. +\comment You may obtain a copy of the License at +\comment +\comment http://www.apache.org/licenses/LICENSE-2.0 +\comment +\comment Unless required by applicable law or agreed to in writing, software +\comment distributed under the License is distributed on an "AS IS" BASIS, +\comment WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +\comment See the License for the specific language governing permissions and +\comment limitations under the License. + + +\page pum_measurement Measurement + +`dip.MeasurementTool` can measure quite a lot of features for objects in an image, see \ref dip::MeasurementTool +for a full list. To demonstrate, we'll start with a simple example image that we can measure: the 'cermet' image. +Because the file doesn't have pixel size information embedded, we'll invent a pixel size. The pixel size is +attached to the image, and taken into account by many functions in the *DIPlib* library. `dip.MeasurementTool` +reports measurements in physical units instead of pixels if the image has pixel size information. +Note that pixels do not need to be isotropic, it is possible to give a different pixel size for each dimension. +```python +gray = dip.ImageReadICS('examples/cermet.ics') +gray.SetPixelSize(1, "um") # "um" is easier to type than "μm", but they mean the same thing +gray.Show() +``` +![The 'cermet' image](basics-7.png) + +Next, we threshold and label the image, then measure some basic features. Because 'Solidity' depends on the +'ConvexArea' measurement, we get that one too in the output. +```python +bin_img = gray < 120 +label = dip.Label(bin_img, minSize=30) +label = dip.EdgeObjectsRemove(label) +measurement = dip.MeasurementTool.Measure(label, gray, ['Size', 'Solidity', 'Statistics']) +print(measurement) +``` +```none + | Size | Solidity | Statistics | ConvexArea | +-- | ---------- | ---------- | ----------------------------------------------------- | ---------- | + | | | Mean | StdDev | Skewness | ExcessKurtosis | | + | (μm²) | | | | | | (μm²) | +-- | ---------- | ---------- | ---------- | ---------- | ---------- | -------------- | ---------- | + 6 | 262.0 | 0.9668 | 45.34 | 30.82 | 0.7216 | -0.6831 | 271.0 | + 7 | 63.00 | 0.9474 | 86.35 | 13.41 | 0.2313 | -0.5471 | 66.50 | + 8 | 243.0 | 0.9293 | 75.09 | 21.16 | 0.1711 | -0.9723 | 261.5 | + 9 | 209.0 | 0.9698 | 61.63 | 25.80 | 0.3937 | -0.7994 | 215.5 | +10 | 462.0 | 0.9665 | 62.10 | 20.27 | 0.7329 | 0.1613 | 478.0 | +11 | 611.0 | 0.9745 | 81.17 | 17.92 | -0.3812 | -0.2219 | 627.0 | +12 | 80.00 | 0.9816 | 83.10 | 15.72 | 0.1468 | -0.7721 | 81.50 | +13 | 205.0 | 0.9762 | 52.92 | 32.19 | 0.1556 | -1.183 | 210.0 | +14 | 419.0 | 0.9836 | 41.60 | 30.24 | 0.8653 | -0.3741 | 426.0 | +15 | 363.0 | 0.9041 | 71.56 | 22.25 | -0.2541 | -0.5946 | 401.5 | +16 | 487.0 | 0.9740 | 57.81 | 25.17 | 0.05945 | -0.4846 | 500.0 | +17 | 383.0 | 0.9746 | 53.10 | 24.60 | 0.6360 | -0.3009 | 393.0 | +18 | 250.0 | 0.9709 | 50.21 | 30.08 | 0.6251 | -0.8159 | 257.5 | +20 | 137.0 | 0.9786 | 64.47 | 22.41 | 0.5215 | -0.8983 | 140.0 | +21 | 378.0 | 0.9668 | 64.85 | 21.35 | 0.3866 | -0.5561 | 391.0 | +22 | 392.0 | 0.9043 | 48.06 | 31.20 | 0.4776 | -0.8514 | 433.5 | +23 | 230.0 | 0.9746 | 70.43 | 23.68 | -0.2813 | -0.6269 | 236.0 | +25 | 262.0 | 0.9686 | 62.26 | 25.31 | 0.3051 | -0.7452 | 270.5 | +26 | 637.0 | 0.9245 | 52.94 | 23.86 | 0.8441 | -0.08530 | 689.0 | +28 | 341.0 | 0.9757 | 54.94 | 25.06 | 0.8843 | -0.3705 | 349.5 | +29 | 501.0 | 0.9747 | 51.85 | 24.15 | 0.9221 | -0.05920 | 514.0 | +30 | 556.0 | 0.8580 | 60.65 | 22.53 | 0.5287 | -0.3121 | 648.0 | +31 | 592.0 | 0.8889 | 58.28 | 29.00 | 0.1195 | -1.026 | 666.0 | +32 | 172.0 | 0.9718 | 68.47 | 23.14 | 0.3064 | -0.9392 | 177.0 | +33 | 566.0 | 0.9792 | 41.71 | 30.85 | 0.7348 | -0.5709 | 578.0 | +35 | 842.0 | 0.9268 | 53.14 | 26.75 | 0.1291 | -0.4931 | 908.5 | +37 | 209.0 | 0.9676 | 56.00 | 26.01 | 0.5350 | -0.8241 | 216.0 | +38 | 147.0 | 0.9545 | 65.14 | 24.51 | 0.3733 | -0.9707 | 154.0 | +39 | 375.0 | 0.9766 | 71.89 | 21.69 | 0.06353 | -0.7623 | 384.0 | +40 | 385.0 | 0.9637 | 51.05 | 27.73 | 0.6729 | -0.5471 | 399.5 | +41 | 223.0 | 0.9612 | 63.78 | 25.31 | 0.1825 | -0.4636 | 232.0 | +42 | 347.0 | 0.9734 | 55.33 | 26.30 | 0.5900 | -0.7111 | 356.5 | +43 | 604.0 | 0.9527 | 50.44 | 26.84 | 0.6709 | -0.5829 | 634.0 | +44 | 354.0 | 0.9739 | 42.53 | 33.74 | 0.6403 | -0.9280 | 363.5 | +45 | 543.0 | 0.9696 | 50.64 | 24.14 | 1.068 | 0.3071 | 560.0 | +47 | 147.0 | 0.9515 | 67.05 | 22.61 | 0.2393 | -0.5154 | 154.5 | +48 | 405.0 | 0.9000 | 83.24 | 23.60 | -0.9721 | 0.0003058 | 450.0 | +49 | 577.0 | 0.9714 | 30.64 | 31.71 | 1.246 | 0.2249 | 594.0 | +50 | 497.0 | 0.9717 | 61.73 | 18.86 | 1.101 | 0.3655 | 511.5 | +52 | 525.0 | 0.9813 | 34.06 | 31.89 | 1.047 | -0.1825 | 535.0 | +53 | 803.0 | 0.9634 | 54.23 | 25.55 | 0.4471 | -0.5974 | 833.5 | +54 | 253.0 | 0.9750 | 59.83 | 25.32 | 0.4961 | -0.8077 | 259.5 | +55 | 193.0 | 0.9772 | 65.91 | 23.49 | 0.4554 | -0.8702 | 197.5 | +``` + + +\section pum_measurement_information Information about the measurement object + +The leftmost column of the table above shows the object IDs (labels) of the measured objects. This list can +be obtained with `measurement.Objects()`. Note that `dip.EdgeObjectsRemove` removed some labeled objects, +those labels are not in the list. + +The column group headers (features), and some information about them, can be obtained with `measurement.Features()`, +which returns a list that tells us the name and the number of columns for each feature. +`measurement.Values()` also returns a list, but it has an element for each column (not column group), and +tells us the name of the value (only for multi-value features) and the units that the value is in. + +To query the size of the measurement object, use `measurement.NumberOfObjects()` (which is the same as +`len(measurement)`), `measurement.NumberOfFeatures()` and `measurement.NumberOfValues()`. One can query +if a specific feature was measured with `measurement.FeatureExists()`, and if a specific objects was +measured with `measurement.ObjectExists()`. + + +\section pum_measurement_indexing Indexing into the measurement object + +The `dip.Measurement` object `measurement` can be indexed in three levels: the measurement name ('Statistics'), +the object number (30), and the measurement value within the selected measurement (2): +```python +measurement['Statistics'][30] # returns a list with the four statistics values +measurement['Statistics'][30][2] # returns a float +measurement[30]['Statistics'][2] # identical to the above, the order of the two first indices is irrelevant +``` + +Leaving out one of the indices returns the full row or column: +```python +row = measurement[30] +col = measurement['Solidity'] +``` + +These objects can be used in many different ways. Both have the same methods as the `measurement` +object has, or rather the subset that makes sense: For the column we can query about objects: +`col.Objects()`, `col.NumberOfObjects()` and `col.ObjectExists()`. For the row we can query about +features and values: `row.Features()`, `row.Values()`, `row.NumberOfFeatures()`, `row.NumberOfValues()` +and `row.FeatureExists()`. + +Additionally, we have `row.ObjectID()` and `col.FeatureName()` to find out which row and which column +is indexed. + +These objects also mimic the functionality of a dictionary: they both have a `keys()` method, +a `values()` method, and an `items()` method. These methods do copy the data, so they are less efficient +to use. But they can be very useful. Additionally, `iter()` and `len()` have been overloaded. This +means that one can iterate over a row or column: +```python +t = 0 +for s in col: + t += s[0] + +for s in row: + print(s) +``` + +As we'll see below, there are also some functions that take a column of measurements as input. + +Because a feature can have multiple values, something like `measurement['Statistics']` doesn't +represent a single value per object. The `Subset()` method can be used to select a single +column. It modifies the object to be a single value: +```python +col = measurement['Statistics'] +col.Subset(1) +col.Values() # Shows that we only have one column, the 'StdDev' one. +``` + + +\section pum_measurement_statistics Statistics on measurements + +There exist various statistics functions that can be applied to a measurement column: +```python +dip.Maximum(measurement['Solidity']) # the largest solidity value +dip.MaximumObject(measurement['Solidity']) # the object ID for the largest solidity value +dip.SampleStatistics(measurement['Solidity']) +``` +These functions all return the statistics for the first column in the column group, use +`Subset()` (see \ref pum_measurement_indexing "above") to select which column to apply the statistics to. + +See under "related" on \ref dip::Measurement::IteratorFeature "this page" for a full +list of functions available. + +Because a measurement column object is accepted as input by NumPy functions, there are +many other statistics that can be applied (see \ref pum_measurement_numpy). + + +\section pum_measurement_selection Selecting measurement rows + +Comparison operators applied to a colum produce a \ref dip::LabelMap "`dip.LabelMap`" object. This +is an object that can be used to index into a measurement object to select rows. For example, +to select all the objects over 600 μm²: +```python +selection = measurement['Size'] > 600 +large_objects = measurement[selection] +large_objects.NumberOfObjects() # equal to 5 +``` +`selection.Count()` is also 5. + +The operation is always applied to the first value of a multi-valued feature. Use `Subset()` +\ref pum_measurement_indexing "as discussed above" to select which value to apply the operation to. + +One can combine multiple `LabelMap` objects together using element-wise Boolean operators `&`, `|` and `^`. +Also the unary element-wise negation operator `~` can be used. But be aware of the precendence +of these operators in Python + +In this `LabelMap` object, each label (object ID) is mapped to a new value. +Those labels mapped to 0 are the rows that will be removed when the mapping is applied to a measurement +object (which we did with `measurement[selection]`). The other labels all map to themselves by default. +One can use indexing to examine and update the mapping. + +For example: +```python +large_objects.Objects() # is [11, 26, 35, 43, 53] + +selection[11] = 1 +selection[26] = 0 +large_objects = measurement[selection] +large_objects.Objects() # is [1, 35, 43, 53] +``` +We re-labeled object 11 as 1, and we explicitly deselected object number 26. + +`selection.Relabel()` causes the selected rows to be relabeled with contiguous labels starting at 1: +```python +selection.Relabel() +large_objects = measurement[selection] +large_objects.Objects() # is [1, 2, 3, 4] +``` + +Applying the `LabelMap` to the label image that the measurement was taken from is a simple way to +select objects with specific properties: +```python +selection = measurement['Size'] > 600 +selection.Relabel() # optional +large_object_image = selection.Apply(label) +large_object_image.Show('labels') +``` +![Image with only the 5 largest objects selected](measurement-1.png) + + +\section pum_measurement_numpy Converting measurements to a NumPy array + +The measurement object can be converted to a NumPy array, which will have the same +layout as the measurement object as shown by `print()`. This array will share data +with the measurement object, modifying it will modify the measurement object. + +```python +array = np.asarray(measurement) +array[0, 0] = 0 +measurement['Size'][6] # will print [0.0] +``` + +Also a partially-indexed measurement object can be converted to a NumPy array, +again sharing data: +```python +np.asarray(measurement[30]) # returns a 2D NumPy array for a single row of the measurement table +np.asarray(measurement['Solidity']) # returns a 2D NumPy array for a column group +``` + +But note that it is not always necessary to explicitly convert to a NumPy array, one +can directly call NumPy functions on these objects: +```python +np.max(measurement['Solidity']) +``` +Casting to an array might be useful to call a function that expects its input to have +a `shape` property. + + +\section pum_measurement_other Other ways to use measurements + +We can paint the objects with one of the measurements, which can be useful for display: +```python +solidity = dip.ObjectToMeasurement(label, measurement['Solidity']) +solidity.Show(colormap='viridis') +``` +![Image where the intensity of each object is its 'Solidity' value](measurement-2.png) + +Casted to a NumPy array, measurement columns can be the input to plotting functions. +Here we plot size vs solidity: +```python +import matplotlib.pyplot as plt +plt.scatter(np.asarray(measurement['Size']), np.asarray(measurement['Solidity'])) +plt.xlabel('Size (μm²)') +plt.ylabel('Solidity') +``` +![2D scatter plot of size vs solidity, there's no correlation](measurement-3.png) + +If you use Pandas, you can convert the measurement object to a Data Frame: +```python +df = measurement.ToDataFrame() +print(df) +``` +```none + Size Solidity Statistics, Mean Statistics, StdDev Statistics, Skewness Statistics, ExcessKurtosis ConvexArea +6 0.0 0.966790 45.343511 30.821640 0.721578 -0.683085 271.0 +7 63.0 0.947368 86.349206 13.414194 0.231313 -0.547051 66.5 +8 243.0 0.929254 75.094650 21.163748 0.171140 -0.972349 261.5 +9 209.0 0.969838 61.626794 25.801656 0.393697 -0.799403 215.5 +10 462.0 0.966527 62.095238 20.270824 0.732851 0.161275 478.0 +11 611.0 0.974482 81.165303 17.923805 -0.381249 -0.221944 627.0 +12 80.0 0.981595 83.100000 15.718730 0.146845 -0.772090 81.5 + +``` +As you can see, column names for multi-valued features are generated that combine the feature +name and the value name, such as `df["Statistics, Mean"]`. + +Finally, the function \ref dip::MeasurementWriteCSV "`dip.MeasurementWriteCSV()`" can be used to write a complete +measurement object to a CSV file, again including the object IDs and header information.