From 59a3474731d7523fbea4256e9452f85ac6cbaaaf Mon Sep 17 00:00:00 2001 From: MMedenski Date: Sat, 17 Jan 2026 13:47:51 +0100 Subject: [PATCH 1/7] Delete data/generated_mask/example_generated_mask directory --- .../RGB_ar037_2019_n_06_04_0_mask.png | Bin 1091 -> 0 bytes .../RGB_ar037_2019_n_07_05_0_mask.png | Bin 1377 -> 0 bytes .../RGB_ar037_2019_n_07_17_1_mask.png | Bin 739 -> 0 bytes .../RGB_ar037_2019_n_08_14_0_mask.png | Bin 1023 -> 0 bytes .../RGB_ar037_2019_n_13_13_0_mask.png | Bin 1379 -> 0 bytes .../RGB_ar039_2019_n_02_04_0_mask.png | Bin 1022 -> 0 bytes .../RGB_ar039_2019_n_02_10_0_mask.png | Bin 1086 -> 0 bytes .../RGB_ar039_2019_n_03_04_0_mask.png | Bin 1015 -> 0 bytes .../RGB_ar039_2019_n_04_10_0_mask.png | Bin 957 -> 0 bytes .../RGB_ar039_2019_n_04_13_0_mask.png | Bin 1145 -> 0 bytes 10 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 data/generated_mask/example_generated_mask/RGB_ar037_2019_n_06_04_0_mask.png delete mode 100644 data/generated_mask/example_generated_mask/RGB_ar037_2019_n_07_05_0_mask.png delete mode 100644 data/generated_mask/example_generated_mask/RGB_ar037_2019_n_07_17_1_mask.png delete mode 100644 data/generated_mask/example_generated_mask/RGB_ar037_2019_n_08_14_0_mask.png delete mode 100644 data/generated_mask/example_generated_mask/RGB_ar037_2019_n_13_13_0_mask.png delete mode 100644 data/generated_mask/example_generated_mask/RGB_ar039_2019_n_02_04_0_mask.png delete mode 100644 data/generated_mask/example_generated_mask/RGB_ar039_2019_n_02_10_0_mask.png delete mode 100644 data/generated_mask/example_generated_mask/RGB_ar039_2019_n_03_04_0_mask.png delete mode 100644 data/generated_mask/example_generated_mask/RGB_ar039_2019_n_04_10_0_mask.png delete mode 100644 data/generated_mask/example_generated_mask/RGB_ar039_2019_n_04_13_0_mask.png diff --git a/data/generated_mask/example_generated_mask/RGB_ar037_2019_n_06_04_0_mask.png b/data/generated_mask/example_generated_mask/RGB_ar037_2019_n_06_04_0_mask.png deleted file mode 100644 index d0cc48f9dc16a542193ebf322191536270c1f968..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1091 zcmeAS@N?(olHy`uVBq!ia0y~yV9W#JMh>6=L-K7F76t|uE>9Q7kP61P2Lr=GqM29^ zp83iD%cSGRf|@H|L*KSe5d1W;`3d6_X7&kp=GQ!6nDl$Hz3e>CcYbyS6-qm;e;!~| z`K~T+(eUKo6J>S~)5_zWzg-DXaf*kLisU37&o&k3Ns1>Y3|8(xL-wELC-*-)vEQ2r z_ny37{ABZ__0LbFtHxiL=HLmPGr#$htI+3mtf638KnKIATu(|JJ|H;C4 zV!Hy-!T02-qnzKTM<*U*4egz;6!K+Hu6?qqc~7B3k@C(L44+Ojs+dp8=JS7Y^>t%PUkX-QT_c;Pt7`=Dz{=`Hu@gVU#JHb)+|KGB7mq0p{Asr*SGpU1t)q-MQ; Y>?-!YP1euvo&c2&p00i_>zopr0FD!_9RL6T diff --git a/data/generated_mask/example_generated_mask/RGB_ar037_2019_n_07_05_0_mask.png b/data/generated_mask/example_generated_mask/RGB_ar037_2019_n_07_05_0_mask.png deleted file mode 100644 index 5b1cdfda7d0000a73375c8f97839eebd4847faf4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1377 zcmeAS@N?(olHy`uVBq!ia0y~yVC(_nnH)d?hW(zA`xqElH9TD$Ln;{G9%L-K%_hKd zaOQveUrs?jO3@mUVS)#k?&RG(?D2Qzo5Usme98d zoweFRcqkJ%F1f^R;$j~2p#PH>^C*1nlu6k!@s|sqp^Zna(r@X!mWjW7_zXO1RoNw0 zvKBQ=j8dS6rKFhgWR2T8w{uxfvcL?#tS3cJmbmG;gG7i7hSgG*(mPi$T1xI*_R#H| zt6t`g$;A`BrShJnJekyHZ7IHU$wR+$uIqrp!4tcs9DvNMCpoyx{_52HXU>(^JSWb3 z?mgE4WODO<)(_)IJmPP0|&7j2oC%KRw|DLnUF z^7uPv&xHczt;ZDhPrNQEwZuR!sVJjDfHGlXm6Qhn}yb z57$2l|D<(z@|E&7^`E_aPJK}MI`^SxowB|$FrDUCOt^niyEiA}iBOyMPp7PU=1)_x zW{XYRCq}8^30@>OV%3i|nor!8p1|?x9UF*fllD}|&u8#dc%QdpGQ+2Dz|=Kw&)$cA zed_r@M;<f<3R~?X4T9LI zR3cyrpnXzH_`n&z#x%%%!g{w?99V+_ccJtreF>kcKXXv~q~(lD`c&lGtXDFhJp94& umBlB6JD#$hc03BD8kG`xKvkI5KbF6_w{MnJY{)B6+3D%(=d#Wzp$P!1T`z9{ diff --git a/data/generated_mask/example_generated_mask/RGB_ar037_2019_n_07_17_1_mask.png b/data/generated_mask/example_generated_mask/RGB_ar037_2019_n_07_17_1_mask.png deleted file mode 100644 index 6727b109b0d608b651b4468315c4b30c6f79d323..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 739 zcmeAS@N?(olHy`uVBq!ia0y~yU`zmE4xj+T0=TD@{2hyfE!@x5n#vir&2$$LERS*qwy+Rpb= zzU-5~LjK$*sh^H1R?gg0IO+OHY4ysvDZZQ3?B}F7)LEQts44egd$N17&WUpM$O-TL zHYwbno8q`h?f#q;r+tgqQqt~m)J{rxs67fe#LoWN7rkaB|g2Y!vrDt?>TT@@Lv(U7!Wl zee0hvoHPc~=bvnzY@ d1u0JdCcpGuYW$v^jDJBn+tbz0Wt~$(69Dx!;B5c^ diff --git a/data/generated_mask/example_generated_mask/RGB_ar037_2019_n_13_13_0_mask.png b/data/generated_mask/example_generated_mask/RGB_ar037_2019_n_13_13_0_mask.png deleted file mode 100644 index 2a9ae3d0c6daaa58e5e19f3e930548d0bd81faa9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1379 zcmd^9ZA@EL7(S&^M!gsO7^6@Oceb;kE(VIDtxVb??RrH!Z6>CnAse~PhHDF6lL>}7 zLbVG^Ywo4ShR8?U(1p=8dWo9j5QEK@Q$J=h2c`zpF2^654oM@CE>XONU^FpH{B53` zyicBU-jkf?ea}1el;Z#?uapA-NZQ`w1VG{!7rR3g!0K}M3;=~%y2Wy+S8{V+!YUV%`iQgX*P8`nHD&6*qr5>-z<2q}_CosiX{i?S-d;@&do-b>`F5q5#fvg}SpJ{k3Ax8(o!sXUD?vT0nO1PqA}fLh z*X17WxJbM4HRkR_!2Ral&R*_55=GJd!GO2RJlC?Tu8KL3u*@~L)eU5hF}Rhfkh0GU zA%2;7tv_&mqn7%fT00f$Kb>K?NT`<8rILU>lcqG0i-Z_0jt3em%#PYwK9Y3Exbw;; zlw|HyolaZEee2?(M~RYZ41*KcEexg0v+hKraU|2Z9^StI*hf-VRv+w(^B{#*^(o%# zS*O~EqZPTi^q80K@PJ%=lQ0JKAz3PD`uMbE&IeMj&wc^2Cj*(sJC2$C){ZXRf#0_I zt@)bz^M-bFJGaYm``9S&2KA0GS@@&*ws diff --git a/data/generated_mask/example_generated_mask/RGB_ar039_2019_n_02_04_0_mask.png b/data/generated_mask/example_generated_mask/RGB_ar039_2019_n_02_04_0_mask.png deleted file mode 100644 index 53ebd62b9e90935250ba63d80363f1e12c9af5f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1022 zcmeAS@N?(olHy`uVBq!ia0y~yU`ztyQVyU1!}llqTNxObk9xW|hEy=VJ;+!V9L~gg z@ZDeiU%nOs$5<8%=iK%9RWdcj$4E_ba*yXRmE=i=CugMi*uD3t^R;`g@KY7ULz6_| z{z>OMA@6}AgkJ-i>=Svu6SAKFUN+q6f1=L5Q~sns--){(b!8KFPu%lZ;k#1h@rk=9 z_MAM=Hwj1~97w)}X!c{}W?O&CD9U7?aD0-sb&VpwYIxa?Q_B3xKx(#4Pt47S)|DbV zw;aCzB=E^2zWXPdPh6GVH=%vvY3l}?zL@)rl`=c`Dfg?O1Sl=sTXg$i^OMIiO^UAaQgV0!?my(MH2d_4FaPA`iQ(lH-t+qQ z>^RK-pu?j`uA)Da(Kx8BP=4TYJ27x>q{kPWzts9`BqnY(PpOG1naDHKJLD%-#5((QSfbSy{kC9#F>i MboFyt=akR{0F&an`v3p{ diff --git a/data/generated_mask/example_generated_mask/RGB_ar039_2019_n_02_10_0_mask.png b/data/generated_mask/example_generated_mask/RGB_ar039_2019_n_02_10_0_mask.png deleted file mode 100644 index 30108b2df5e8284f6fc023fcdded723bf4e1dbd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1086 zcmeAS@N?(olHy`uVBq!ia0y~yVC)Ct2^>HHhT_7_r3?%#te!58Ar*{o4|*0!doUbv zc<{gf-NvVH9d0gMutUW3#1x&0qmcLzNVlBC{VBnHpS8kHh5phXu0@`5uRgRtX|lQU z(EOxo<%+}kle|Agw9i+0KY`u)k3wa;&7Ft6PbS&iduV-9-u>PQVVQlCV-V)tHQC-2WK%}?B(Oy*PADfn~BVe?7R6VF>rnmx(p*2Mjj z2#0XZm5IA2g9(1CNxvueTY9|nw|my`C=cOR|D;v+XBGh^ zOJ#oE!lvX2tL)EJSS2R*J(>Tck?+38KR>xw3P0IT3Y`?HY*VhB4hhy8OZPY^6Bg2Y z8kGNwPg4EVD)Z#%$?1g-adT@#*(Zdn6Pp}xs{15w#9%Y=I=6Q)s9^AP^>bP0l+XkK D`t6cK diff --git a/data/generated_mask/example_generated_mask/RGB_ar039_2019_n_03_04_0_mask.png b/data/generated_mask/example_generated_mask/RGB_ar039_2019_n_03_04_0_mask.png deleted file mode 100644 index 287af0ff5010ff9d7fe8e70a1da3ae19ad6ca45c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1015 zcmeAS@N?(olHy`uVBq!ia0y~yU~C2A`5Zt2hN-L*GZ`3|_j$TFhEy=VJs6mGH;mz^ zL(%{4eYsNCD_5o-U{jjgU*xt){pRF1p5IieC)u8ym*P(hx32ifp^4UDJ^I!~{3oZ& z?rcZZc`I;JsQ{93PwL;DAYk~D>b^gPlh#`|{HdIjZ*hWG<$l2vJ|L@z9c0d@+08b7 z#JG(lk5EZ*pbTb{+Jyz$>!mQ$uSdwq??|{yn-LoYg8%^J)B#kPYx0}r~iccHV}iH+j7`(N3zI={!$Sc;a-+%8nXib$P2NlArY2ZCv(EpWH9= zNu+Wjb7fDBcKe^%%ug&o&6WA|p09F3jk>zLW#t4q`1^^W`g$dQ%O|0zLCfqH=K=Fz z-R{ZuCsBgFzQAa{-@k(=YpkdeHy-bw^e!ns!R`5eg7~C6iuo!#o$q;6weOkmg|AYv z@_^!dWqyk%wNE(J-z)oDRLWN#I{EvAu-`rRc?F;Bj0E9axq?qlm#iz5U(Ve#@rhrY zCsTWm2%pUa^T{U^->Z?Bd`_G^?fH1Z^vOpW_skLLvp>1p^TChqJ+n>tCWKEurMO;g zrt>`?BlY!|#-1-cQR88EwDC;)6JZ&J&xSus**{J8^NVw=yV-cAeo}my!sq$OL4E&8 rN}a;~lZv0qKUK>Z*-xNxBClsy`M@(Xq+_=#C|`QI`njxgN@xNAXa#8f diff --git a/data/generated_mask/example_generated_mask/RGB_ar039_2019_n_04_13_0_mask.png b/data/generated_mask/example_generated_mask/RGB_ar039_2019_n_04_13_0_mask.png deleted file mode 100644 index 719a7b67050c000a31466d9a526b235cf5f3a31d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1145 zcmeAS@N?(olHy`uVBq!ia0y~yV4MrY^EiM43|R`z7Z?~=96VhdLn;{G9%RglWfx;T z`0lU%FSQ988`=0dy7f!{_)Ybhjt=)6QT&`~6pIQ!edU|kKVg0OiJF$0z0S{+pPZI8 zl2?jY`z-Sw>`?C6g#SD<^{4@V_R1*i7yMZ}r{I(H#FvxEhm|`g$18s>Qs3|P?5ST} zFM9F=@oRQ7&zwIozr5j$|B3C^4$st|{FXJcSHAy#!cR#Izf8CMDKm4r<%IJSDrYi3 z5l)GFHo<>lQuQzWD)XJ5IByWE=!_pB!e zfue5nUjLZ*ka>--)ORDEf{`ekxvXakc2})9YHbJd&4|C=u%O#%A944jkWllP7Jwa_Iv*#qs m2`Z9&o|7If@gy>6Uh`)}dCq6y`JfFdQ#@V$T-G@yGywpqx%B1$ From b84eaa1d84e4fbfcd800c6fe73124845068f41e1 Mon Sep 17 00:00:00 2001 From: MMedenski Date: Sat, 17 Jan 2026 20:38:35 +0100 Subject: [PATCH 2/7] Add files via upload --- config.yaml | 2 +- .../RGB_ar037_2019_n_06_04_0_mask.png | Bin 0 -> 1091 bytes .../RGB_ar037_2019_n_07_05_0_mask.png | Bin 0 -> 1377 bytes .../RGB_ar037_2019_n_07_17_1_mask.png | Bin 0 -> 739 bytes .../RGB_ar037_2019_n_08_14_0_mask.png | Bin 0 -> 1023 bytes .../RGB_ar037_2019_n_13_13_0_mask.png | Bin 0 -> 1379 bytes .../RGB_ar039_2019_n_02_04_0_mask.png | Bin 0 -> 1022 bytes .../RGB_ar039_2019_n_02_10_0_mask.png | Bin 0 -> 1086 bytes .../RGB_ar039_2019_n_03_04_0_mask.png | Bin 0 -> 1015 bytes .../RGB_ar039_2019_n_04_10_0_mask.png | Bin 0 -> 957 bytes .../RGB_ar039_2019_n_04_13_0_mask.png | Bin 0 -> 1145 bytes data_examples/example_iou_results.csv | 11 + iou_results.csv | 3 + main.py | 647 +++--------------- src/cli.py | 85 +++ src/config_loader.py | 23 + src/evaluation.py | 20 + src/io_utils.py | 25 + src/logger.py | 29 + src/optimization.py | 21 + src/segmentation.py | 22 + temp_config.yaml | 2 +- 22 files changed, 339 insertions(+), 551 deletions(-) create mode 100644 data_examples/example_generated_mask/RGB_ar037_2019_n_06_04_0_mask.png create mode 100644 data_examples/example_generated_mask/RGB_ar037_2019_n_07_05_0_mask.png create mode 100644 data_examples/example_generated_mask/RGB_ar037_2019_n_07_17_1_mask.png create mode 100644 data_examples/example_generated_mask/RGB_ar037_2019_n_08_14_0_mask.png create mode 100644 data_examples/example_generated_mask/RGB_ar037_2019_n_13_13_0_mask.png create mode 100644 data_examples/example_generated_mask/RGB_ar039_2019_n_02_04_0_mask.png create mode 100644 data_examples/example_generated_mask/RGB_ar039_2019_n_02_10_0_mask.png create mode 100644 data_examples/example_generated_mask/RGB_ar039_2019_n_03_04_0_mask.png create mode 100644 data_examples/example_generated_mask/RGB_ar039_2019_n_04_10_0_mask.png create mode 100644 data_examples/example_generated_mask/RGB_ar039_2019_n_04_13_0_mask.png create mode 100644 data_examples/example_iou_results.csv create mode 100644 iou_results.csv create mode 100644 src/cli.py create mode 100644 src/config_loader.py create mode 100644 src/evaluation.py create mode 100644 src/io_utils.py create mode 100644 src/logger.py create mode 100644 src/optimization.py create mode 100644 src/segmentation.py diff --git a/config.yaml b/config.yaml index 890984e..56e5b6a 100644 --- a/config.yaml +++ b/config.yaml @@ -2,7 +2,7 @@ paths: rgb: ./data/RGB_images/*.png # nrg: ./data/NRG_images/*.png # masks: ./data/masks/*.png # - output: ./data/generated_masks/ # + output: ./data_examples/example_generated_mask/ # thresholds: hue_min: 0.75 diff --git a/data_examples/example_generated_mask/RGB_ar037_2019_n_06_04_0_mask.png b/data_examples/example_generated_mask/RGB_ar037_2019_n_06_04_0_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..d0cc48f9dc16a542193ebf322191536270c1f968 GIT binary patch literal 1091 zcmeAS@N?(olHy`uVBq!ia0y~yV9W#JMh>6=L-K7F76t|uE>9Q7kP61P2Lr=GqM29^ zp83iD%cSGRf|@H|L*KSe5d1W;`3d6_X7&kp=GQ!6nDl$Hz3e>CcYbyS6-qm;e;!~| z`K~T+(eUKo6J>S~)5_zWzg-DXaf*kLisU37&o&k3Ns1>Y3|8(xL-wELC-*-)vEQ2r z_ny37{ABZ__0LbFtHxiL=HLmPGr#$htI+3mtf638KnKIATu(|JJ|H;C4 zV!Hy-!T02-qnzKTM<*U*4egz;6!K+Hu6?qqc~7B3k@C(L44+Ojs+dp8=JS7Y^>t%PUkX-QT_c;Pt7`=Dz{=`Hu@gVU#JHb)+|KGB7mq0p{Asr*SGpU1t)q-MQ; Y>?-!YP1euvo&c2&p00i_>zopr0FD!_9RL6T literal 0 HcmV?d00001 diff --git a/data_examples/example_generated_mask/RGB_ar037_2019_n_07_05_0_mask.png b/data_examples/example_generated_mask/RGB_ar037_2019_n_07_05_0_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..5b1cdfda7d0000a73375c8f97839eebd4847faf4 GIT binary patch literal 1377 zcmeAS@N?(olHy`uVBq!ia0y~yVC(_nnH)d?hW(zA`xqElH9TD$Ln;{G9%L-K%_hKd zaOQveUrs?jO3@mUVS)#k?&RG(?D2Qzo5Usme98d zoweFRcqkJ%F1f^R;$j~2p#PH>^C*1nlu6k!@s|sqp^Zna(r@X!mWjW7_zXO1RoNw0 zvKBQ=j8dS6rKFhgWR2T8w{uxfvcL?#tS3cJmbmG;gG7i7hSgG*(mPi$T1xI*_R#H| zt6t`g$;A`BrShJnJekyHZ7IHU$wR+$uIqrp!4tcs9DvNMCpoyx{_52HXU>(^JSWb3 z?mgE4WODO<)(_)IJmPP0|&7j2oC%KRw|DLnUF z^7uPv&xHczt;ZDhPrNQEwZuR!sVJjDfHGlXm6Qhn}yb z57$2l|D<(z@|E&7^`E_aPJK}MI`^SxowB|$FrDUCOt^niyEiA}iBOyMPp7PU=1)_x zW{XYRCq}8^30@>OV%3i|nor!8p1|?x9UF*fllD}|&u8#dc%QdpGQ+2Dz|=Kw&)$cA zed_r@M;<f<3R~?X4T9LI zR3cyrpnXzH_`n&z#x%%%!g{w?99V+_ccJtreF>kcKXXv~q~(lD`c&lGtXDFhJp94& umBlB6JD#$hc03BD8kG`xKvkI5KbF6_w{MnJY{)B6+3D%(=d#Wzp$P!1T`z9{ literal 0 HcmV?d00001 diff --git a/data_examples/example_generated_mask/RGB_ar037_2019_n_07_17_1_mask.png b/data_examples/example_generated_mask/RGB_ar037_2019_n_07_17_1_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..6727b109b0d608b651b4468315c4b30c6f79d323 GIT binary patch literal 739 zcmeAS@N?(olHy`uVBq!ia0y~yU`zmE4xj+T0=TD@{2hyfE!@x5n#vir&2$$LERS*qwy+Rpb= zzU-5~LjK$*sh^H1R?gg0IO+OHY4ysvDZZQ3?B}F7)LEQts44egd$N17&WUpM$O-TL zHYwbno8q`h?f#q;r+tgqQqt~m)J{rxs67fe#LoWN7rkaB|g2Y!vrDt?>TT@@Lv(U7!Wl zee0hvoHPc~=bvnzY@ d1u0JdCcpGuYW$v^jDJBn+tbz0Wt~$(69Dx!;B5c^ literal 0 HcmV?d00001 diff --git a/data_examples/example_generated_mask/RGB_ar037_2019_n_13_13_0_mask.png b/data_examples/example_generated_mask/RGB_ar037_2019_n_13_13_0_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..2a9ae3d0c6daaa58e5e19f3e930548d0bd81faa9 GIT binary patch literal 1379 zcmd^9ZA@EL7(S&^M!gsO7^6@Oceb;kE(VIDtxVb??RrH!Z6>CnAse~PhHDF6lL>}7 zLbVG^Ywo4ShR8?U(1p=8dWo9j5QEK@Q$J=h2c`zpF2^654oM@CE>XONU^FpH{B53` zyicBU-jkf?ea}1el;Z#?uapA-NZQ`w1VG{!7rR3g!0K}M3;=~%y2Wy+S8{V+!YUV%`iQgX*P8`nHD&6*qr5>-z<2q}_CosiX{i?S-d;@&do-b>`F5q5#fvg}SpJ{k3Ax8(o!sXUD?vT0nO1PqA}fLh z*X17WxJbM4HRkR_!2Ral&R*_55=GJd!GO2RJlC?Tu8KL3u*@~L)eU5hF}Rhfkh0GU zA%2;7tv_&mqn7%fT00f$Kb>K?NT`<8rILU>lcqG0i-Z_0jt3em%#PYwK9Y3Exbw;; zlw|HyolaZEee2?(M~RYZ41*KcEexg0v+hKraU|2Z9^StI*hf-VRv+w(^B{#*^(o%# zS*O~EqZPTi^q80K@PJ%=lQ0JKAz3PD`uMbE&IeMj&wc^2Cj*(sJC2$C){ZXRf#0_I zt@)bz^M-bFJGaYm``9S&2KA0GS@@&*ws literal 0 HcmV?d00001 diff --git a/data_examples/example_generated_mask/RGB_ar039_2019_n_02_04_0_mask.png b/data_examples/example_generated_mask/RGB_ar039_2019_n_02_04_0_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..53ebd62b9e90935250ba63d80363f1e12c9af5f1 GIT binary patch literal 1022 zcmeAS@N?(olHy`uVBq!ia0y~yU`ztyQVyU1!}llqTNxObk9xW|hEy=VJ;+!V9L~gg z@ZDeiU%nOs$5<8%=iK%9RWdcj$4E_ba*yXRmE=i=CugMi*uD3t^R;`g@KY7ULz6_| z{z>OMA@6}AgkJ-i>=Svu6SAKFUN+q6f1=L5Q~sns--){(b!8KFPu%lZ;k#1h@rk=9 z_MAM=Hwj1~97w)}X!c{}W?O&CD9U7?aD0-sb&VpwYIxa?Q_B3xKx(#4Pt47S)|DbV zw;aCzB=E^2zWXPdPh6GVH=%vvY3l}?zL@)rl`=c`Dfg?O1Sl=sTXg$i^OMIiO^UAaQgV0!?my(MH2d_4FaPA`iQ(lH-t+qQ z>^RK-pu?j`uA)Da(Kx8BP=4TYJ27x>q{kPWzts9`BqnY(PpOG1naDHKJLD%-#5((QSfbSy{kC9#F>i MboFyt=akR{0F&an`v3p{ literal 0 HcmV?d00001 diff --git a/data_examples/example_generated_mask/RGB_ar039_2019_n_02_10_0_mask.png b/data_examples/example_generated_mask/RGB_ar039_2019_n_02_10_0_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..30108b2df5e8284f6fc023fcdded723bf4e1dbd0 GIT binary patch literal 1086 zcmeAS@N?(olHy`uVBq!ia0y~yVC)Ct2^>HHhT_7_r3?%#te!58Ar*{o4|*0!doUbv zc<{gf-NvVH9d0gMutUW3#1x&0qmcLzNVlBC{VBnHpS8kHh5phXu0@`5uRgRtX|lQU z(EOxo<%+}kle|Agw9i+0KY`u)k3wa;&7Ft6PbS&iduV-9-u>PQVVQlCV-V)tHQC-2WK%}?B(Oy*PADfn~BVe?7R6VF>rnmx(p*2Mjj z2#0XZm5IA2g9(1CNxvueTY9|nw|my`C=cOR|D;v+XBGh^ zOJ#oE!lvX2tL)EJSS2R*J(>Tck?+38KR>xw3P0IT3Y`?HY*VhB4hhy8OZPY^6Bg2Y z8kGNwPg4EVD)Z#%$?1g-adT@#*(Zdn6Pp}xs{15w#9%Y=I=6Q)s9^AP^>bP0l+XkK D`t6cK literal 0 HcmV?d00001 diff --git a/data_examples/example_generated_mask/RGB_ar039_2019_n_03_04_0_mask.png b/data_examples/example_generated_mask/RGB_ar039_2019_n_03_04_0_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..287af0ff5010ff9d7fe8e70a1da3ae19ad6ca45c GIT binary patch literal 1015 zcmeAS@N?(olHy`uVBq!ia0y~yU~C2A`5Zt2hN-L*GZ`3|_j$TFhEy=VJs6mGH;mz^ zL(%{4eYsNCD_5o-U{jjgU*xt){pRF1p5IieC)u8ym*P(hx32ifp^4UDJ^I!~{3oZ& z?rcZZc`I;JsQ{93PwL;DAYk~D>b^gPlh#`|{HdIjZ*hWG<$l2vJ|L@z9c0d@+08b7 z#JG(lk5EZ*pbTb{+Jyz$>!mQ$uSdwq??|{yn-LoYg8%^J)B#kPYx0}r~iccHV}iH+j7`(N3zI={!$Sc;a-+%8nXib$P2NlArY2ZCv(EpWH9= zNu+Wjb7fDBcKe^%%ug&o&6WA|p09F3jk>zLW#t4q`1^^W`g$dQ%O|0zLCfqH=K=Fz z-R{ZuCsBgFzQAa{-@k(=YpkdeHy-bw^e!ns!R`5eg7~C6iuo!#o$q;6weOkmg|AYv z@_^!dWqyk%wNE(J-z)oDRLWN#I{EvAu-`rRc?F;Bj0E9axq?qlm#iz5U(Ve#@rhrY zCsTWm2%pUa^T{U^->Z?Bd`_G^?fH1Z^vOpW_skLLvp>1p^TChqJ+n>tCWKEurMO;g zrt>`?BlY!|#-1-cQR88EwDC;)6JZ&J&xSus**{J8^NVw=yV-cAeo}my!sq$OL4E&8 rN}a;~lZv0qKUK>Z*-xNxBClsy`M@(Xq+_=#C|`QI`njxgN@xNAXa#8f literal 0 HcmV?d00001 diff --git a/data_examples/example_generated_mask/RGB_ar039_2019_n_04_13_0_mask.png b/data_examples/example_generated_mask/RGB_ar039_2019_n_04_13_0_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..719a7b67050c000a31466d9a526b235cf5f3a31d GIT binary patch literal 1145 zcmeAS@N?(olHy`uVBq!ia0y~yV4MrY^EiM43|R`z7Z?~=96VhdLn;{G9%RglWfx;T z`0lU%FSQ988`=0dy7f!{_)Ybhjt=)6QT&`~6pIQ!edU|kKVg0OiJF$0z0S{+pPZI8 zl2?jY`z-Sw>`?C6g#SD<^{4@V_R1*i7yMZ}r{I(H#FvxEhm|`g$18s>Qs3|P?5ST} zFM9F=@oRQ7&zwIozr5j$|B3C^4$st|{FXJcSHAy#!cR#Izf8CMDKm4r<%IJSDrYi3 z5l)GFHo<>lQuQzWD)XJ5IByWE=!_pB!e zfue5nUjLZ*ka>--)ORDEf{`ekxvXakc2})9YHbJd&4|C=u%O#%A944jkWllP7Jwa_Iv*#qs m2`Z9&o|7If@gy>6Uh`)}dCq6y`JfFdQ#@V$T-G@yGywpqx%B1$ literal 0 HcmV?d00001 diff --git a/data_examples/example_iou_results.csv b/data_examples/example_iou_results.csv new file mode 100644 index 0000000..8afe8c0 --- /dev/null +++ b/data_examples/example_iou_results.csv @@ -0,0 +1,11 @@ +image_name - iou +RGB_ar037_2019_n_06_04_0.png - 52.10% +RGB_ar037_2019_n_07_05_0.png - 17.80% +RGB_ar037_2019_n_07_17_1.png - 0.00% +RGB_ar037_2019_n_08_14_0.png - 41.98% +RGB_ar037_2019_n_13_13_0.png - 15.57% +RGB_ar039_2019_n_02_04_0.png - 50.59% +RGB_ar039_2019_n_02_10_0.png - 13.10% +RGB_ar039_2019_n_03_04_0.png - 14.69% +RGB_ar039_2019_n_04_10_0.png - 8.10% +RGB_ar039_2019_n_04_13_0.png - 44.99% diff --git a/iou_results.csv b/iou_results.csv new file mode 100644 index 0000000..f3cf13e --- /dev/null +++ b/iou_results.csv @@ -0,0 +1,3 @@ +image_name,iou +RGB_ar037_2019_n_06_04_0.png,52.10% +RGB_ar037_2019_n_07_05_0.png,17.80% diff --git a/main.py b/main.py index a0f65cc..9bff311 100644 --- a/main.py +++ b/main.py @@ -1,572 +1,121 @@ -# Segmentation Pipeline - -#1. Load RGB and NRG images -#2. Generate independent segmentation masks (RGB / NRG) -#3. Fuse masks using morphological operations -#4. Compare predicted masks with main masks -#5. Compute IoU and confusion matrix -#6. Optimize segmentation thresholds - - -# Libraries - -import numpy as np -import configparser -from skimage import io, color, morphology, filters -import matplotlib.pyplot as plt -import os, glob, cv2, yaml, argparse -from sklearn.metrics import confusion_matrix, classification_report - -# Loading configuration from config.yaml - -def load_config(path="config.yaml"): - with open(path, "r", encoding="utf-8") as f: - cfg = yaml.safe_load(f) - - config = { - "paths_img": cfg["paths"]["rgb"], - "paths_img_nrg": cfg["paths"]["nrg"], - "paths_mask": cfg["paths"]["masks"], - "output_dir": cfg["paths"]["output"], - - "hue_min": float(cfg["thresholds"]["hue_min"]), - "hue_max": float(cfg["thresholds"]["hue_max"]), - "sat_thr": float(cfg["thresholds"]["sat_thr"]), - "val_thr": float(cfg["thresholds"]["val_thr"]), - - "num_images": int(cfg["general"]["num_images"]), - "num_compare": int(cfg["general"]["num_compare"]), - } - - return config - -def parse_args(): - parser = argparse.ArgumentParser( - description="Dead Tree Segmentation Pipeline (RGB + NRG)" - ) - - parser.add_argument("-c", "--config", type=str, default="config.yaml", - help="Path to config.yaml file") - - # Thresholds (override config) - parser.add_argument("-h_min", "--hue-min", type=float, help="HSV hue min threshold") - parser.add_argument("-h_max", "--hue-max", type=float, help="HSV hue max threshold") - parser.add_argument("-s", "--sat-thr", type=float, help="HSV saturation threshold") - parser.add_argument("-v", "--val-thr", type=float, help="HSV value threshold") - - # General - parser.add_argument("-ni", "--num-images", type=int, help="Number of images to preview") - parser.add_argument("-nc", "--num-compare", type=int, help="Number of images for evaluation") - - # Output - parser.add_argument("-o", "--output-dir", type=str, help="Output directory for results") - - return parser.parse_args() - - -args = parse_args() -config = load_config(args.config) - -if args.hue_min is not None: - config["hue_min"] = args.hue_min -if args.hue_max is not None: - config["hue_max"] = args.hue_max -if args.sat_thr is not None: - config["sat_thr"] = args.sat_thr -if args.val_thr is not None: - config["val_thr"] = args.val_thr +from src.cli import parse_args +from src.config_loader import load_config +from src.segmentation import ( + generate_segment_mask_rgb, + generate_segment_mask_nrg, + fuse_masks +) +from src.evaluation import iou, compute_confusion_matrix +from src.io_utils import save_iou_results, save_masks -if args.num_images is not None: - config["num_images"] = args.num_images -if args.num_compare is not None: - config["num_compare"] = args.num_compare +import glob +from skimage import io +import cv2 +import os +import logging -if args.output_dir is not None: - config["output_dir"] = args.output_dir -# Check if num_compare is greater than num_images -if config["num_compare"] > config["num_images"]: - raise ValueError( - f"Invalid configuration: num_compare ({config['num_compare']}) " - f"cannot be greater than num_images ({config['num_images']})." +def main(): + # === Logging setup === + logging.basicConfig( + level=logging.INFO, + format="[%(levelname)s] %(message)s" ) + # === Parse CLI === + args = parse_args() -paths_img = sorted(glob.glob(config["paths_img"])) -paths_img_nrg = sorted(glob.glob(config["paths_img_nrg"])) -paths_mask = sorted(glob.glob(config["paths_mask"])) - -hue_min = float(config["hue_min"]) -hue_max = float(config["hue_max"]) -sat_thr = float(config["sat_thr"]) -val_thr = float(config["val_thr"]) - -num_images = int(config["num_images"]) -num_compare = int(config["num_compare"]) - -comparison_results = [] - -assert len(paths_img) > 0, "No RGB images found in config path" -assert len(paths_img_nrg) > 0, "No NRG images found in config path" -assert len(paths_mask) > 0, "No main masks found in config path" + # === Load config from YAML === + config = load_config(args.config) -# Program Functions + # === CLI overrides config === -def generate_segment_mask_rgb(rgb_image, hue_min, hue_max, sat_thr, val_thr): + # Output path + if args.output_dir: + config["output_dir"] = args.output_dir - hsv = color.rgb2hsv(rgb_image.astype(np.float32) / 255.0) + # Input paths + if args.path_rgb: + config["paths_img"] = args.path_rgb + if args.path_nrg: + config["paths_img_nrg"] = args.path_nrg + if args.path_mask: + config["paths_mask"] = args.path_mask - H = hsv[:, :, 0] - S = hsv[:, :, 1] - V = hsv[:, :, 2] + # Thresholds + if args.hue_min is not None: + config["hue_min"] = args.hue_min + if args.hue_max is not None: + config["hue_max"] = args.hue_max + if args.sat_thr is not None: + config["sat_thr"] = args.sat_thr + if args.val_thr is not None: + config["val_thr"] = args.val_thr - hue_mask = (H > hue_min) & (H < hue_max) - saturation_mask = S > sat_thr - value_mask = V > val_thr - - mask_rgb = hue_mask & saturation_mask & value_mask - return mask_rgb.astype(np.uint8) - -def generate_segment_mask_nrg(nrg_image): - nrg = nrg_image.astype(np.float32) - - nir = nrg[:,:,0] - red = nrg[:,:,1] - green = nrg[:,:,2] - - nir_norm = (nir - nir.min()) / (nir.max() - nir.min() + 1e-6) - red_norm = (red - red.min()) / (red.max() - red.min() + 1e-6) - green_norm = (green - green.min()) / (green.max() - green.min() + 1e-6) - - mask = ( - (nir_norm < 0.40) & - (red_norm > 0.55) & - (green_norm > 0.55) + # General + if args.num_images is not None: + config["num_images"] = args.num_images + if args.num_compare is not None: + config["num_compare"] = args.num_compare + + # === Load image paths === + rgb_paths = sorted(glob.glob(config["paths_img"])) + nrg_paths = sorted(glob.glob(config["paths_img_nrg"])) + mask_paths = sorted(glob.glob(config["paths_mask"])) + + assert rgb_paths and nrg_paths and mask_paths, "No input files found" + + if config["num_images"] is not None: + rgb_paths = rgb_paths[:config["num_images"]] + nrg_paths = nrg_paths[:config["num_images"]] + mask_paths = mask_paths[:config["num_images"]] + + # === Number of images used for evaluation === + n = min( + config["num_compare"], + len(rgb_paths), + len(nrg_paths), + len(mask_paths) ) - return mask.astype(np.uint8) - -def connection_mask(rgb_image, nrg_image, hue_min, hue_max, sat_thr, val_thr): - - mask_rgb = generate_segment_mask_rgb( - rgb_image, hue_min, hue_max, sat_thr, val_thr - ).astype(np.uint8) - - mask_nrg = generate_segment_mask_nrg(nrg_image).astype(np.uint8) - - combined = np.zeros_like(mask_rgb, dtype=bool) - - combined[(mask_rgb == 1) & (mask_nrg == 1)] = 1 - combined[(mask_rgb == 1) & (mask_nrg == 0)] = 1 - combined[(mask_rgb == 0) & (mask_nrg == 1)] = 0 - combined[(mask_rgb == 0) & (mask_nrg == 0)] = 0 - - - combined = morphology.binary_opening(combined, morphology.disk(1)) - combined = morphology.binary_closing(combined, morphology.disk(2)) - combined = morphology.remove_small_objects(combined, min_size=50) - combined = morphology.remove_small_holes(combined, area_threshold=100) - combined = mask_rgb & morphology.binary_dilation(mask_nrg, morphology.disk(2)) - combined = morphology.binary_closing(combined, morphology.disk(4)) - - - - return combined.astype(np.uint8) - -def combined_masks(img, nrg): - mask_rgb = generate_segment_mask_rgb( - img, hue_min, hue_max, sat_thr, val_thr - ).astype(bool) - - mask_nrg = generate_segment_mask_nrg(nrg).astype(bool) - - combined = mask_rgb | mask_nrg - - return combined.astype(np.uint8) - - - -def iou(mask1, mask2): - inter = np.logical_and(mask1, mask2) - all = np.logical_or(mask1, mask2) - - inter_sum = np.sum(inter) - all_sum = np.sum(all) - - iou_score = inter_sum / all_sum - return iou_score - -def preview_loaded_images(num_images): - if len(paths_img) == 0: - print("No images found") - return - preview_paths = paths_img[:num_images] - preview_paths_nrg = paths_img_nrg[:num_images] - preview_paths_mask = paths_mask[:num_images] - - idx = 0 - - fig, axes = plt.subplots(1, 3, figsize=(18, 6)) - - def draw(): - img = io.imread(preview_paths[idx]) - nrg = io.imread(preview_paths_nrg[idx]) - mask = cv2.imread(preview_paths_mask[idx], cv2.IMREAD_GRAYSCALE) - - axes[0].imshow(img) - axes[0].set_title("RGB Image") - - axes[1].imshow(nrg) - axes[1].set_title("NRG Image") - - axes[2].imshow(mask, cmap="gray") - axes[2].set_title("Ground Truth Mask") - - for ax in axes: - ax.axis("off") - - fig.suptitle( - f"{idx+1}/{len(preview_paths)} | {os.path.basename(preview_paths[idx])}", - fontsize=14 + results = [] + # === Main processing loop === + for i in range(n): + rgb = io.imread(rgb_paths[i]) + nrg = io.imread(nrg_paths[i]) + gt = cv2.imread(mask_paths[i], cv2.IMREAD_GRAYSCALE) + + rgb_mask = generate_segment_mask_rgb( + rgb, + config["hue_min"], + config["hue_max"], + config["sat_thr"], + config["val_thr"] ) - fig.canvas.draw_idle() - - def on_key(event): - nonlocal idx - if event.key == "right": - idx = (idx + 1) % len(preview_paths) - draw() - elif event.key == "left": - idx = (idx - 1) % len(preview_paths) - draw() - - fig.canvas.mpl_connect("key_press_event", on_key) - draw() - plt.show() - - -def run_evaluation(): - comparison_results.clear() - - for i in range(min(num_compare, len(paths_img))): - img_path = paths_img[i] - nrg_path = paths_img_nrg[i] - main_mask_path = paths_mask[i] - - img = io.imread(img_path) - nrg = io.imread(nrg_path) - generated_mask = connection_mask(img, nrg, hue_min, hue_max, sat_thr, val_thr); - rgb_mask = generate_segment_mask_rgb(img, hue_min, hue_max, sat_thr, val_thr); - nrg_mask = generate_segment_mask_nrg(nrg); - combined_mask = combined_masks(img, nrg) - - main_mask = cv2.imread(main_mask_path, cv2.IMREAD_GRAYSCALE); - - iou_score = iou(generated_mask, main_mask); - - comparison_results.append({ - 'original_image': img, - 'generated_mask': generated_mask, - 'combined_mask': combined_mask, - 'rgb_mask': rgb_mask, - 'nrg_mask': nrg_mask, - 'main_mask': main_mask, - 'iou_score': iou_score, - 'image_filename': os.path.basename(img_path) - }); - + nrg_mask = generate_segment_mask_nrg(nrg) + fused = fuse_masks(rgb_mask, nrg_mask) -def show_comparison_results(): - if len(comparison_results) == 0: - print("No comparison results to show") - return + score = iou(fused, gt) - idx = 0 + results.append({ + "image_filename": os.path.basename(rgb_paths[i]), + "generated_mask": fused, + "main_mask": gt, + "iou_score": score + }) - fig, axes = plt.subplots(2, 3, figsize=(16, 10)) - axes = axes.flatten() + # === Evaluation === + logging.info(f"{n} images have been loaded to processing\n and {len(results)} images have been processed correctly") + cm = compute_confusion_matrix(results) + logging.info(f"Confusion Matrix:\n{cm}") - def draw(): - result = comparison_results[idx] + # === Save results === + save_iou_results(results) + save_masks(results, config["output_dir"]) - axes[0].imshow(result['original_image']) - axes[0].set_title(result['image_filename']) - - axes[1].imshow(result['generated_mask'], cmap='gray') - axes[1].set_title("Generated Mask") - - axes[2].imshow(result['main_mask'], cmap='gray') - axes[2].set_title("Main Mask") - - axes[3].imshow(result['rgb_mask'], cmap='gray') - axes[3].set_title("RGB Mask") - - axes[4].imshow(result['nrg_mask'], cmap='gray') - axes[4].set_title("NRG Mask") - - axes[5].imshow(result['combined_mask'], cmap='gray') - axes[5].set_title("Combined RGB + NRG") - - for ax in axes: - ax.axis("off") - - fig.suptitle( - f"{idx+1}/{len(comparison_results)} | IoU: {result['iou_score']*100:.1f}%", - fontsize=14 - ) - - fig.canvas.draw_idle() - - def on_key(event): - nonlocal idx - if event.key == "right": - idx = (idx + 1) % len(comparison_results) - draw() - elif event.key == "left": - idx = (idx - 1) % len(comparison_results) - draw() - - fig.canvas.mpl_connect("key_press_event", on_key) - draw() - plt.show() - -def result_all_stats(): - iou_scores = [result['iou_score'] for result in comparison_results] - image_filenames = [os.path.basename(result['image_filename']) for result in comparison_results] - - plt.figure(figsize=(15, 7)) - plt.bar(image_filenames, iou_scores, color='skyblue') - plt.xlabel('Nazwa pliku obrazu') - plt.ylabel('Wynik IoU') - plt.title('Comparison results IoUs for all sets') - plt.xticks(rotation=45, ha='right') - plt.ylim(0, 1) - plt.tight_layout() - plt.show() - -def confusion_matrix_all(): - y_main_all = [] - y_pred_all = [] - - for result in comparison_results: - main_mask = result['main_mask'].astype(bool).flatten() - pred_mask = result['generated_mask'].astype(bool).flatten() - - y_main_all.extend(main_mask) - y_pred_all.extend(pred_mask) - - cm = confusion_matrix(y_main_all, y_pred_all) - print("CONFUSION MATRIX (for all images)") - print("TN FP") - print("FN TP") - print(cm) - return confusion_matrix(y_main_all, y_pred_all) - -def plot_confusion_matrix(cm, title="Confusion Matrix – normalized (%)"): - - labels = ["Background", "Dead Tree"] - - cm = cm.astype(np.float32) - cm_percent = cm / cm.sum(axis=1, keepdims=True) * 100 - - fig, ax = plt.subplots(figsize=(6, 5)) - im = ax.imshow(cm_percent, vmin=0, vmax=100) - - ax.set_title(title, fontsize=14, pad=10) - ax.set_xlabel("Prediction", fontsize=12) - ax.set_ylabel("Main Truth", fontsize=12) - - ax.set_xticks([0, 1]) - ax.set_yticks([0, 1]) - ax.set_xticklabels(labels) - ax.set_yticklabels(labels) - - # Explicit cell labels - cell_names = [ - ["TN\n(Background → Background)", "FP\n(Background → Dead Tree)"], - ["FN\n(Dead Tree → Background)", "TP\n(Dead Tree → Dead Tree)"] - ] - - for i in range(2): - for j in range(2): - value = cm_percent[i, j] - text_color = "white" if value > 50 else "black" - - ax.text( - j, i, - f"{cell_names[i][j]}\n{value:.1f}%", - ha="center", - va="center", - color=text_color, - fontsize=11, - fontweight="bold" - ) - - cbar = fig.colorbar(im, ax=ax) - cbar.set_label("Percentage of pixels (%)") - - plt.tight_layout() - plt.show() - - - -def search_best_thresholds(hue_ranges, sats, vals): - rgb_images = [io.imread(p) for p in paths_img[:num_compare]] - main_masks = [cv2.imread(p, cv2.IMREAD_GRAYSCALE).astype(bool) - for p in paths_mask[:num_compare]] - - hsv_images = [color.rgb2hsv(img.astype(np.float32) / 255.0) for img in rgb_images] - - best_iou = -1 - best_params = (None, None, None, None) - - hue_iou = [] - sat_iou = [] - val_iou = [] - - for (hmin, hmax) in hue_ranges: - ious = [] - for s in sats: - for v in vals: - for hsv, main in zip(hsv_images, main_masks): - H, S, V = hsv[:,:,0], hsv[:,:,1], hsv[:,:,2] - mask = (H > hmin) & (H < hmax) & (S > s) & (V > v) - - inter = np.logical_and(mask, main).sum() - union = np.logical_or(mask, main).sum() - ious.append(inter / union if union != 0 else 0) - - hue_iou.append(np.mean(ious)) - - for s in sats: - ious = [] - for (hmin, hmax) in hue_ranges: - for v in vals: - for hsv, main in zip(hsv_images, main_masks): - H, S, V = hsv[:,:,0], hsv[:,:,1], hsv[:,:,2] - mask = (H > hmin) & (H < hmax) & (S > s) & (V > v) - - inter = np.logical_and(mask, main).sum() - union = np.logical_or(mask, main).sum() - ious.append(inter / union if union != 0 else 0) - - sat_iou.append(np.mean(ious)) - - for v in vals: - ious = [] - for (hmin, hmax) in hue_ranges: - for s in sats: - for hsv, main in zip(hsv_images, main_masks): - H, S, V = hsv[:,:,0], hsv[:,:,1], hsv[:,:,2] - mask = (H > hmin) & (H < hmax) & (S > s) & (V > v) - - inter = np.logical_and(mask, main).sum() - union = np.logical_or(mask, main).sum() - ious.append(inter / union if union != 0 else 0) - - val_iou.append(np.mean(ious)) - - for (hmin, hmax) in hue_ranges: - for s in sats: - for v in vals: - ious = [] - for hsv, main in zip(hsv_images, main_masks): - H, S, V = hsv[:,:,0], hsv[:,:,1], hsv[:,:,2] - mask = (H > hmin) & (H < hmax) & (S > s) & (V > v) - - inter = np.logical_and(mask, main).sum() - union = np.logical_or(mask, main).sum() - ious.append(inter / union if union != 0 else 0) - - mean_iou = np.mean(ious) - if mean_iou > best_iou: - best_iou = mean_iou - best_params = (hmin, hmax, s, v) - - - plt.figure(figsize=(18,5)) - - plt.subplot(1,3,1) - plt.bar(range(len(hue_ranges)), hue_iou) - plt.xticks(range(len(hue_ranges)), - [f"{h[0]:.2f}-{h[1]:.2f}" for h in hue_ranges], - rotation=45) - plt.title("IoU vs Hue range") - plt.ylabel("Mean IoU") - - plt.subplot(1,3,2) - plt.bar(range(len(sats)), sat_iou) - plt.xticks(range(len(sats)), [f"{s:.2f}" for s in sats]) - plt.title("IoU vs Saturation") - - plt.subplot(1,3,3) - plt.bar(range(len(vals)), val_iou) - plt.xticks(range(len(vals)), [f"{v:.2f}" for v in vals]) - plt.title("IoU vs Value") - - plt.tight_layout() - plt.show() - - print("Best IoU:", best_iou) - print("Best params:") - print("Hue:", best_params[0], "-", best_params[1]) - print("S :", best_params[2]) - print("V :", best_params[3]) - - return best_params, best_iou - -def save_iou_results(filename="iou_results.csv"): - if len(comparison_results) == 0: - print("No IoU results to save") - return - - script_dir = os.path.dirname(os.path.abspath(__file__)) - save_path = os.path.join(script_dir, filename) - - with open(save_path, "w", encoding="utf-8") as f: - f.write("image_name - iou\n") - for result in comparison_results: - f.write(f"{result['image_filename']} - {result['iou_score']*100:.2f}%\n") - - print(f"IoU results saved to: {save_path}") - -# Saves generated segmentation masks to a specified output directory -def save_generated_masks(output_dir): - if len(comparison_results) == 0: - print("No generated masks to save") - return - - os.makedirs(output_dir, exist_ok=True) - - for result in comparison_results: - mask = (result["generated_mask"] * 255).astype(np.uint8) - filename = os.path.splitext(result["image_filename"])[0] + "_mask.png" - save_path = os.path.join(output_dir, filename) - - cv2.imwrite(save_path, mask) - - print(f"Generated masks saved to: {output_dir}") - - -def main(): - output_dir = config["output_dir"] - - preview_loaded_images(num_images) - run_evaluation() - show_comparison_results() - result_all_stats() - cm = confusion_matrix_all() - plot_confusion_matrix(cm) - save_iou_results() - save_generated_masks(output_dir) + logging.info("Pipeline finished successfully") if __name__ == "__main__": - main() - -search_best_thresholds( - hue_ranges=[(0.7, 0.8), (0.75, 0.85), (0.8, 0.9)], - sats=[0.15, 0.2], - vals=[0.5, 0.55] -) + main() diff --git a/src/cli.py b/src/cli.py new file mode 100644 index 0000000..a6f7f19 --- /dev/null +++ b/src/cli.py @@ -0,0 +1,85 @@ +import argparse + +def parse_args(): + parser = argparse.ArgumentParser( + description=( + "Dead Tree Segmentation Pipeline (RGB + NRG)\n\n" + "The pipeline performs classical image segmentation using RGB and NRG data,\n" + "computes evaluation metrics (IoU, confusion matrix) and saves results to disk." + ), + formatter_class=argparse.RawTextHelpFormatter + ) + + # === Configuration === + parser.add_argument( + "-c", "--config", + type=str, + default="config.yaml", + help="Path to YAML configuration file (default: config.yaml)" + ) + + # === Segmentation thresholds === + parser.add_argument( + "-h_min", "--hue_min", + type=float, + help="Minimum HSV hue threshold for RGB segmentation" + ) + parser.add_argument( + "-h_max", "--hue_max", + type=float, + help="Maximum HSV hue threshold for RGB segmentation" + ) + parser.add_argument( + "-s", "--sat_thr", + type=float, + help="HSV saturation threshold for RGB segmentation" + ) + parser.add_argument( + "-v", "--val_thr", + type=float, + help="HSV value (brightness) threshold for RGB segmentation" + ) + + # === General pipeline control === + parser.add_argument( + "-n_img", "--num_images", + type=int, + help="Number of images to load or preview from the dataset" + ) + parser.add_argument( + "-n_cmp", "--num_compare", + type=int, + help="Number of images used for quantitative evaluation" + ) + + # === Input / Output paths === + parser.add_argument( + "-o", "--output_dir", + type=str, + help="Output directory for generated masks, metrics and logs" + ) + parser.add_argument( + "-p_rgb", "--path_rgb", + type=str, + help="Path or glob pattern to RGB images (e.g. data/RGB_images/*.png)" + ) + parser.add_argument( + "-p_nrg", "--path_nrg", + type=str, + help="Path or glob pattern to NRG images (e.g. data/NRG_images/*.png)" + ) + parser.add_argument( + "-p_mask", "--path_mask", + type=str, + help="Path or glob pattern to ground truth masks (e.g. data/masks/*.png)" + ) + + # === Logging === + parser.add_argument( + "--log-level", + default="INFO", + choices=["DEBUG", "INFO", "WARNING", "ERROR"], + help="Logging verbosity level (default: INFO)" + ) + + return parser.parse_args() diff --git a/src/config_loader.py b/src/config_loader.py new file mode 100644 index 0000000..0a74132 --- /dev/null +++ b/src/config_loader.py @@ -0,0 +1,23 @@ +import yaml + +def load_config(path="config.yaml"): + with open(path, "r", encoding="utf-8") as f: + cfg = yaml.safe_load(f) + + config = { + "paths_img": cfg["paths"]["rgb"], + "paths_img_nrg": cfg["paths"]["nrg"], + "paths_mask": cfg["paths"]["masks"], + "output_dir": cfg["paths"]["output"], + "hue_min": float(cfg["thresholds"]["hue_min"]), + "hue_max": float(cfg["thresholds"]["hue_max"]), + "sat_thr": float(cfg["thresholds"]["sat_thr"]), + "val_thr": float(cfg["thresholds"]["val_thr"]), + "num_images": int(cfg["general"]["num_images"]), + "num_compare": int(cfg["general"]["num_compare"]), + } + + if config["num_compare"] > config["num_images"]: + raise ValueError("num_compare cannot exceed num_images") + + return config diff --git a/src/evaluation.py b/src/evaluation.py new file mode 100644 index 0000000..e99f6f8 --- /dev/null +++ b/src/evaluation.py @@ -0,0 +1,20 @@ +import numpy as np +import logging +from sklearn.metrics import confusion_matrix + +logger = logging.getLogger("dead_tree") + +def iou(mask1, mask2): + inter = np.logical_and(mask1, mask2).sum() + union = np.logical_or(mask1, mask2).sum() + return inter / union if union > 0 else 0 + +def compute_confusion_matrix(results): + y_true, y_pred = [], [] + + for r in results: + y_true.extend(r["main_mask"].astype(bool).flatten()) + y_pred.extend(r["generated_mask"].astype(bool).flatten()) + + logger.info("Confusion matrix computed") + return confusion_matrix(y_true, y_pred) diff --git a/src/io_utils.py b/src/io_utils.py new file mode 100644 index 0000000..100d7d8 --- /dev/null +++ b/src/io_utils.py @@ -0,0 +1,25 @@ +import os +import cv2 +import logging + +logger = logging.getLogger("dead_tree") + +def save_iou_results(results, filename="iou_results.csv"): + with open(filename, "w", encoding="utf-8") as f: + f.write("image_name,iou\n") + for r in results: + f.write(f"{r['image_filename']},{r['iou_score']*100:.2f}%\n") + + logger.info(f"IoU results saved to {filename}") + +def save_masks(results, output_dir): + os.makedirs(output_dir, exist_ok=True) + + for r in results: + path = os.path.join( + output_dir, + os.path.splitext(r["image_filename"])[0] + "_mask.png" + ) + cv2.imwrite(path, r["generated_mask"] * 255) + + logger.info(f"Masks saved to {output_dir}") diff --git a/src/logger.py b/src/logger.py new file mode 100644 index 0000000..0b0c771 --- /dev/null +++ b/src/logger.py @@ -0,0 +1,29 @@ +import logging +import sys + +def setup_logger( + name: str = "dead_tree", + level: int = logging.INFO, + log_file: str | None = None +) -> logging.Logger: + logger = logging.getLogger(name) + logger.setLevel(level) + + if logger.handlers: + return logger + + formatter = logging.Formatter( + "[%(asctime)s] [%(levelname)s] %(name)s: %(message)s", + datefmt="%Y-%m-%d %H:%M:%S" + ) + + console_handler = logging.StreamHandler(sys.stdout) + console_handler.setFormatter(formatter) + logger.addHandler(console_handler) + + if log_file: + file_handler = logging.FileHandler(log_file, encoding="utf-8") + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + + return logger diff --git a/src/optimization.py b/src/optimization.py new file mode 100644 index 0000000..7bd82b7 --- /dev/null +++ b/src/optimization.py @@ -0,0 +1,21 @@ +import numpy as np +from skimage import color + +def search_best_thresholds(hsv_images, masks, hue_ranges, sats, vals): + best_iou, best_params = -1, None + + for hmin, hmax in hue_ranges: + for s in sats: + for v in vals: + scores = [] + for hsv, gt in zip(hsv_images, masks): + H,S,V = hsv[:,:,0], hsv[:,:,1], hsv[:,:,2] + mask = (H>hmin)&(Hs)&(V>v) + inter = (mask & gt).sum() + union = (mask | gt).sum() + scores.append(inter/union if union else 0) + mean_iou = np.mean(scores) + if mean_iou > best_iou: + best_iou, best_params = mean_iou, (hmin,hmax,s,v) + + return best_params, best_iou diff --git a/src/segmentation.py b/src/segmentation.py new file mode 100644 index 0000000..aa26f8f --- /dev/null +++ b/src/segmentation.py @@ -0,0 +1,22 @@ +import numpy as np +from skimage import color, morphology + +def generate_segment_mask_rgb(rgb_image, hue_min, hue_max, sat_thr, val_thr): + hsv = color.rgb2hsv(rgb_image.astype(np.float32) / 255.0) + H, S, V = hsv[:,:,0], hsv[:,:,1], hsv[:,:,2] + return ((H > hue_min) & (H < hue_max) & (S > sat_thr) & (V > val_thr)).astype(np.uint8) + +def generate_segment_mask_nrg(nrg_image): + nrg = nrg_image.astype(np.float32) + nir, red, green = nrg[:,:,0], nrg[:,:,1], nrg[:,:,2] + + nir = (nir - nir.min()) / (nir.max() - nir.min() + 1e-6) + red = (red - red.min()) / (red.max() - red.min() + 1e-6) + green = (green - green.min()) / (green.max() - green.min() + 1e-6) + + return ((nir < 0.4) & (red > 0.55) & (green > 0.55)).astype(np.uint8) + +def fuse_masks(mask_rgb, mask_nrg): + combined = mask_rgb & morphology.binary_dilation(mask_nrg, morphology.disk(2)) + combined = morphology.binary_closing(combined, morphology.disk(4)) + return combined.astype(np.uint8) diff --git a/temp_config.yaml b/temp_config.yaml index cd17f5a..332239d 100644 --- a/temp_config.yaml +++ b/temp_config.yaml @@ -2,7 +2,7 @@ paths: rgb: ./data/RGB_images/*.png # nrg: ./data/NRG_images/*.png # masks: ./data/masks/*.png # - output: ./data/generated_masks/ # + output: ./data_examples/example_generated_mask/ # thresholds: hue_min: 0.75 From 14478850e00590447a41214c422cd1c285550653 Mon Sep 17 00:00:00 2001 From: MMedenski Date: Sat, 17 Jan 2026 20:39:20 +0100 Subject: [PATCH 3/7] Delete iou_results.csv --- iou_results.csv | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 iou_results.csv diff --git a/iou_results.csv b/iou_results.csv deleted file mode 100644 index f3cf13e..0000000 --- a/iou_results.csv +++ /dev/null @@ -1,3 +0,0 @@ -image_name,iou -RGB_ar037_2019_n_06_04_0.png,52.10% -RGB_ar037_2019_n_07_05_0.png,17.80% From 13323bf05b6697750ed5be9b0d4dc9706929a7dc Mon Sep 17 00:00:00 2001 From: MMedenski Date: Sat, 17 Jan 2026 21:07:26 +0100 Subject: [PATCH 4/7] Update README.md --- README.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 817155b..5386d08 100644 --- a/README.md +++ b/README.md @@ -106,17 +106,19 @@ This allows for qualitative inspection of segmentation accuracy and typical erro --- -### 6️⃣ Figure X -IoU scores for all evaluated image sets. -The chart shows segmentation performance for each image, measured as the overlap between the predicted mask and the ground truth. - -

- -
- Chart for comparison results(IoU)[img_comparison.png] -

- - +### 6️⃣ Output of results in csv +A table containing IoU scores for evaluated images is automatically saved. +File: +``` +iou_results.csv +``` +Format: +``` +image_filename,iou +image_01.png,0.7421 +image_02.png,0.8153 +image_03.png,0.6910 +``` --- --- From d7a0b27e44bf5e9ab1fc969f87cd9d4b07120a72 Mon Sep 17 00:00:00 2001 From: MMedenski Date: Sat, 17 Jan 2026 21:14:09 +0100 Subject: [PATCH 5/7] Update README.md --- README.md | 66 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 5386d08..02af137 100644 --- a/README.md +++ b/README.md @@ -114,10 +114,17 @@ iou_results.csv ``` Format: ``` -image_filename,iou -image_01.png,0.7421 -image_02.png,0.8153 -image_03.png,0.6910 +image_name - iou +RGB_ar037_2019_n_06_04_0.png - 52.10% +RGB_ar037_2019_n_07_05_0.png - 17.80% +RGB_ar037_2019_n_07_17_1.png - 0.00% +RGB_ar037_2019_n_08_14_0.png - 41.98% +RGB_ar037_2019_n_13_13_0.png - 15.57% +RGB_ar039_2019_n_02_04_0.png - 50.59% +RGB_ar039_2019_n_02_10_0.png - 13.10% +RGB_ar039_2019_n_03_04_0.png - 14.69% +RGB_ar039_2019_n_04_10_0.png - 8.10% +RGB_ar039_2019_n_04_13_0.png - 44.99% ``` --- @@ -179,14 +186,18 @@ python main.py --help ```bash python main.py \ - -c --config config.yaml \ - -ni --num-images 5 \ - -nc --num-compare 5 \ - -h_min --hue-min 0.75 \ - -h_max --hue-max 0.95 \ - -s --sat-thr 0.25 \ - -v --val-thr 0.55 \ - -o --output-dir output/final_run/ + -h, --help show this help message and exit + -c, --config CONFIG / Path to YAML configuration file (default: config.yaml) + -h_min, --hue_min HUE_MIN / Minimum HSV hue threshold for RGB segmentation + -h_max, --hue_max HUE_MAX / Maximum HSV hue threshold for RGB segmentation + -s, --sat_thr SAT_THR HSV / saturation threshold for RGB segmentation + -v, --val_thr VAL_THR HSV value / (brightness) threshold for RGB segmentation + -n_img, --num_images NUM_IMAGES / Number of images to load or preview from the dataset + -n_cmp, --num_compare NUM_COMPARE / Number of images used for quantitative evaluation + -o, --output_dir OUTPUT_DIR / Output directory for generated masks, metrics and logs + -p_rgb, --path_rgb PATH_RGB / Path or glob pattern to RGB images (e.g. data/RGB_images/*.png) + -p_nrg, --path_nrg PATH_NRG / Path or glob pattern to NRG images (e.g. data/NRG_images/*.png) + -p_mask, --path_mask PATH_MASK / Path or glob pattern to ground truth masks (e.g. data/masks/*.png) ``` > Command-line arguments have **higher priority** than values defined in `config.yaml`. @@ -249,6 +260,11 @@ pip install -r requirements.txt ```bash python main.py ``` +or with CLI overrides: + +```bash +python main.py -n_cmp 5 -o data/generated +``` Optional parameters can be supplied via the CLI. @@ -257,19 +273,33 @@ Optional parameters can be supplied via the CLI. ## 📁 Project Structure ``` -Dead-Tree-Segmentation-main/ +Dead-Tree-Segmentation/ │ -├── main.py # Main pipeline script -├── requirements.txt # Project dependencies -├── temp_config.yaml # Configuration template -├── config.yaml # Local config (gitignored) +├── main.py +├── requirements.txt +├── temp_config.yaml +├── config.yaml # gitignored ├── README.md ├── .gitignore +│ +├── src/ +│ ├── cli.py +│ ├── config_loader.py +│ ├── segmentation.py +│ ├── evaluation.py +│ ├── io_utils.py +│ └── logger.py +│ ├── data/ │ ├── RGB_images/ │ ├── NRG_images/ │ └── masks/ -└── output/ # Generated results +│ +├── data_examples/ +│ ├── example_generated_mask/ +│ └── example_iou_results.csv +│ + ``` --- From 1a0696cd3f1f6f4cefeca7897245e6813dfc0431 Mon Sep 17 00:00:00 2001 From: MMedenski Date: Sat, 17 Jan 2026 21:15:27 +0100 Subject: [PATCH 6/7] Add files via upload --- main.py | 6 +----- src/__pycache__/cli.cpython-313.pyc | Bin 0 -> 2628 bytes src/__pycache__/config_loader.cpython-313.pyc | Bin 0 -> 1291 bytes src/__pycache__/evaluation.cpython-313.pyc | Bin 0 -> 1385 bytes src/__pycache__/io_utils.cpython-313.pyc | Bin 0 -> 1636 bytes src/__pycache__/segmentation.cpython-313.pyc | Bin 0 -> 2393 bytes src/io_utils.py | 4 +--- 7 files changed, 2 insertions(+), 8 deletions(-) create mode 100644 src/__pycache__/cli.cpython-313.pyc create mode 100644 src/__pycache__/config_loader.cpython-313.pyc create mode 100644 src/__pycache__/evaluation.cpython-313.pyc create mode 100644 src/__pycache__/io_utils.cpython-313.pyc create mode 100644 src/__pycache__/segmentation.cpython-313.pyc diff --git a/main.py b/main.py index 9bff311..08fe2ad 100644 --- a/main.py +++ b/main.py @@ -8,12 +8,8 @@ from src.evaluation import iou, compute_confusion_matrix from src.io_utils import save_iou_results, save_masks -import glob +import glob, logging, cv2, os from skimage import io -import cv2 -import os -import logging - def main(): # === Logging setup === diff --git a/src/__pycache__/cli.cpython-313.pyc b/src/__pycache__/cli.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ed3df396a7676a631e25b269eb4a977e0b7ab0f GIT binary patch literal 2628 zcmbVN&2ke*5MKRRvZS?zgRvp}48#du@XAS5OcfL)0c;Ev5TJpiQYNJuuSTnh_b)TM z!rl`{j$C+%yg{BI=a3xiTDT>b+)OzSke-p`EFo5kY?XF?2Gr*Wa-toCBkmV2|m_;1K zTw+rs(nOJ9o{c04@!FCGwMj@8%9ig2QAh=%J>o?DMO_+lW(hQZ%YV3lEZ^%uG$i8^ znBO!MgCIQ!;#5RVC{XC5HWM3lBb!v(R9Kt^GW}#yW=6u0@|Maj&YD(SGEK?J?6}hZ z*0+f3#24<7uq&Cqxq9ceT9w%m?`PCu4z!?6I|P=yG;pdg!^kE1a4Vomao|q!UFrn! znAvT)%!|iOvl~%m*6%Qnxsi)*;;#@)qOc1EbbY6dAWzktbkB?|AA?Qe^2qk-$mTvB znDskk^Bw4*b|J$B30tsmY|1QPFsyNOG7#O9(XdM@dPzphErY(Sb7pr#j|yS@M@dkI z*J7?6S54E4Trfc>BL1`yx$Bf8ztfkF+EB*_MM7HwPJ4_#Mjh_EKu?u$1Pv9JbNL2bUw$d5wMVyn$~eBrLLK~sbp+EOM^6e(o`!x0&^)c% zbye9S%ntgpc&;9JwxLKpZts&y`#@58wB9C#*?*80AyZjJTp6UQ2Gj42!gOelI!XRk!C;ax-VF`g;a3r#3>dd$K~HzQnb20v#3xf!zd*s!fn`kL6eEA+>Ye!bx#g? za=1wzKcJi8O*oM^hF3vSCGB<#ViYpTiI69S4#+J`a?A^pvf`2eVPujqHR2)zE2HG& z>z5wMtJ0KLrJ24eO{+^h8xA^Q-Xp=SN0!6t!4{u}IQ5eM^5RFJd7{12r?>UFU-Y@% zd{+N9^JhsrdU|_y>Ba2QYrQxz_T zS}PRBc55j}?b-QP`qYkoWw)sOdGMFt!C!s4q2eze5I^)Ur~DId@YiLE`t_9m&Gi?R z|BIAA9bbLwDgR2!|7QGx@;Cmc|Io85D*k>dKE3|*PVGoiTiVetq#8V-Rj0Nq$6r*A z|5`cubN=KWY@i00^2BgQf6(}>v}fRzlrFn|J96k}{1^s15%ic&Hb@L{yfcoEzKbij({veC@!H6f#~?SXwUI{&-H++<1UTpk#j9g2 zc}h#2($Y%XNsY6BPexgEP4RK2|G~3o;OEZ3bEojkiTrtm=;zPCKRJo(6Dt}gRiY29 za1OjTe@2LAp*q+@(kI6iF?PVk zqq1JcGR?T*nKrj%qD6zIFNv@oNWujeTTdsb-tiU7L>t z`b4ID8r0#2Og*N~P_P(hf;v*;ZMSZ_)MCEOD%tSd78R_l$;==pfR@a?7wamyHdNIU zq&9p(-L!=`2h|BRc_OdadBr9an*_GEa3XxkpqzU_i9InGGfN*HQBKOZ*J518cd6^h zj87Y^?s(Lci3S{jBsPa~?@_1C?g-(Da$LsMb0ls^Vl*t7G(6t0Enm%Z2}o{kZPsox zYF54%j8$}Ixr_%iusvQenv`46ENb(bFO1sIb&!vVmDaAzs?^XE6;WJt_{AsJR_(x``OZ)lBr~C&V-tFb*56C-$v};FcR49hi&uX13(Z#E;igV%p!?_NN zip#ISzWB`P+=vR-#`1bpSnf~Fgi~QHDlT<5qT*U_>iS1oApU2eeYnz{i)OF)3OD-G zv*BvEdw8>3i9TKLO>expn9V2mzdo8p>HL8krRKuN9VeP!?WI=x`I#5RFMIi0FVeUE z%ueio`)(16d6`-I*H^Rs*^yyc03?dSjfXVdd hu4lF#c2isd6-FQVYD$g>#`rbL{u94|C&dC-{sVOIHN^k` literal 0 HcmV?d00001 diff --git a/src/__pycache__/evaluation.cpython-313.pyc b/src/__pycache__/evaluation.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..332eec08cf0827bd6a62f9c03825b7b9cdc52518 GIT binary patch literal 1385 zcmb_c&2JM|5TE_Y^MatMSMj?0o0PlLN<{iB~rApU))u^Z>_hx zg>W(_0+)b9Bul;YSP;h^`e(#lR7VdgA#vhHF@Hekt-Wp(sZtM&wKKCbv-94}{Kmah zY6Ose-`OBPMkT;}MTye;m<) z3=+REe`)nPHmPt2n^dTfx<#mKxENEY{%FuT!B(1d)nV0D@{`f<0@qA)iz}w>u_Kr! zhL1CktOakh^iJt8_UVXn9|oY26U!#m9^7uYB!nSIc#@oUavHH}=0g^~<%N zzuzuCy1J+4f1O3v!!%GGOj_t58X1gg2CcjBHU65Xmf$FSX8}-UNh%+V4n6@84HT0q zhsYb)g;)`#2NWWzEGjre;ZVbv3@pk2agzMGPg=AAl8}LwczCHYLf|m{yAc?f3cbr? zAyg4AYKbSr^l*gECQsK1CCqS1EqK3wVm{oKOn+N4RoiNM=+DM@l-zR(!YXPw?pBj< zb=|fbJhss=T(t8j0Cs-dGxA`NZI) zwZjX`PNG}G$GMBF1^dU2x5u_`zDtd7OZ)NkPU=zWiP26zrCmMO(Q_}a`}%BGzu3_) z`ugQp@hiiay@~0Lp6%-AI{LZa#(n*pu0Ge%=Y0L!KjYW_jzId@r#M8C4;LOR?2YDL zsrfg_FWNW#WNuH*u~9rbo8S@ZScssa zXbGFZ@L(^3TAo9YtzUNhie=Zl2AN}17$k7(Oe6cCNRsp##$H4EFUY@5jCK=~e1=QY26<%U&-V>|J+u z4XG2Uw3k3t2`-U1awIp7J*NMGO-eu$q*jU)iCZHhMe3!US$i>6k@{A8@6CJjK4#|k zwh;S8|A3U{it@+LryGvg1s1 zjeM$4<)UzpL4K8w0t)C%+<#1+vslb)*?h)9nwBGtoDxk*8)=*n_88_{sQ}W9%BZZ$ zUE^3V*)*H-+C2>gPTYJ8Zk43MdHZV zISXmWc~YjzB}WQX_OevHc*{yFNV6+N$5!m@vX)mIONsNCx5}D{eJjXtH0&>0x~?J2 z8)m^G&Oj>y3+*BW%pjW{Tp@(VCqG-5A}^VkBQKdyHNB*nPS!CjGm%@&nxx-S)-V@r zlv^;YOvN#ZcC!2(j?fk|wAoe#)J-zdH?UU-TZJPrw6?g#ZR>kta<}hL5H|wrfm)>h zH{tykoo}yA9Lk}M8|ybVzpeGe>u(L#E`7Xnb?45`gD2noGFeZ2^^~g(O+B5gb>Dn0 z-+IkKSI-+CNW&)*_=1n7f0(Xy4eSWp)mmg^pC5e_B(8li`Ij9ewST@o5$Ar5OB0_2 z&PV1Lc{hSgvb9O6v-oY<`dfItM&8#&qDGL&o{2d{j+X`CJPf6o|A`UX@t&3@Q$laj zn+27pE7g~vwpOnuH}C!kXiQ3`{NL0McSlse8yBE6;l(L1qc>+^KI}!LIz6o|RpGzb zkBhT&E}-ulwv(}z5S)%~CH7LXZ0b0yYo>;>j+S?A$A43Q#|&|w#jS{0%f><} zyQJj}WMjUZbrvyy&oWFb*ySPt^L+<%x#bZYFiI}CQHYkKUB`LUK?Ihpic_vQ8Il2Y zQRh2^okAO3aQC=CrQO-6j~HkY`+LF&M0x+C=^v*zrR}*re(+_edyP5j=-gOYU)gxL z{_s$~bR>rV6*tX4QQ%jA literal 0 HcmV?d00001 diff --git a/src/__pycache__/segmentation.cpython-313.pyc b/src/__pycache__/segmentation.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab6dee4624c4e85ca001fde1103c77cf89de9355 GIT binary patch literal 2393 zcma)8ZD>ZElbARXNt~JhR+)`i49B#y!UuuTS`$ZuM5&BX z$P8}^px=oiC{$@}%tfgeMj^Y&ZS+0hnNpbZI}tKRswHB9t)NccX!&k|y51xewv>v` zrK$H->yQ+vEHCSFdyiRqcF(Cw^H z&S%B+=!gv~W1SP)Sz?v6^lVBdyqY4mGt)^a-PK7fGjTP&-&aFwKy5UhkQABNCT8T( zL|io(={&J%QhGE!fr;&`G|d=+dgP7~E=;($wHgSt!z9w%9~e0-OXL1iSeE@^SxLxh zT1v;0sy{X%sR}rk;_8ToV-!l%6lO$I%4 zxGwuY@MmkvE$w56x5GP`SDy36G~ zSvl8{k3H}_=qpGC@14m@o=o3TUHx1%JD7{=wZ6jO0(8A!4t@pQ@v?JJcMkp>I#mvx z)?@)PLx-zrN5q##6RX)*L=z)?*h1kO5yDmzvJr8EuX5Kfhp%mbp%&983ey8uKK$#8 z{69Yw4=M9GO1}gR=Jy}~nqC;D9x)s{A_4`ekdV&fS{V8)q(;Sx0b&7IBB*gp1T2pe zivn0wHTs4zoZ@ZN!GFLFOtA;%X*i9(`Wdw3Xe>M2y2E{|^L8K?C^>cmz_zu{d*=JH zQr3HQGQaDt^nfdP^75UILhO;}QD0FidKV@y$(jCTXZ_9QtIgM2=Ym=7_PN}-k7f&9 z5BDwX`?y!%-d#-V+YjiDgW&N0>eV&e9KJeyJ*wC3$VcuE-xI3JgVc)$+qtXl#M;ols;A$>SP5Wys3sIq5hvqtObXO=-9TCK}<>-hu{_(A9ja zqwMZ}>h3NMee>?O7rwsm(>tZ$V96cQU7^geXRg+=tNp2~z0mS`;BoL#@X70?t|KMa z(af Date: Sat, 17 Jan 2026 21:16:08 +0100 Subject: [PATCH 7/7] Delete example_iou_results.csv --- example_iou_results.csv | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 example_iou_results.csv diff --git a/example_iou_results.csv b/example_iou_results.csv deleted file mode 100644 index 8afe8c0..0000000 --- a/example_iou_results.csv +++ /dev/null @@ -1,11 +0,0 @@ -image_name - iou -RGB_ar037_2019_n_06_04_0.png - 52.10% -RGB_ar037_2019_n_07_05_0.png - 17.80% -RGB_ar037_2019_n_07_17_1.png - 0.00% -RGB_ar037_2019_n_08_14_0.png - 41.98% -RGB_ar037_2019_n_13_13_0.png - 15.57% -RGB_ar039_2019_n_02_04_0.png - 50.59% -RGB_ar039_2019_n_02_10_0.png - 13.10% -RGB_ar039_2019_n_03_04_0.png - 14.69% -RGB_ar039_2019_n_04_10_0.png - 8.10% -RGB_ar039_2019_n_04_13_0.png - 44.99%