From 677a8365690760285d05d72be6b56d5a3c4705e8 Mon Sep 17 00:00:00 2001 From: dongchangxi <458593490@qq.com> Date: Sat, 7 Oct 2023 09:09:12 +0800 Subject: [PATCH] first commit --- __pycache__/config.cpython-310.pyc | Bin 0 -> 3472 bytes __pycache__/libs.cpython-310.pyc | Bin 0 -> 9833 bytes __pycache__/libs_db.cpython-310.pyc | Bin 0 -> 1421 bytes config/ModelExportParams.xml | 40 ++++ config/ModelExportParams102.xml | 9 + config/detectMarkers.config.xml | 7 + config/distanceDefinitions.txt | 4 + config/exportControlPoints.config.xml | 6 + config/exportXMP.config.xml | 8 + data/pids.txt | 1 + install.txt | 13 ++ libs/__pycache__/config.cpython-310.pyc | Bin 0 -> 3557 bytes libs/__pycache__/libs.cpython-310.pyc | Bin 0 -> 9910 bytes libs/__pycache__/libs_db.cpython-310.pyc | Bin 0 -> 2552 bytes libs/config.py | 91 ++++++++ libs/libs.py | 272 +++++++++++++++++++++++ libs/libs_db.py | 86 +++++++ main_step1.py | 208 +++++++++++++++++ main_step2.py | 96 ++++++++ main_step3.py | 229 +++++++++++++++++++ test/test.py | 30 +++ test/use.sql | 2 + tools/Remove_grayscale.py | 237 ++++++++++++++++++++ tools/auto_distance.py | 81 +++++++ tools/cal_weight.py | 92 ++++++++ tools/downxmps.py | 82 +++++++ tools/gen_xmps.py | 98 ++++++++ tools/push_cmd.py | 37 +++ tools/upload_x.py | 56 +++++ 29 files changed, 1785 insertions(+) create mode 100644 __pycache__/config.cpython-310.pyc create mode 100644 __pycache__/libs.cpython-310.pyc create mode 100644 __pycache__/libs_db.cpython-310.pyc create mode 100644 config/ModelExportParams.xml create mode 100644 config/ModelExportParams102.xml create mode 100644 config/detectMarkers.config.xml create mode 100644 config/distanceDefinitions.txt create mode 100644 config/exportControlPoints.config.xml create mode 100644 config/exportXMP.config.xml create mode 100644 data/pids.txt create mode 100644 install.txt create mode 100644 libs/__pycache__/config.cpython-310.pyc create mode 100644 libs/__pycache__/libs.cpython-310.pyc create mode 100644 libs/__pycache__/libs_db.cpython-310.pyc create mode 100644 libs/config.py create mode 100644 libs/libs.py create mode 100644 libs/libs_db.py create mode 100644 main_step1.py create mode 100644 main_step2.py create mode 100644 main_step3.py create mode 100644 test/test.py create mode 100644 test/use.sql create mode 100644 tools/Remove_grayscale.py create mode 100644 tools/auto_distance.py create mode 100644 tools/cal_weight.py create mode 100644 tools/downxmps.py create mode 100644 tools/gen_xmps.py create mode 100644 tools/push_cmd.py create mode 100644 tools/upload_x.py diff --git a/__pycache__/config.cpython-310.pyc b/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7791e56d9f630f8886546ca09527f66b5b458a03 GIT binary patch literal 3472 zcmdT`Uvt~W5hp>4)PGsBEz7CnGD@44P6!gDNYRSzslrII$5N$AnmXpV!#Qz}BqRWb z+#O^Q-yz>X56NRcO8XJ+wNGi^JDp_OWDgJ}I;G?4vA|#tySKad+r59wW@aW6`26*| zIqKXtwE;b#h&-{B`7BohfpK+;Jx<2Kn$M!eB9qBhk`fdMHMZ_Vr8}fe)p)s9U#*u{Ewxm#8h;4Swj2Hy z-#*@=<%aY8uu<=wsa&=8;%OJ0jnl2I6?^^R^Ysm{YI(%|qF=H4N6$$G z+t1gxUzY8imrtLa!9nq^GCm5PXT*~z_MDEE{$sgRD%t;7N+iC`ph`Zi(mf{7t51Y@%{ZvE&$v_u z9EH=?YT2}EXlhy&mGOIMTets?gDAtdRVEHeWdqr%yGJYe3}4Vg-$J6&fuNk<)ckGD zXWKo(1(hoM!uEi@7Gdi1wO3(k1IgxcW9`)*XI)O*+@|eN@v1?58F1U{=JqLOS@vID zv~dUh32V|bx*RSPbzUQaBZ*=Kis5QcN?)uMi>`0tk}zY;dcumx7TbZ4470aafo;|; zO18s*Iyi2)F)-xRCpI5t>}uWpsC$;fNiPCpB52llj!D1Qf%$UwXc0sS(teV2N&*k)eG z?rt*PU=HKqqn}40F*{M9angrkj_?Sh{G@s>j<+SE z2wTw#z}3ep9QcwC&k4{7V6B zp~v6#aHrA+bKSthIg&Q>ayYQeEsKu35kYL|qmkgsWc+dDWBE#Bhn#IvT&SFiJ<50d zv&Z^uEWK4D3O?2uR*G}7drG-O{Krr2Zf~RqJSR9i#xx?z4bBmdwM-VUWO!FJcra?S z2E)VI_HeTu^o68uZkyq)uq@GV3toZ&Vetz^};nx-1a z7H5G6dk*pFCTH$Qw*`Uo@fK%hJ!hG<2p2gL%n2mTwejSm{2e$;W~o%J<_(_Lm*_;4 z6P<wVff-#7 z$*=IC^C9^g?#*hrG#H?I8Bi@(GRqbgImW4lUm3p&ek=SFU6`1fe2fH3u`JLI2^^`C zsEE~$VXfj<&o8Mo)^%kyhDDn#uuO7@>@e=CtlxM0;!8(O91$CW7FH8jbUJnm)m7%R zs4`RO^+5L2B#K&bt)|BIBR|uTyB4P|lT=N`oTGO%R!YqCaKo#q$bT!AW<<2>RTgWs z6R^1&$0}5tafH@BvOTQ0xFb}OD}!SQysYkY96U4ds85;fPn&*UjX%PxFL3A++^GrCLy}7vDI=-h^uNL)jdU`d%p}vsKy#p@+sr?WS)~6-XV7{~{~6;(^kn{(naQA? IekZ^G5ys|megFUf literal 0 HcmV?d00001 diff --git a/__pycache__/libs.cpython-310.pyc b/__pycache__/libs.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab0da4b4d9ddcb675c9f701ad2c11262b3ee8493 GIT binary patch literal 9833 zcmbtaYj7MzexIJ#&d#n@56f8g8E)~RoR_mDFiDOQF9Wv094O@M3PELVS#6J`mG|YI zS!^$}iBmRU7Z8$5nWWC>ZgpG&CV_(s;bAc3Lmro;k`Jl8YVske3M*Ura;ds6DOG~= z`*-h3E7=B8H(T@Xo__bg|L@*PPminN`Hzp|-VZZK>7)BG@UauG|6c%HL#v56R?zDCh0#V6t=cfyt5teBWcaFbgnw236o z@ECVau!$ZX=PpW8Ji(JldwCB}A?@S6ybozV@8<(Z2lzTZh;$ua&#yr`$T#pIr0e-c zz6t3y{6@Z+UyBhpAa@=AGIB%wYy5h?1-Xs^#bTeoBw9K|e z>{=Vou*eJ!@lr;Qti7e&v==31vb#K!E9bpDHR?QsFca^+c>QkyaMsWQZI(6lP@89B zIMA8~<#uV0t#7c9HBGLgd`qZ>Yy*(9L2KyT4D~Y_H|GppW7m~J(1p1sg-#yC{)S^qFB4Io;)zAszg2!AxX(M+$!6 zN3kLjUJ#M%2RRY=hYP5*s-jR1A`@+bj1k2rbH10A-3)5l*PbCeSt!fmvzi#d1WVVC zVyU@N?p2GGqobwVLGKnWlO3bQ!lXY^JsPc_DwKJ4?Ep4<`-1=`OR^Mm0sEN#sd>q< zl1xy=1-y9v{jR*OauQO%7+!f)PauP%VeO;2rJ;v)2n zt8;^-!Si}U3xOZh*r?eiYhr6^9MpVW*2KGOGVXX`0Bc&LwJg33updkevaROIe32Gc zNKrN6dvO8)gz99cy#Pcz6&c6?nG(UMu-Z75b%}~7cx9j-Otl7QViN|U*C&qBbqB;~ z@`g;(Ni5*ZR1fj(<+wh_`Z)C!Cnzz7RKUrlQs6viMh zD96o>z+hklPu51FJj&V2xftaPLpK_RqEb+(HD!SAobhNgcABmF*&Ls(`N*|NMdY$I zQH=BgKMSl?bC4`xePYkZofL)XnV{_XKDa&?)FerjqrQBlSP}cZQq@CAO?VI(j_?lE zJO~Q09XpEbe5E{9n2t=0?Ta2NGH3ltIkKjTm0SQ?HLHGsM+Qg_QUtU5QUA(x*{NJU zsEDY4W!6s7K(I1zyKK$bL_=5G>DRChpOoMbL7Xa3f}`QgeDoZ(uF4uJxdv)avs( zw2K)y%{cD|bmz5s(nABPA4(iBaU5cjL>VJJ*4ZJn*^Y)Ay3HhChuJ>}dYY;3HlaIj zg6$JQZbN9lI?`WB{*a^;q&o&P#s#SfK{765 z2pTMM?^$7kn5XQ3j44K`-96L*@>?1QB1$Rd^0<=KYYDTz&3h|^ehd0bitK}^aPc!) zeQP&|`Xjd&EBRc}zjNfW&R5^}4H#DKTR@L}#(s6R-_BO8dx^8fedylVA+dqDpTJ%M zq!<*Cs-v@`6+Rt91GyvT3-4&9^Qs+@oTQf~@(Ix=!HPIRxg(`zz&tw1I_Piqj5dW6 z=Eez!bWWf*!LA@ZBQ&Rsc^x|2!ueWizpLaioi*7CavstWA!Zr`)-r5dZWZc;70xoT z3w2$4cWy7{N|QWyCy5ZBMsT$HZJ&L3UBYYSU_0YP#y9V~Cu2sIUo92_h(##>K-mOV z$rIC1@rvqbArTW1gF>I|I+t-;pgfm>_A%D1S@%(hT5~QV_xJR;2y??xtE_)tN2bHv%pxyhH+7MQtY|YlYKWe@H zd##sV{mF;VU3~YCFMj+CDQ-uOldT+F>K}e|`*3OdFi#KPKQ{c(*zi7SzNT9r9&5ew z?8P^aFPwh6b>^cBr=D3jGuL|R)X2z)xCW!wh5^#uHSM-M9F(|;8a{NF{$y{8rCuOBECpORs zB!C3%RBC^59dc2e7pA7N)G*T<#ST}*K{AnzDyDdVT0ThN5dwD+Ac7Z?`Q&LUt6O## z*HaaV9~jtDG=G|LojPh zHpDgoZepA8wFwf6N?n~~)sU_q+k!eVgU+j1ikQK~OY8SZc#wp@vWWvGWwX|Csf{=^ z<191S%C zf~1nPOvG)JyPbeMW`m3$6SiO=?m%H=!93x9WZ-T2+v)OfYr6OW;68gG|1K^BE9 zzd9x+(c}M%31A@xqu78qY4RB9^uXbcOyf(8d2B9-+Z_A!i%4rJGXH9&DIaVEx!-8x$x?OMkc$st+e`h|6F|>NH=pV@Qa#rL~=l2BaSNnk&Kw8)lX%0(Sraz>Rh8eOupw~|9$ zBgw788VfI-S~z`j;q{jC>B?+Sg$)PCLo+3)f=!N=%GbAUD;;Q38 z?`ULF{aQ*NlAWH&6(QPGmu#h+#mo(i?Gw_IpmsQ!x=9im3OtFFe)^}bNt{6PTAW0~ zRh&Q;t%Fw*(+x0p5T^nO!AOFsLxBwr>mEd6N>Z_iRO)WPWuyP>w~$-3U|Kdv-r`_k+6^$pYFRnKIvte0@C) zlUAF+)CdQ0SV{;$5uGS?_#4PYE=eavp$I|w{*K7?j^w>6ML+x~K3>^Z%g=nnJF2*1 zS!PGjvpG2Oh%#kjiVMscgbiTD21kpYc#39un!rw)#iZ4&CHw?P{%EaC?!5PHwDL)@ zbZA^h@Tm{hh65^386@qA$_I2xQ}VZ2-%Q~t!?^_NpF`KY0nmBKxFZgb)c-0n(%W3| z+aWNe-@XA(;ff#KE``U=WPBFi;1C_WVFciSAT|rZD#J8pfN&H7gd{vo(CroyuCs14 zL9t%aC=ENb;Vawlm3M~l_2vBJPh-r`o;Mo~jn?d;UZFFOaSn}!F;F1ly%r9{A6;Uj zGB_i=slt&O*)5Q6Fq}Vm@7asz&yOu$!c#X#&^D90qD4!=X>l4L>iZ=ry!HkZx~Yg? z*|9SnOhav5#v8a?t?EMhkWw3XI4H$|9`p9$9;koGIH5#1Vl(&#} z9v2yU>HPiw1W;#9u?qxq4a8h1B5%!;iuB~Um@@1O>p@2CEu4pr6S5G6fjWHQ0mNmT zrcIF$>0VL<$O+@SNNTDQYgGw@3u<3hfxGXa+Um7uBmcxf0{<^H01tcNg1NWyqujR z>95bhP5y2D5ET7cquzfcFUIzkAM*T}LV5a*EjMjh>RnSL$Ved>C=kX6=bqIgedMOd zEEdWhip>)GDo&S9hkH9wM!r(2-uV~k_)}U{TB~pMDJ)5zrJto}w=}uBedA82t8PjB z5v}9$CEvV7yn!0IOhL3H-Xice0ZDtbEb$J&UsC2>fX|&{ox;I>_zN{y*9tLhTy+($ znUj!2E(SuSYi9oO&oDFgcaFN#F?8+k1NvOOzrX6tBEEuL#_5s~(&43B2<5nv&l<%^ zqSGlN?0Z-g0#D}QMRl>gJh5@{C)D#p>Xc9=?b{#FVdMa)Pc{X=ipG=cf?u`VO+_mP zuS5eMV^n_&0P(zwmjpgVE$Bzi*5*t4vCT^m{QoQH0P}>0&O<__k@Ld7Ml6hxtI%+yFrY9n#Df$DssxT&R|yFN=dzM` zcZnR^mBN6=wjlo33WGifgU7qsy;~U2ozmKGv_I0_Ufw&wB~;;?o9lM=W)c%fZ~c66 zA$d!@4g_{l*cCYFAE*U!Q2ndRXZf?Rvv}`wr~Jpxl%LJC;{DYlU4cFR=?ji@{&PqA z=gvrq(d@O1M(ia%B0$vLtw_b6q9BU5&3?p$R#=3zsQ5Die@;NMHc3#$JL|51@4rQ@ ze;oitY=)$z)Xg~8T}mzK_Le2_*J!dxYCLZObRM!VmJOyG87WbDqi2#!0QE_#6b^y> z!u=A=nlF);7+Ok+ESvVM3Ex!qusBo;QFhQ>X12>>@H|<=M(~F&AnZ8<3fP&ALutdk zfa~GleiZ=^sO+&uJdDpKlrcq)g}jEqzXz~}11odk3c+Y3%Fc3$sY>AE!WW*C7ZLJu zrG_YSibf0*((KxW+r{>GnB>Xl4VYrP0x}x#j|9NJ_9aP5uCy1I5%9b*qP(h?eAYKE z9tHa9>(jX+NztP!Eb4Dd`v(hEA;tZ#qJi=xzDv395tye=#F=nq>%H<4a#Y>QZmVX+ z-%yiN1P;h{X#@?&(lt8acAQgLf-M>0+?MVRaAv8SLxAL=3UHE%q|EqlQSLDUrwM$Y z04ZkiGJ$0?Q2c;CA19C_FbR<9QC@MrNSCBty1~G;pzx@fobfKoH3|FyfmaAfl0Hkh zGXx|_FRQGmM_Pg%EAU^@!rux&=008bn{WtlFRdpLqNHyZ_F$jxA}ra*2K8;a_&YRS zB@{?A5DMpuX%;Q-SO0-5M^^9<~hdO*T)#B5OR-me_4eEM4zUZ|AVk%ib>7Ec9=YbViprBwt7rs<`z;5Li|D${k`n z(yKS)-4QvRsO*f&Krcolr2JZjET;n*qhYpkUR;=_rY{<;F*l0TCmJG>r={ zY&j*7pwGiPR+rCDXkri^4=lh#HmB<_b38K1Y~Q~J*AjNsuLXr-lx+LFSy%=_{5?jE zOo}q%FEeN&t$bEpy9u}8XGN`ChRpu{5ysWwBs5aA^V7xBBm@CwJB*f5(nT<4Vq7M<$(4OE@z4_m{?Dv z>EaKONRVNYg)`SGMwBdh2USv7IGdhBIDt`52}j?t1}Qg8U=xAe z1ZD_G77i$Pn7~N_a|B)^AnjWc_L4OunMjPw^>&@q3JW^711q(ncPF-}`8NXGir`Zp ih|JO5gcEoAaaHgOcwNVG);rg_eQwH4IoCQ)(*8e-;CW*J literal 0 HcmV?d00001 diff --git a/__pycache__/libs_db.cpython-310.pyc b/__pycache__/libs_db.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5ba57b3640ccd23c7a155553aac9ce806f1a7a35 GIT binary patch literal 1421 zcmZvc&uGsmp%FyTob;NR_Na?lk4g5@>-o~32PdQ#2qwILNiP(>2yl)>C?cF=LH9@!f5PvO z-Hh7l?D256<1r4kgGSJryeEPkdq-3g;nnm#IcEP*N;I9bnw~VCcS4R>&ERIugnymv z5fNa9aJRavWG*w!b*e`Fku3Q>6k``Des~~3Hg9w}mHaQ>tv0(nod~XUs%HvU?(qVI z1U@MFQ1Db`JRgpSn%_SS)T{gf2YY)%65j+<7*4bavl*xYOeEjPY^J4&_odDbiiu1- z({k6a$&81`jW1H2{!gHTA343fZKG`=r+HCskJFL-Q@H7m?R+>;eKEK@ElvDF>c0I< z%SyC{W`(ZNHjSxo2ZL2y`bASq)@fRyQ*o=RVArj(9(t%4dckqvG!B2Ou{ZZm2qn*u z_#W|z4$2*hnp0A(BbP{rHBwoyc}AY&z9aUQ+1-tINDq;I?ZQN@y#y8_R@ZLi+4@D09mna0=6ViqbC3bJm5(LShfTLYeF=Ji2?E`#t6)` zp1jHAR1b>@O0OaDz-j_kMMI?{(3lmw5~o5ql`mtYHqbD7!|A++mecqh`wCZZ`JB== z{Zi@wFsQaPnNzGydQNfFN%*LH;B*hsi<}o9w~m-*qE)knk{WfdnXB?(9-bo*l~0j? zhXgL;nYNB=?>hk<%v;TFN5XsSosmG#k?8DJH)d0;xTXUXS5kj3dlNPtP_1q8gF@*^ zI&S(SK?=<=(N8rt$fP*jOn%o&4YiG?pBB_?kAW@PQ`&X(-~t9o8}k*}NNUr;@?IqT z2`8-MR=8-51p=4Qm>7yYAEenR@f}bTFHc*vVEO$Fx6}Lp$M+V#S69(6?4CPI!W;Ce zW&8zh;;W=(qG>rUv18#H# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/ModelExportParams102.xml b/config/ModelExportParams102.xml new file mode 100644 index 0000000..46b5e39 --- /dev/null +++ b/config/ModelExportParams102.xml @@ -0,0 +1,9 @@ + +
+ \ No newline at end of file diff --git a/config/detectMarkers.config.xml b/config/detectMarkers.config.xml new file mode 100644 index 0000000..456ff59 --- /dev/null +++ b/config/detectMarkers.config.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/config/distanceDefinitions.txt b/config/distanceDefinitions.txt new file mode 100644 index 0000000..c7b44d5 --- /dev/null +++ b/config/distanceDefinitions.txt @@ -0,0 +1,4 @@ +D1 36h11:001 36h11:002 1 +D2 36h11:002 36h11:004 1 +D3 36h11:004 36h11:003 1 +D4 36h11:003 36h11:001 1 \ No newline at end of file diff --git a/config/exportControlPoints.config.xml b/config/exportControlPoints.config.xml new file mode 100644 index 0000000..bc57fdc --- /dev/null +++ b/config/exportControlPoints.config.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/config/exportXMP.config.xml b/config/exportXMP.config.xml new file mode 100644 index 0000000..8f7ebb6 --- /dev/null +++ b/config/exportXMP.config.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/data/pids.txt b/data/pids.txt new file mode 100644 index 0000000..1c78fd2 --- /dev/null +++ b/data/pids.txt @@ -0,0 +1 @@ +101668,102013,101997,102049,102097,102123,102139,102142,102153,102058 \ No newline at end of file diff --git a/install.txt b/install.txt new file mode 100644 index 0000000..0dc7a86 --- /dev/null +++ b/install.txt @@ -0,0 +1,13 @@ +pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple +pip config set install.trusted-host pypi.tuna.tsinghua.edu.cn + +python -m pip install --upgrade pip +pip install oss2 redis MySQLdb pillow numpy opencv-python bpy tqdm pyautogui psutil pywin32 pymysql + + +config +set bin="C:\Program Files\Capturing Reality\RealityCapture\RealityCapture.exe" +%bin% -disableOnlineCommunication -setInstanceName %pid% +%bin% -disableOnlineCommunication -delegateTo %pid% +%bin% -set "appCacheLocation=ProjectFolder" + diff --git a/libs/__pycache__/config.cpython-310.pyc b/libs/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ed4d7f38f4bc60595e1c95f27919efc709d3d9f GIT binary patch literal 3557 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 M<))l#9Vco39}%XMPyhe` literal 0 HcmV?d00001 diff --git a/libs/__pycache__/libs_db.cpython-310.pyc b/libs/__pycache__/libs_db.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cab67098c9c5b60f69dd49483a66ced7393e1960 GIT binary patch literal 2552 zcmbtV&2Jk;6rb5IuRrtA0!hoqVCv!pCuxerA)-~K5{W~bi;+-ri8h{1}W7))-rJY^|}i$JUu8JoT;f zU1f(3=+N3DI|g?LbD-B6SmMUO;+a|U2{GWCEciVzDYU92 z4VaAZY?Uk$dAkeq@wW;uj;O*MmFj&?$&O7)|DtkLdYIuk!0aB`Ghp|;K0`s}2KEE` zkn95D+*O#pW8b%N<=E@eF7YN-B5Ny@Dze(40PM&>L~h%cvKtB>8GIv3)wX;g1GVo6 z%#JV-ECd???^2`y`tveq1U1E!FSpmXgC5%yVNuv=ihYVwq#v1izU9m-hxr@kXs{2Kf+I!_IGgo5QC7#-8)40F zK(HfZD56wwFQ|1?5V@Oys%?d>AWARas|9UU4_lGl7WEd~l&Uvjgj8ZvduhFfc1;L+ zPL$U7fL|L~xUayoMkb!a;=b}D8tfDCIs`}F0tOa%&~Ajig{Hq9yvMbe=9-%e1iT3 zP5%j?zx6NrTlXzo8R!q5rvK#1i#aV^yp6?r3(7(<$a^^EbNMj2l|#OJ>@&a72_!2Q z#VpRViWOFj+=F`P8%0mxJttY~a~`u+WTD5)iyRmM(nv%BVFCe}Bc=e5xJ{@DaSkKr z5ne)g8Q~R#834}~ucCheLC?L2-fIZRw;0|4FLDp|$GE%0@j6_LIefkIV_N!+m(Yr?^Qnpr46SUPtZ3QBMf`XR?FQ>iyZVJ@Z7^Vi6)>}Gs zPo>nY6DgJM^$!&_qq4&sF97DpiaMbwHK_`xC~8b8&{j>UssBVNuhWK-j}=ieP}d_F zT&yruxgBU+3Z zISq}mj_SP$j?;t!^@7cuIW$d3rm 50: + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 图片{item["filename"]} V通道值{item["v"]},低于平均值{avg_v},将不参与贴图') + libs.set_photo_join_type(config.workdir, pid, 'photo2', item['filename'].split('_')[0], mesh='1', texture='0') + + # 复制xmp文件到photo3目录,如果photo3目录存在的话 + if os.path.exists(os.path.join(config.workdir, pid, 'photo3')): + shutil.copyfile(os.path.join(config.workdir, pid, 'photo2', item['filename'].replace('jpg', 'xmp')), os.path.join(config.workdir, pid, 'photo3', item['filename'].replace('jpg', 'xmp'))) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 射灯异常图片检测完成,共费时{libs.diff_time(start_time)}') + +def detect_markers(psid, pid): + def fix_region(): + region_filename = os.path.join(config.workdir, pid, f'{pid}.rcbox') + with open(region_filename, 'r') as f: + lines = f.readlines() + lines = [line.replace('"NONE" globalCoordinateSystemWkt="NONE" globalCoordinateSystemName="NONE"', '"+proj=geocent +ellps=WGS84 +no_defs" globalCoordinateSystemName="local:1 - Euclidean"') for line in lines] + + start_time = time.time() + add_photo3 = ' ' + if os.path.exists(os.path.join(config.workdir, pid, 'photo3')): + add_photo3 = ' -addFolder "' + os.path.join(config.workdir, pid, 'photo3') + '" ' + + cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName {pid} \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" \ + -addFolder "{os.path.join(config.workdir, pid, "photo1")}" {config.r["setTextureFalse"]} -align -addFolder "{os.path.join(config.workdir, pid, "photo2")}" \ + {add_photo3} -align -selectAllImages \ + -detectMarkers "D:\\make2\\config\\detectMarkers.config.xml" \ + {libs.get_defineDistances(config.ps_floor_sticker.get(psid, config.ps_floor_sticker["default"]))} -align -align -update {config.r2["setRegion"]} \ + -exportXMP "D:\\make2\\config\\exportXMP.config.xml" \ + -exportControlPointsMeasurements "{os.path.join(config.workdir, pid, f"{pid}.controlPoints.csv")}" "D:\\make2\\config\\exportControlPoints.config.xml" \ + -exportReconstructionRegion "{os.path.join(config.workdir, pid, f"{pid}.rcbox")}" \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -quit' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + fix_region() + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 定位点检测完成, 共费时{libs.diff_time(start_time)}') + + if os.path.exists(os.path.join(config.workdir, pid, 'photo3')): + for filename in os.listdir(os.path.join(config.workdir, pid, 'photo2')): + if filename.endswith('_8.xmp'): + # photo3 exist, 设置photo2的xmp文件,不参与贴图 + libs.set_photo_join_type(config.workdir, pid, 'photo2', filename.split('_')[0], mesh='1', texture='0') + +def cal_reconstruction_region(psid, pid): + def fix_region(): + region_filename = os.path.join(config.workdir, pid, f'{pid}.rcbox') + with open(region_filename, 'r') as f: + lines = f.readlines() + lines = [line.replace('"NONE" globalCoordinateSystemWkt="NONE" globalCoordinateSystemName="NONE"', '"+proj=geocent +ellps=WGS84 +no_defs" globalCoordinateSystemName="local:1 - Euclidean"') for line in lines] + add_photo3 = ' ' + if os.path.exists(os.path.join(config.workdir, pid, 'photo3')): + add_photo3 = ' -addFolder "' + os.path.join(config.workdir, pid, 'photo3') + '" ' + cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName {pid} \ + -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" \ + -addFolder "{os.path.join(config.workdir, pid, "photo2")}" -align {add_photo3} \ + -detectMarkers "D:\\make2\\config\\detectMarkers.config.xml" {libs.get_defineDistances(config.ps_floor_sticker.get(psid, config.ps_floor_sticker["default"]))} -align -align \ + -update {config.r2["setRegion"]} \ + -exportControlPointsMeasurements "{os.path.join(config.workdir, pid, f"{pid}.controlPoints.csv")}" "D:\\make2\\config\\exportControlPoints.config.xml" \ + -selectAllImages -exportXMP "D:\\make2\\config\\exportXMP.config.xml" \ + -exportReconstructionRegion "{os.path.join(config.workdir, pid, f"{pid}.rcbox")}" \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -quit' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + + fix_region() + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 重建区域计算完成') + +def step1(pid, experience=False, makeloop=True): + libs_db.start_task({"task_type": "make", "task_key": pid}) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 开始处理{pid}建模任务') + psid = libs.getPSid(pid) + + # 更新云端任务状态 + res = requests.post(config.urls['update_status_modeling_url'], data={'id': pid}) + print('更新建模中状态:', res.text) + + # 下载图片 + start_time = time.time() + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 开始下载图片...') + if experience: + libs.down_from_oss(config.oss_bucket, config.workdir, pid, per=50) + else: + libs.down_from_oss(config.oss_bucket, config.workdir, pid) + os.system(f'python tools/downxmps.py {pid}') + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 图片下载完成,共费时{libs.diff_time(start_time)}') + + start_time = time.time() + # TODO: 上报图片采集数量,更新影棚相机状态 + + # 处理图片,如果experience=True,将图片缩减一半,已节省建模时间和算力成本 + # if experience: + # libs.resize_photos(os.path.join(config.workdir, pid, 'photo1')) + # libs.resize_photos(os.path.join(config.workdir, pid, 'photo2')) + + # 根据配置调整photo2曝光,均衡贴图亮度,可能产生photo3 + libs.adjust_photos(config.workdir, pid) + + # TODO: 检测模糊异常图片,上报微信通知 + # TODO: 处理图片,去反光算法 + + # 检测图片定位点,定位异常及时上报微信通知给客服人员,人工判断是否需要重新拍摄或更换地贴。定义定位点距离,导出相机位姿信息 + detect_markers(psid, pid) + + # 处理图片,检测photo2中的异常图片不参与贴图,以免破坏贴图效果,默认不检测射灯异常图片,以节省算力成本 + if not makeloop: + filter_dark_texture_image(pid) + + # 处理图片,暗部提亮,提高贴图效果 + + # TODO: 处理图片,去遮挡处理,提高建模与贴图质量 + + # 加入photo2,计算重建区域,导出重建区域与相机位姿配置信息 + # cal_reconstruction_region(psid, pid) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} step1图片预处理完成,共费时{libs.diff_time(start_time)}') + + # TODO: 更新本地step1任务状态,加入step2任务队列 + if makeloop: + os.system(f'python main_step2.py {pid}') + else: + os.system(f'python main_step2.py {pid}') + # if os.path.exists(os.path.join(config.sharedir, pid)): + # shutil.rmtree(os.path.join(config.sharedir, pid), ignore_errors=True) + # shutil.move(os.path.join(config.workdir, pid), config.sharedir) + + # print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} step1任务完成,移动到共享目录') + +def main(pid, experience=False, makeloop=True): + if pid == '0': + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 开始进入本地任务值守模式...') + while True: + # 取云端redis多个key任务,TODO:后续要改为api调用 + experience = False + pid = libs_db.get_task('make_experience') + if pid == '': + time.sleep(3) + pid = libs_db.get_task('make') + if pid == '': + time.sleep(3) + continue + else: + experience = True + step1(pid, experience, makeloop) + else: + step1(pid, experience, makeloop) + +if __name__ == '__main__': + # 取云端redis任务,完成第一步的数据预处理后,将数据放入共享存储目录,将第二步任务塞入本地mysql队列 + # 默认循环值守,可传参数运行单一任务,以方便调试 + pid = '0' + if len(sys.argv) == 2: + pids = sys.argv[1].split(',') + for pid in pids: + main(pid, experience=False, makeloop=False) + exit() + if len(sys.argv) == 3: + experience = False + if sys.argv[2] == '1': + print('演示测试...') + experience = True + pids = sys.argv[1].split(',') + for pid in pids: + main(pid, experience=experience, makeloop=False) + exit() + main(pid, experience=False, makeloop=True) \ No newline at end of file diff --git a/main_step2.py b/main_step2.py new file mode 100644 index 0000000..52a002d --- /dev/null +++ b/main_step2.py @@ -0,0 +1,96 @@ +import os, sys, time, shutil, subprocess, shlex +import platform +if platform.system() == 'Windows': + sys.path.append('e:\\libs\\') +else: + sys.path.append('/data/deploy/make3d/make2/libs/') +import config, libs, libs_db + +def load_model(pid): + cmd = f'{config.rcbin} {config.r1["init"]} -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}"' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + +def get_rcver(): + rcbin = '"C:\\Program Files\\Capturing Reality\\RealityCapture\\RealityCapture.exe"' + if os.path.getsize(rcbin[1:-1]) == 20783616: + return 1 + else: + return 2 + +def make3d(pid): + simplify_value = 1000000 * libs.getHeadCount(pid) + add_photo3 = ' ' + if os.path.exists(os.path.join(config.workdir, pid, 'photo3')): + add_photo3 = ' -addFolder "' + os.path.join(config.workdir, pid, 'photo3') + '" -align -align ' + + if get_rcver() == 1: # old version + cmd = f'{config.rcbin} {config.r1["init"]} \ + -addFolder "{os.path.join(config.workdir, pid, "photo1")}" -addFolder "{os.path.join(config.workdir, pid, "photo2")}" {add_photo3} \ + -importControlPointsMeasurements "{os.path.join(config.workdir, pid, f"{pid}.controlPoints.csv")}" \ + -align -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}"' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 定位点导入完成') + + # defind_distance + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 定义定位点距离完成') + + cmd = f'{config.rcbin} {config.r1["init"]} -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -update \ + -setReconstructionRegion "{os.path.join(config.workdir, pid, f"{pid}.rcbox")}" \ + -mvs -modelSelectMaximalConnectedComponent -modelInvertSelection -modelRemoveSelectedTriangles -closeHoles -clean -simplify {simplify_value} -smooth -unwrap -calculateTexture -renameModel {pid} -exportModel "{pid} {os.path.join(config.workdir, pid, "output", f"{pid}.obj")}" "d:\\make2\\config\\ModelExportParams102.xml" -quit' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + else: # new version + if libs.aliyun_face(pid) and libs.get_ps_type(pid) == 1: + calulate_type = 'calculateHighModel' + else: + calulate_type = 'calculateNormalModel' + cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName {pid} \ + -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" \ + -{calulate_type} \ + -selectLargestModelComponent -invertTrianglesSelection -removeSelectedTriangles -simplify {simplify_value} -smooth -closeHoles -cleanModel -calculateTexture \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" \ + -exportSelectedModel "{os.path.join(config.workdir, pid, "output", f"{pid}.obj")}" "d:\\make2\\config\\ModelExportParams.xml" -quit' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + +def step2(pid): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始建模任务step2') + if os.path.exists(os.path.join(config.sharedir, pid)) and not os.path.exists(os.path.join(config.workdir, pid)): + shutil.move(os.path.join(config.sharedir, pid), config.workdir) + else: + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 目录{os.path.join(config.sharedir, pid)}不存在,或{os.path.join(config.workdir, pid)}已存在') + # return + start_time = time.time() + make3d(pid) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 建模任务step2完成,共费时{libs.diff_time(start_time)},任务已提交到step3') + # 更新本地任务状态,加入step3任务队列 + + os.system(f'python d:\\make2\\main_step3.py {pid}') + +def main(pid): + if pid == '0': + while True: + # 取本地mysql队列任务,完成第二步的建模任务 + + step2(pid) + else: + step2(pid) + +if __name__ == '__main__': + # 取本地mysql队列任务,完成第二步的建模任务 + # 默认循环值守,可传参数运行单一任务,以方便调试 + pid = '0' + if len(sys.argv) > 1: + pids = sys.argv[1].split(',') + for pid in pids: + main(pid) + exit() + main(pid) \ No newline at end of file diff --git a/main_step3.py b/main_step3.py new file mode 100644 index 0000000..2f0f202 --- /dev/null +++ b/main_step3.py @@ -0,0 +1,229 @@ +import os, sys, time, bpy, math, requests, bmesh, json, shutil +from PIL import Image +import platform +if platform.system() == 'Windows': + sys.path.append('e:\\libs\\') +else: + sys.path.append('/data/deploy/make3d/make2/libs/') +import config, libs, libs_db + +def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False): + """Returns a transformed, triangulated copy of the mesh""" + assert obj.type == 'MESH' + if apply_modifiers and obj.modifiers: + import bpy + depsgraph = bpy.context.evaluated_depsgraph_get() + obj_eval = obj.evaluated_get(depsgraph) + me = obj_eval.to_mesh() + bm = bmesh.new() + bm.from_mesh(me) + obj_eval.to_mesh_clear() + else: + me = obj.data + if obj.mode == 'EDIT': + bm_orig = bmesh.from_edit_mesh(me) + bm = bm_orig.copy() + else: + bm = bmesh.new() + bm.from_mesh(me) + if transform: + matrix = obj.matrix_world.copy() + if not matrix.is_identity: + bm.transform(matrix) + matrix.translation.zero() + if not matrix.is_identity: + bm.normal_update() + if triangulate: + bmesh.ops.triangulate(bm, faces=bm.faces) + return bm + +def find_pid_objname(pid): + for obj in bpy.data.objects: + if obj.name.startswith(str(pid)): + return obj.name + +def reload_obj(pid): + obj_filename = os.path.join(config.workdir, pid, 'output', f'{pid}.obj') + bpy.ops.wm.read_homefile() + bpy.ops.object.delete(use_global=False, confirm=False) + bpy.ops.import_scene.obj(filepath=obj_filename) + bpy.context.scene.unit_settings.scale_length = 1 + bpy.context.scene.unit_settings.length_unit = 'CENTIMETERS' + bpy.context.scene.unit_settings.mass_unit = 'GRAMS' + + obj = bpy.context.selected_objects[0] + bpy.context.view_layer.objects.active = obj + obj.select_set(True) + +def base_fix(pid): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始模型基础校正...') + start_time = time.time() + # 统一文件名规则 + def fix_filename(pid): + if os.path.exists(os.path.join(config.workdir, pid, 'output', f'{pid}.jpg')): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 已经是最新文件名规则,无需处理') + return + elif os.path.exists(os.path.join(config.workdir, pid, 'output', f'{pid}_u0_v0_diffuse.jpg')): + texture_filename_end = '_u0_v0_diffuse.jpg' + elif os.path.exists(os.path.join(config.workdir, pid, 'output', f'{pid}_u1_v1.jpg')): + texture_filename_end = '_u1_v1.jpg' + os.rename(os.path.join(config.workdir, pid, 'output', f'{pid}{texture_filename_end}'), os.path.join(config.workdir, pid, 'output', f'{pid}.jpg')) + with open(os.path.join(config.workdir, pid, 'output', f'{pid}.mtl'), 'r') as f: + lines = f.readlines() + lines = [line.replace(texture_filename_end, '.jpg') for line in lines] + with open(os.path.join(config.workdir, pid, 'output', f'{pid}.mtl'), 'w') as f: + f.writelines(lines) + f.close() + fix_filename(pid) + + # 统一blender环境 + reload_obj(pid) + + # 统一模型方向、位置、大小... + pid_objname = find_pid_objname(pid) + bpy.data.objects[pid_objname].rotation_euler = (0, 0, 0) + bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN') + bpy.context.object.location[0] = 0 + bpy.context.object.location[1] = 0 + bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 模型基础校正完成,共费时{libs.diff_time(start_time)}') + +def export_and_update_obj(pid): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始导出并上传模型...') + start_time = time.time() + obj_filename = os.path.join(config.workdir, pid, 'output', f'{pid}.obj') + bpy.ops.export_scene.obj(filepath=obj_filename) + + # 上传到oss + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/{pid}.obj', os.path.join(config.workdir, pid, 'output', f'{pid}.obj')) + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/{pid}.mtl', os.path.join(config.workdir, pid, 'output', f'{pid}.mtl')) + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/{pid}.jpg', os.path.join(config.workdir, pid, 'output', f'{pid}.jpg')) + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/{pid}.obj.rcInfo', os.path.join(config.workdir, pid, 'output', f'{pid}.obj.rcInfo')) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 模型导出并上传完成,共费时{libs.diff_time(start_time)}') + +def resize_texture_and_reload_obj(pid, ratio=0.5): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始压缩贴图并重载模型...') + start_time = time.time() + bpy.ops.wm.quit_blender() + + image_name = os.path.join(config.workdir, pid, 'output', f'{pid}.jpg') + img = Image.open(image_name) + w, h = img.size + img = img.resize((int(w * ratio), int(h * ratio))) + img.save(image_name, optimize=True, quality=95) + + reload_obj(pid) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 贴图压缩并重载模型完成,共费时{libs.diff_time(start_time)}') + +def export_and_update_glbs(pid): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始导出并上传审核模型和3D相册模型glb文件...') + start_time = time.time() + headcount = libs.getHeadCount(pid) + pid_objname = find_pid_objname(pid) + obj = bpy.data.objects[pid_objname] + obj.select_set(True) + + model_info = {} + model_info['headcount'] = headcount + model_info['faces'] = round(len(obj.data.polygons) / 10000) + model_info['height'] = round(obj.dimensions.y * 100) + + # bpy.ops.wm.save_as_mainfile(filepath=os.path.join(config.workdir, pid, f'{pid}.blend')) + + # 统一缩放到9cm标准尺寸 + scale = 90 / bpy.data.objects[pid_objname].dimensions.y + bpy.data.objects[pid_objname].scale = (scale, scale, scale) + bpy.ops.object.transform_apply(scale=True) + + # bpy.ops.wm.save_as_mainfile(filepath=os.path.join(config.workdir, pid, f'{pid}-9cm.blend')) + + bm = bmesh_copy_from_object(obj) + model_info['volume'] = round(bm.calc_volume(), 2) + model_info['weight'] = round(model_info['volume'] * 1.226, 2) + print(f'{pid}的模型数据:{model_info}') + + res = requests.get(f'{config.urls["upload_model_info_url"]}?pid={pid}&headcount={headcount}&faces={model_info["faces"]}&volume={model_info["volume"]}&weight={model_info["weight"]}&height={model_info["height"]}') + print('上传模型数据:', res.text) + # with open(os.path.join(config.sharedir, 'model_info', f'{pid}.json'), 'w') as f: + # json.dump(model_info, f) + # f.close() + + # 先生成审核模型 + faces_dest = 300000 * headcount + # 减面 + faces_current = len(bpy.data.objects[pid_objname].data.polygons) + print(f'当前面数:{faces_current},目标面数:{faces_dest}') + + bpy.ops.object.modifier_add(type='DECIMATE') + bpy.context.object.modifiers["Decimate"].ratio = faces_dest / faces_current + bpy.ops.object.modifier_apply(modifier="Decimate") + + glb_filename = os.path.join(config.workdir, pid, 'output', f'{pid}.glb') + bpy.ops.export_scene.gltf(filepath=glb_filename, export_format='GLB', export_apply=True, export_jpeg_quality=75, export_draco_mesh_compression_enable=False) + + # 再生成数字模型 + faces_dest = 120000 * headcount + + # 减面 + faces_current = len(bpy.data.objects[pid_objname].data.polygons) + print(f'当前面数:{faces_current},目标面数:{faces_dest}') + + bpy.ops.object.modifier_add(type='DECIMATE') + bpy.context.object.modifiers["Decimate"].ratio = faces_dest / faces_current + bpy.ops.object.modifier_apply(modifier="Decimate") + + glb_filename = os.path.join(config.workdir, pid, 'output', f'{pid}-3d.glb') + bpy.ops.export_scene.gltf(filepath=glb_filename, export_format='GLB', export_apply=True, export_jpeg_quality=75, export_draco_mesh_compression_enable=False) + + os.system(f'gltfpack -c -i {os.path.join(config.workdir, pid, "output", f"{pid}.glb")} -o {os.path.join(config.workdir, pid, "output", f"{pid}-pack.glb")}') + config.oss_bucket.put_object_from_file(f'glbs/auto/{pid}.glb', os.path.join(config.workdir, pid, 'output', f'{pid}-pack.glb')) + config.oss_bucket.put_object_from_file(f'glbs/3d/{pid}.glb', os.path.join(config.workdir, pid, 'output', f'{pid}-3d.glb')) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} glb文件导出并上传完成,共费时{libs.diff_time(start_time)}') + +def step3(pid): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始模型后道处理') + start_time = time.time() + # 方向、大小、位置等基础校正 + base_fix(pid) + # 去灰 + + # 避开人脸白色提纯 + + # TODO: 人脸五官特征加深 + # TODO: 自动UV分割,优先级顺序:脚底、人脸、手指、手臂内侧、大腿内侧、前身、后背、其他 + # TODO: 根据UV分割自动修贴图 + # 调用blender生成3D相册glb文件和修模审核glb文件,压缩贴图 + + export_and_update_obj(pid) + resize_texture_and_reload_obj(pid) + export_and_update_glbs(pid) + + # 更新本地任务状态,更新云端任务状态 + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 模型后道处理完成,共费时{libs.diff_time(start_time)}') + res = requests.post(config.urls['update_status_modelsuccess_url'], data={'id': pid}) + print('上传完成更新建模成功状态:', res.text) + shutil.rmtree(os.path.join(config.workdir, pid), ignore_errors=True) + libs_db.finish_task({"task_type": "make", "task_key": pid}) + +def main(pid): + if pid == '0': + while True: + # 取本地mysql队列任务,完成第三步的建模后处理任务 + + step3(pid) + else: + step3(pid) + +if __name__ == '__main__': + # 取本地mysql队列任务,完成第三步的建模后处理任务 + # 默认循环值守,可传参数运行单一任务,以方便调试 + pid = '0' + if len(sys.argv) > 1: + pids = sys.argv[1].split(',') + for pid in pids: + main(pid) + exit() + main(pid) \ No newline at end of file diff --git a/test/test.py b/test/test.py new file mode 100644 index 0000000..301a255 --- /dev/null +++ b/test/test.py @@ -0,0 +1,30 @@ +import sys +from PyQt5.QtCore import QUrl +from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget +from PyQt5.QtWebEngineWidgets import QWebEngineView + +class WebBrowserWindow(QMainWindow): + def __init__(self): + super().__init__() + + self.browser = QWebEngineView() + self.browser.setUrl(QUrl("https://www.qq.com")) # 设置要打开的网页 + + layout = QVBoxLayout() + layout.addWidget(self.browser) + + central_widget = QWidget() + central_widget.setLayout(layout) + + self.setCentralWidget(central_widget) + self.setWindowTitle("Web Browser") + self.setGeometry(100, 100, 800, 600) + +def main(): + app = QApplication(sys.argv) + window = WebBrowserWindow() + window.show() + sys.exit(app.exec_()) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test/use.sql b/test/use.sql new file mode 100644 index 0000000..f81dba3 --- /dev/null +++ b/test/use.sql @@ -0,0 +1,2 @@ +SELECT *, ABS(TIMESTAMPDIFF(MINUTE , started_at, finished_at)) AS durning from tasks + diff --git a/tools/Remove_grayscale.py b/tools/Remove_grayscale.py new file mode 100644 index 0000000..3f0e2d1 --- /dev/null +++ b/tools/Remove_grayscale.py @@ -0,0 +1,237 @@ +import os, oss2, sys, time +import cv2 +import numpy as np +from tqdm import tqdm +import matplotlib.pyplot as plt +from PIL import Image, ImageEnhance +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +import config, libs + +def ps_color_scale_adjustment(image, shadow=0, highlight=255, midtones=1): + ''' + 模拟 PS 的色阶调整; 0 <= Shadow < Highlight <= 255 + :param image: 传入的图片 + :param shadow: 黑场(0-Highlight) + :param highlight: 白场(Shadow-255) + :param midtones: 灰场(9.99-0.01) + :return: 图片 + ''' + if highlight > 255: + highlight = 255 + if shadow < 0: + shadow = 0 + if shadow >= highlight: + shadow = highlight - 2 + if midtones > 9.99: + midtones = 9.99 + if midtones < 0.01: + midtones = 0.01 + image = np.array(image, dtype=np.float16) + # 计算白场 黑场离差 + Diff = highlight - shadow + image = image - shadow + image[image < 0] = 0 + image = (image / Diff) ** (1 / midtones) * 255 + image[image > 255] = 255 + image = np.array(image, dtype=np.uint8) + + return image + + +def show_histogram(image, image_id, save_hist_dir, min_threshold, max_threshold): + ''' + 画出直方图展示 + :param image: 导入图片 + :param image_id: 图片id编号 + :param save_hist_dir: 保存路径 + :param min_threshold: 最小阈值 + :param max_threshold: 最大阈值 + :return: 原图image,和裁剪原图直方图高低阈值后的图片image_change + ''' + plt.rcParams['font.family'] = 'SimHei' + plt.rcParams['axes.unicode_minus'] = False + plt.hist(image.ravel(), 254, range=(2, 256), density=False) + plt.hist(image.ravel(), 96, range=(2, 50), density=False) # 放大 range(0, 50),bins值最好是range的两倍,显得更稀疏,便于对比 + plt.hist(image.ravel(), 110, range=(200, 255), density=False) # 放大 range(225, 255) + plt.annotate('thresh1=' + str(min_threshold), # 文本内容 + xy=(min_threshold, 0), # 箭头指向位置 # 阈值设定值! + xytext=(min_threshold, 500000), # 文本位置 # 阈值设定值! + arrowprops=dict(facecolor='black', width=1, shrink=5, headwidth=2)) # 箭头 + plt.annotate('thresh2=' + str(max_threshold), # 文本内容 + xy=(max_threshold, 0), # 箭头指向位置 # 阈值设定值! + xytext=(max_threshold, 500000), # 文本位置 # 阈值设定值! + arrowprops=dict(facecolor='black', width=1, shrink=5, headwidth=2)) # 箭头 + # 在y轴上绘制一条直线 + # plt.axhline(y=10000, color='r', linestyle='--', linewidth=0.5) + plt.title(str(image_id)) + # plt.show() + # 保存直方图 + save_hist_name = os.path.join(save_hist_dir, f'{image_id}_{min_threshold}&{max_threshold}.jpg') + plt.savefig(save_hist_name) + # 清空画布, 防止重叠展示 + plt.clf() + + +def low_find_histogram_range(image, target_frequency): + ''' + 循环查找在 target_frequency (y)频次限制下的直方图区间值(x) + :param image: 导入图片 + :param target_frequency: 直方图 y 频次限制条件 + :return: 直方图区间 x,和 该区间频次 y + ''' + # 计算灰度直方图 + hist, bins = np.histogram(image, bins=256, range=[0, 256]) + # 初始化区间和频次 + interval = 2 + frequency = hist[255] + while frequency < target_frequency: + # 更新区间和频次 + interval += 1 + # 检查直方图的频次是否为None,如果频次是None,则将其设为0,这样可以避免将None和int进行比较报错。 + frequency = hist[interval] if hist[interval] is not None else 0 + frequency += hist[interval] if hist[interval] is not None else 0 + print(f'x={interval}, y={frequency}') + # 如果频次接近10000则停止循环 + if target_frequency - 2000 <= frequency <= target_frequency + 1000: + break + + return interval, frequency + + +def high_find_histogram_range(image, target_frequency): + ''' + 循环查找在 target_frequency (y)频次限制下的直方图区间值(x) + :param image: 导入图片 + :param target_frequency: 直方图 y 频次限制条件 + :return: 直方图区间 x,和 该区间频次 y + ''' + # 计算灰度直方图 + hist, bins = np.histogram(image, bins=256, range=[0, 256]) + # 初始化区间和频次 + interval = 255 + frequency = hist[255] + while frequency < target_frequency: + # 更新区间和频次 + interval -= 1 + # 检查直方图的频次是否为None,如果频次是None,则将其设为0,这样可以避免将None和int进行比较报错。 + frequency = hist[interval] if hist[interval] is not None else 0 + frequency += hist[interval] if hist[interval] is not None else 0 + # 如果频次接近10000则停止循环 + if target_frequency - 2000 <= frequency <= target_frequency + 2000: + break + + return interval, frequency + + +def sharpening_filter(image): + ''' + 锐化滤波器对图片进行锐化,增强图像中的边缘和细节 + :param image: 导入图片 + :return: 锐化后的图片 + ''' + # sharp_kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]]) + sharp_kernel = np.array([[0, -0.5, 0], [-0.5, 3, -0.5], [0, -0.5, 0]]) + sharpened_image = cv2.filter2D(image, -1, sharp_kernel) + return sharpened_image + +def reduce_sharpness(image, factor): + ''' + 使用PIL库减弱图像锐度 + :param image: 图像 + :param factor: 锐度因子,0表示最大程度减弱锐度,1表示原始图像 + :return: 减弱锐度后的图像 + ''' + # OpenCV 格式的图像转换为 PIL 的 Image 对象 + image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + pil_image = Image.fromarray(image_rgb) + enhancer = ImageEnhance.Sharpness(pil_image) + reduced_image = enhancer.enhance(factor) + # PIL 的 Image 对象转换为 OpenCV 的图像格式 + image_array = np.array(reduced_image) + sharpened_image = cv2.cvtColor(image_array, cv2.COLOR_RGB2BGR) + + return sharpened_image + +def find_last_x(image, slope_threshold = 1000): + x = [] + y = [] + hist, bins = np.histogram(image, bins=256, range=[0, 256]) + for i in range(2, 50): + x.append(i) + y.append(hist[i]) + slopes = [abs(y[i + 1] - y[i]) for i in range(len(x) - 1)] + + current_interval = [] + max_interval = [] + max_x = {} + for i, slope in enumerate(slopes): + current_interval.append(slope) + if slope >= slope_threshold: + if len(current_interval) > len(max_interval): + max_interval = current_interval.copy() + max_x[x[i]] = slope + current_interval = [] + + print(max_x) + last_x = list(max_x)[-1] + last_y = max_x[last_x] + return last_x, last_y + +def main(pid, print_id): + texture_filename = f'{input_dir}{pid}Tex1.{print_id}.jpg' + input_image = cv2.imread(texture_filename) + # low_x_thresh, low_y_frequency = low_find_histogram_range(input_image, low_y_limit) + low_x_thresh, low_y_frequency = find_last_x(input_image, 1000) + high_x_thresh, high_y_frequency = high_find_histogram_range(input_image, high_y_limit) + print(f"{low_x_thresh} 区间, {low_y_frequency} 频次") + print(f"{high_x_thresh} 区间, {high_y_frequency} 频次") + high_output_image = ps_color_scale_adjustment(input_image, shadow=low_x_thresh, highlight=high_x_thresh, midtones=1) + # high_output_image = ps_color_scale_adjustment(low_ouput_image, shadow=0, highlight=high_x_thresh, midtones=1) + + # 人体贴图和黑色背景交界处不进行锐化 + gray = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY) + _, thresh = cv2.threshold(gray, 2, 255, cv2.THRESH_BINARY) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7)) + gradient = cv2.morphologyEx(thresh, cv2.MORPH_GRADIENT, kernel) + roi_gradient = cv2.bitwise_and(high_output_image, high_output_image, mask=gradient) + + # 锐化滤波器 + # sharpened_image = sharpening_filter(high_output_image) + sharpened_image = reduce_sharpness(high_output_image, factor=4) + # 将原图边界替换锐化后的图片边界 + sharpened_image[gradient != 0] = roi_gradient[gradient != 0] + + # 直方图标记并保存 + # show_histogram(input_image, img_id, low_x_thresh, high_x_thresh) + cv2.imwrite(texture_filename, sharpened_image, [cv2.IMWRITE_JPEG_QUALITY, 95]) # 保存图片的质量是原图的 95% + + +if __name__ == "__main__": + input_dir = "D:\\AI_pycharm\\Change_grayscale\\Texture_photos\\original_images\\" + save_dir = "D:\\AI_pycharm\\Change_grayscale\\Texture_photos\\test\\" + + low_y_limit = 48000 + high_y_limit = 13000 + + input_dir = '/data/datasets/texure_photos/' + + pids = '99724,99747,99762,99763,99777,99778,99807,99812,99823,99843,99405,99416,97984,97662,86153' + for pid in pids.split(','): + pid, print_id = pid.split('_') + # 根据前缀获取文件列表 + path = f'/data/datasets/texure_photos/' + texture_filename = f'{path}{pid}Tex1.{print_id}.jpg' + + prefix = f'objs/print/{pid}/' + if config.oss_bucket.object_exists(f'{prefix}{pid}Tex1.{print_id}.jpg'): + print(f'{pid}Tex1.{print_id}.jpg 处理中...') + if not os.path.exists(texture_filename): + os.makedirs(path, exist_ok=True) + config.oss_bucket.get_object_to_file(f'{prefix}{pid}Tex1.{print_id}.jpg', texture_filename) + main(pid, print_id) + else: + print(f'文件已存在,直接上传') + config.oss_bucket.put_object_from_file(f'objs/print/{pid}/{pid}Tex1.{print_id}.jpg', texture_filename) + + print(f'{pid}Tex1.{print_id}.jpg 处理完成') + \ No newline at end of file diff --git a/tools/auto_distance.py b/tools/auto_distance.py new file mode 100644 index 0000000..429d3c5 --- /dev/null +++ b/tools/auto_distance.py @@ -0,0 +1,81 @@ +import win32gui, win32con, time, os, sys +import pyautogui as ag +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +import config, libs + +def find_and_maximize_window(window_title): + windows = [] + win32gui.EnumWindows(lambda hwnd, windows: windows.append(hwnd), windows) + + for hwnd in windows: + if win32gui.IsWindowVisible(hwnd): + if window_title in win32gui.GetWindowText(hwnd): + print(f'found {window_title} hwnd:{hwnd}') + # win32gui.ShowWindow(hwnd, win32con.SW_MAXIMIZE) + win32gui.SetForegroundWindow(hwnd) + pid = win32gui.GetWindowText(hwnd).split('wait')[0].split(' ')[0].split('-')[0].split('*')[0] + left, top, right, bottom = win32gui.GetWindowRect(hwnd) + return pid, left, top, right, bottom + return '0', 0, 0, 0, 0 + +def get_defineDistances(pid, left, top, right, bottom): + psid = libs.getPSid(pid) + distances = config.ps_floor_sticker.get(psid, config.ps_floor_sticker['default']) + for index, d in enumerate(distances.split(';')): + p1, p2, distance = d.split(' ') + if index == 0: + ag.moveTo(left + 80, top + 290) + else: + ag.moveTo(left + 80, top + 290 + 15) # Create distance line height 15 + ag.click() + + ag.moveTo(left + 302, (bottom - 100)) # A point + ag.click();repeat_backspace(20) + ag.typewrite(p1) + + ag.moveTo(left + 302, (bottom - 80)) # B point + ag.click();repeat_backspace(20) + ag.typewrite(p2) + + ag.moveTo(left + 302, (bottom - 35)) # Definded distance + ag.click();repeat_backspace(8) + ag.typewrite(distance) + ag.press('enter') + + +def repeat_backspace(times): + for i in range(times): + ag.press('backspace') + for i in range(times): + ag.press('delete') + +def defind_distance(pid, left, top, right, bottom): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 开始定义定位点距离...') + print(f'left: {left}, top: {top}, right: {right}, bottom: {bottom}') + # ag.PAUSE = 1 + + ag.moveTo(left + 20, top + 200) # open Control points + ag.click() + + get_defineDistances(pid, left, top, right, bottom) + + ag.hotkey('ctrl', 's') # save project + ag.hotkey('alt', 'f4') # close project + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 定义定位点距离完成') + time.sleep(3) + +def main(): + while True: + time.sleep(1) + title = "wait" + pid, left, top, right, bottom = find_and_maximize_window(title) + if pid == '0': + pass + else: + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 找到{pid}的定位点距离定义窗口,开始定位点距离定义...') + start_time = time.time() + defind_distance(pid, left, top, right, bottom) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 定位点距离定义完成,共费时{libs.diff_time(start_time)}') + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tools/cal_weight.py b/tools/cal_weight.py new file mode 100644 index 0000000..06c7a32 --- /dev/null +++ b/tools/cal_weight.py @@ -0,0 +1,92 @@ +import os, sys, time, bpy, bmesh, shutil +import platform +if platform.system() == 'Windows': + sys.path.append('e:\\libs\\') +else: + sys.path.append('/data/deploy/make3d/make2/libs/') + +import config, libs, libs_db + +def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False): + """Returns a transformed, triangulated copy of the mesh""" + assert obj.type == 'MESH' + if apply_modifiers and obj.modifiers: + import bpy + depsgraph = bpy.context.evaluated_depsgraph_get() + obj_eval = obj.evaluated_get(depsgraph) + me = obj_eval.to_mesh() + bm = bmesh.new() + bm.from_mesh(me) + obj_eval.to_mesh_clear() + else: + me = obj.data + if obj.mode == 'EDIT': + bm_orig = bmesh.from_edit_mesh(me) + bm = bm_orig.copy() + else: + bm = bmesh.new() + bm.from_mesh(me) + if transform: + matrix = obj.matrix_world.copy() + if not matrix.is_identity: + bm.transform(matrix) + matrix.translation.zero() + if not matrix.is_identity: + bm.normal_update() + if triangulate: + bmesh.ops.triangulate(bm, faces=bm.faces) + return bm + +def reload_obj(pid, action): + obj_filename = os.path.join(config.workdir, action, pid, f'{pid}.obj') + bpy.ops.wm.read_homefile() + bpy.ops.object.delete(use_global=False, confirm=False) + bpy.ops.import_scene.obj(filepath=obj_filename) + bpy.context.scene.unit_settings.scale_length = 0.001 + bpy.context.scene.unit_settings.length_unit = 'CENTIMETERS' + bpy.context.scene.unit_settings.mass_unit = 'GRAMS' + + obj = bpy.context.selected_objects[0] + bpy.context.view_layer.objects.active = obj + obj.select_set(True) + + return obj + +def cal_weight(obj, size): + # 统一缩放到9cm标准尺寸 + scale = size / obj.dimensions.z + obj.scale = (scale, scale, scale) + bpy.ops.object.transform_apply(scale=True) + + # bpy.ops.wm.save_as_mainfile(filepath=os.path.join(config.workdir, action, pid, f'{pid}-{size/10}cm.blend')) + model_info = {} + bm = bmesh_copy_from_object(obj) + model_info['volume'] = round(bm.calc_volume() / 1000) + model_info['weight'] = round(model_info['volume'] * 1.226) + print(f'{size/10}cm:体积 {model_info["volume"]}cm³, 克重 {model_info["weight"]}g') + +def main(action, pid, sizes): + libs.down_obj_from_oss(config.workdir, pid, action) + obj = reload_obj(pid, action) + + bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) + print(f'模型{pid}的体积与克重估算信息:') + for size in sizes: + size = float(size) + cal_weight(obj, size) + +if __name__ == '__main__': + sizes = (90, 120, 150, 180) + + if len(sys.argv) == 3: + action = sys.argv[1] + pids = sys.argv[2].split(',') + for pid in pids: + main(action, pid, sizes) + elif len(sys.argv) == 4: + action = sys.argv[1] + pids = sys.argv[2].split(',') + sizes = sys.argv[3].split(',') + for pid in pids: + main(action, pid, sizes) + print('Usage: python cal_weight.py ') \ No newline at end of file diff --git a/tools/downxmps.py b/tools/downxmps.py new file mode 100644 index 0000000..24cda78 --- /dev/null +++ b/tools/downxmps.py @@ -0,0 +1,82 @@ +import os, sys, datetime, subprocess, shlex, shutil, requests, time, json, oss2 +import platform +if platform.system() == 'Windows': + sys.path.append('e:\\libs\\') +else: + sys.path.append('/data/deploy/make3d/make2/libs/') + +import config, libs, libs_db + +def down_xmps_from_oss(psid): + def need_down(): + filename = f'xmps/{psid}/{psid}.rcbox' + if not config.oss_bucket.object_exists(filename): + print(f'未找到{filename}, 请检查是否已经上传{psid}号影棚坐标文件') + time.sleep(5) + return False + oss_file_last_modified = float(config.oss_bucket.get_object_meta(filename).last_modified) + local_file = os.path.join(config_path, 'xmps', psid, f'{psid}.rcbox') + if not os.path.exists(local_file): + print(f'未找到{local_file}, 需要下载影棚坐标文件') + os.makedirs(os.path.join(config_path, 'xmps', psid), exist_ok=True) + config.oss_bucket.get_object_to_file(filename, local_file) + return True + local_file_last_modified = os.path.getmtime(local_file) + if oss_file_last_modified > local_file_last_modified: + print(f'本地{local_file}文件已过期, 需要下载影棚坐标文件') + config.oss_bucket.get_object_to_file(filename, local_file) + return True + + if not need_down(): return + + print('正在下载影棚坐标文件') + dest_path = os.path.join(config_path, 'xmps', psid) + + if os.path.exists(os.path.join(dest_path)): + shutil.rmtree(os.path.join(dest_path), ignore_errors=True) + + os.makedirs(os.path.join(dest_path, 'mesh'), exist_ok=True) + os.makedirs(os.path.join(dest_path, 'texture'), exist_ok=True) + + prefix = f'xmps/{psid}/mesh/' + filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix) + for file in filelist: + if file.key.endswith('.xmp'): + filename = file.key.split('/')[-1] + print('正在下载:', file.key) + config.oss_bucket.get_object_to_file(file.key, os.path.join(dest_path, 'mesh', filename)) + + prefix = f'xmps/{psid}/texture/' + filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix) + for file in filelist: + if file.key.endswith('.xmp'): + filename = file.key.split('/')[-1] + print('正在下载:', file.key) + config.oss_bucket.get_object_to_file(file.key, os.path.join(dest_path, 'texture', filename)) + + print('下载完成') + +if __name__ == '__main__': + start = datetime.datetime.now() + if len(sys.argv) == 2: + pids = sys.argv[1] + else: + print('usage: python downxmps.py [pids]') + exit(1) + + for pid in pids.split(','): + input_path = os.path.join('d:\\', pid) + ImagesGeometry = os.path.join(input_path, "photo1") + ImagesTexture = os.path.join(input_path, "photo2") + + psid = libs.getPSid(pid) + config_path = f'D:\\apps\\config\\' + xmps_mesh_path = config_path + f'xmps\\{psid}\\mesh\\' + xmps_texture_path = config_path + f'xmps\\{psid}\\texture\\' + + down_xmps_from_oss(psid) + + os.system('xcopy /y /q ' + xmps_mesh_path + '*.* ' + ImagesGeometry) + os.system('xcopy /y /q ' + xmps_texture_path + '*.* ' + ImagesTexture) + + \ No newline at end of file diff --git a/tools/gen_xmps.py b/tools/gen_xmps.py new file mode 100644 index 0000000..effb639 --- /dev/null +++ b/tools/gen_xmps.py @@ -0,0 +1,98 @@ +import os, sys, time, shutil, subprocess, shlex, json +import platform +if platform.system() == 'Windows': + sys.path.append('e:\\libs\\') +else: + sys.path.append('/data/deploy/make3d/make2/libs/') + +import config, libs, libs_db + +def upload_xmp(pid): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 上传xmp文件之前先删除oss上的xmp文件所在目录...') + config.oss_bucket.delete_object(f'xmps/{pid}/') + start_time = time.time() + workdir = os.path.join(config.workdir, pid) + psid = libs.getPSid(pid) + config.oss_bucket.put_object_from_file(f'xmps/{psid}/{psid}.rcbox', os.path.join(workdir, f'{pid}.rcbox')) + for xmp in os.listdir(os.path.join(workdir, 'photo1')): + if xmp.endswith('.xmp'): + config.oss_bucket.put_object_from_file(f'xmps/{psid}/mesh/{xmp}', os.path.join(workdir, 'photo1', xmp)) + for xmp in os.listdir(os.path.join(workdir, 'photo2')): + if xmp.endswith('.xmp'): + config.oss_bucket.put_object_from_file(f'xmps/{psid}/texture/{xmp}', os.path.join(workdir, 'photo2', xmp)) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} xmp文件上传完成,共费时{time.time() - start_time}秒') + +def main(pid): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 开始计算相机位姿...') + start_time = time.time() + libs.down_from_oss(config.oss_bucket, config.workdir, pid) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 图片下载完成,共费时{libs.diff_time(start_time)}') + + start_time = time.time() + photo1_path = os.path.join(config.workdir, pid, 'photo1') + photo2_path = os.path.join(config.workdir, pid, 'photo2') + photos1_count = len(os.listdir(photo1_path)) + photos2_count = len(os.listdir(photo2_path)) + if photos1_count + photos2_count < 164: + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} photo1数量{photos1_count} photo2数量{photos2_count},未能覆盖所有相机,是否继续计算相机位姿?') + continue_or_not = input('是否继续计算相机位姿?(y/n)') + if continue_or_not == 'y': + pass + else: + sys.exit(0) + + cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName {pid} \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" \ + -addFolder "{os.path.join(config.workdir, pid, "photo1")}" -selectAllImages \ + -detectMarkers "D:\\make2\\config\\detectMarkers.config.xml" \ + -align -align \ + -exportXMP "D:\\make2\\config\\exportXMP.config.xml" \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -quit' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + + # TODO:加入report相机位姿质量评估 + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} photo1相机位姿完成,共费时{libs.diff_time(start_time)}') + + for xmp in os.listdir(photo1_path): + if xmp.endswith('.xmp'): + shutil.copy(os.path.join(photo1_path, xmp), os.path.join(photo2_path, xmp.replace('_1.xmp', '_8.xmp'))) + + psid = libs.getPSid(pid) + cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName {pid} \ + -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" {config.r["setTextureFalse"]} \ + -addFolder "{os.path.join(config.workdir, pid, "photo2")}" -selectAllImages \ + -detectMarkers "D:\\make2\\config\\detectMarkers.config.xml" \ + {libs.get_defineDistances(config.ps_floor_sticker.get(psid, config.ps_floor_sticker["default"]))} -align -align -update {config.r2["setRegion"]} \ + -exportXMP "D:\\make2\\config\\exportXMP.config.xml" \ + -exportReconstructionRegion "{os.path.join(config.workdir, pid, f"{pid}.rcbox")}" \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -quit' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} photo2相机位姿完成,共费时{libs.diff_time(start_time)}') + + # TODO:加入report相机位姿质量评估 + upload_or_not = input('是否上传oss?(y/n)') + if upload_or_not == 'y': + upload_xmp(pid) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} xmp文件上传完成,共费时{libs.diff_time(start_time)}') + + delete_or_not = input('是否删除本地文件?(y/n)') + if delete_or_not == 'y': + shutil.rmtree(os.path.join(config.workdir, pid)) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 本地文件已删除') + else: + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 本地文件未删除') + +if __name__ == '__main__': + if len(sys.argv) == 2: + pids = sys.argv[1].split(',') + for pid in pids: + main(pid) + else: + print(f'useage: python {sys.argv[0]} pid1,pid2,pid3') + sys.exit(1) \ No newline at end of file diff --git a/tools/push_cmd.py b/tools/push_cmd.py new file mode 100644 index 0000000..1f5f48e --- /dev/null +++ b/tools/push_cmd.py @@ -0,0 +1,37 @@ +import redis, os, sys +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +import config, libs + +def main(cmd, order_id): + if cmd == 'print': + key = 'model:printOrder' + elif cmd == 'repair': + key = 'model:IndependentRepairTeamcheckGLBQueue' + elif cmd == 'make3d': + key = 'model:make' + elif cmd == 'make3d10': + key = 'model:make10' + elif cmd == 'foot': + key = 'model:foot' + + if order_id == 'view': + for i in r.lrange(key, 0, -1): + print(i) + print(f'当前{key}队列长度:{r.llen(key)}') + else: + order_ids = order_id.split(',') + for order_id in order_ids: + r.lpush(key, order_id) + print(f'已推送{order_id}到{key}, 当前队列长度:{r.llen(key)}') + +if __name__ == '__main__': + if len(sys.argv) == 3: + cmd = sys.argv[1] + order_id = sys.argv[2] + else: + print('用法:python push_cmd.py ') + exit(1) + + r = config.redis_local + + main(cmd, order_id) \ No newline at end of file diff --git a/tools/upload_x.py b/tools/upload_x.py new file mode 100644 index 0000000..e024625 --- /dev/null +++ b/tools/upload_x.py @@ -0,0 +1,56 @@ +import os, sys, bpy, time +import platform +if platform.system() == 'Windows': + sys.path.append('e:\\libs\\') +else: + sys.path.append('/data/deploy/make3d/make2/libs/') + +import config, libs, libs_db + +def upload_3d(pid): + start_time = time.time() + workdir = os.path.join(config.workdir, '3d') + + glb_filename = os.path.join(workdir, pid, f'{pid}_decimate.glb') + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {glb_filename} 开始处理...') + if os.path.exists(glb_filename): + config.oss_bucket.put_object_from_file(f'glbs/3d/{pid}.glb', glb_filename) + # break + os.remove(glb_filename) + else: + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {glb_filename} 文件不存在,跳过') + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} glb文件上传完成,共费时{time.time() - start_time}秒') + +def upload_xmp(pid): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 上传xmp文件之前先删除oss上的xmp文件所在目录...') + config.oss_bucket.delete_object(f'xmps/{pid}/') + start_time = time.time() + workdir = os.path.join(config.workdir, pid) + psid = libs.getPSid(pid) + config.oss_bucket.put_object_from_file(f'xmps/{psid}/{psid}.rcbox', os.path.join(workdir, f'{pid}.rcbox')) + for xmp in os.listdir(os.path.join(workdir, 'photo1')): + if xmp.endswith('.xmp'): + config.oss_bucket.put_object_from_file(f'xmps/{psid}/mesh/{xmp}', os.path.join(workdir, 'photo1', xmp)) + for xmp in os.listdir(os.path.join(workdir, 'photo2')): + if xmp.endswith('.xmp'): + config.oss_bucket.put_object_from_file(f'xmps/{psid}/texture/{xmp}', os.path.join(workdir, 'photo2', xmp)) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} xmp文件上传完成,共费时{time.time() - start_time}秒') + +def main(): + pass + +if __name__ == '__main__': + if len(sys.argv) == 3: + action = sys.argv[1] + pids = sys.argv[2] + else: + print(f'useage: python {sys.argv[0]} [3d|xmp] pid1,pid2,pid3') + sys.exit(1) + + for pid in pids.split(','): + if action == '3d': + upload_3d(pid) + elif action == 'xmp': + upload_xmp(pid) \ No newline at end of file