From 218a91659b3f92d14017c6f9a76be1e8f4d13d4f Mon Sep 17 00:00:00 2001 From: jinye_huang Date: Sat, 26 Apr 2025 13:45:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E7=9A=84=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../simple_collage.cpython-312.pyc | Bin 33372 -> 35060 bytes core/simple_collage.py | 204 ++++++++++-------- examples/test_simple_collage.py | 19 +- poster_gen_config.json | 2 +- .../output_handler.cpython-312.pyc | Bin 10649 -> 11678 bytes .../tweet_generator.cpython-312.pyc | Bin 29694 -> 29976 bytes utils/output_handler.py | 18 +- utils/tweet_generator.py | 14 +- 8 files changed, 149 insertions(+), 108 deletions(-) diff --git a/core/__pycache__/simple_collage.cpython-312.pyc b/core/__pycache__/simple_collage.cpython-312.pyc index f10b63d659c43a6dbb4dd9700c844d4e9708dbb2..82c1e67916cd8c58306d08f63b0de2de1f5eda93 100644 GIT binary patch delta 7414 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 Q=Jh0*JykA3xz8;AKWfF$P5=M^ delta 6143 zcmZ`-3sjs%mj0{X?+5*)>8860uZDzY-a-NilWbxn4@e@A7#q52Iy5x-yITnS9WW0i zCK{M>P96j$tK-B`M>05PFvb-}ongoE_(S7{!*I^JdnB4!&x**d*>U&G-s+|aiJ3a^ zb^W*Qz4iL4ZdJ|qa;8sm6d%jw5(?7nCTWB2nQMxF(Q(WY{G2NdOZ8u)MEti-D>fL^ zoTJ>KJcAd3#FuErZySjOj~g!{7YO>$a-t}=y@rS{ib52Wt~VV>@lauDFe4_UsMi)U zVHqPSqYgevQDHeAF|CryU*p7z!it|J*|Cila}KY-S9ls_O59~w$w&|9;%jn^QW=*a zijf`8Co(X}(kx>XeN;c^Zt2+*_=P0Bh!Hn}dpe!8QJBGq`zTi?9!SzAK*rI}zpD?8 zzLH}_S)@<^h1vaFY^NP*%NY)Qt?1|8RdsR0Du(0AY0L!A8iqPtNSa}hGTgH!+>fQEWeW6c9JUILB0MU8b2&^QAc`HLD4JwPJ>8p=hD;s0AUalsOUEicI_i7$8C92w%!qDf)MC|Ai`YbDtY#35J7;ML zqpF~mQ9*5-QmGW3h6k;7e9&r;R8cT3f@1d1$ZeP(fQkPJ7|zSkG;BwA&f< z`kQU_vEI8v^xe9h&=hZ7tUNQLotxpvY0q7;VudYky*X}uw}a8%lzC|wa6f%?y{&y+ z>H20{sM+1t5}N}zO~c6h>*_)@4MVh0;lfNTX%8ql?D)yFGyExTz#YWubc=xNbh)OC z*qLv{73pj7yXhwUa(W`ZnO?zhsPV3h&J78%kv|lj=|e5fU_)R^u$E zlgV%@Xm=pw3m#giJfOx`inLgmS(#G6r>HLK3z71i?6izEl#Nu4JUkj?b(IsMy|bJ! z5*u-6W~*XWK-o$WD>Kgu_;NFrINreTI&O8&3n)kKoAOuX?CLF}l1OF+t4jMqra8Cc z^bR(unB7pp7FV)~dnaW3z=7V_y=B^-`jU>7B~P03=O~f98uf^#lPoW;xwz)VqKidW z#e-`jNku)PS&l$mjiyc37qc#A4Llk#<=ix_9y6^Td?I4n(6f0`YdZf(?;``9%OTzYp zwAy&y(rXzoTs2*lu`8=5Y93<`xg$08Y_*5=bk0)HU(P&5C2Zn+ZZt3UDfcbz;Fclj z)i7JMi%sm@Jt5mOFQeqzSdX)>{^aVK!BjSJ&4g_2q{ZG{KB?4Rkc=x+ChciA?Hk7I z8-|}4>4?}XdKI6m^%s_ptJ9+;C1duI5&dXV#J;yzF$pEv1w#e}Ld)}*^F>QQ{I zw6MywX;7E<%0Y>(tYRyw+3JIA@?#UKLz9Li;!4&lW3A<*M@Jjks)MX0R+0upx--9( zOKB~06i03bCpT60F_oQ7EgG&Get3i)wU27>#ub9jX+zQvvwYC`-#H1gwW2%ok0Huv zpQ8|=5YPdb!ltep&Kx$46pT1WD%g~5Y~`ajwN(*q)eo~=qV~glEuvJ3P?gv?Ez{hT zS;k}*)>g$<*RfSD)>Y4X8rXU-n|yde);O&(kP5}dzXtj!s*~#5#ILYWzq4})rmoms zNPVE@LH+|>Y1*Dt-iPZ~!pBFsoATgi)K+SRpFdKi$$K1}kCRuy$Dg!6;lj_D(L}IN zyvNQRD@xj9=8hYPn{hKAB;$4w$veaqCjNMVr9#J#C+XBfL|#cI+^n(l3s!>fQ|v)kare!;E(gH2|p!LE7=`{3)=#|leZHc$NjtP*qg6H zlA-qelU#HW|83=UZF5vH&=9Jx_qplI#I^%}vMSZI=!WhmLK^`)fdMRCy-{!ivh6we zTfP<#>=$9*>I9TI)VaEwhZ?ZxA!|Bm)@`y!(OXH5?C=iW?E)frYY{HtM;O-$eme zY%UQjbm!vc4D=iP{^sI)DiN2hL+|6wWfhz$1HM*fMA?hIdAm%BfLaPdQR;G86 z@9hNE5-N^Di_Zw_IgEgyhZhzFA!5W5BZ{9>;gC&J z)B!mChQ(nCBgW>vR{UpoI+m8`5@M$Dvrt&dP}sR%hpi=24#UNz$8`AMHfbKBg6g1Suo4cva| zxmyD_Zk@UD)mv|U`TRTda?qkL5-hg%Sa7{q~)iX9i;CxObmM32T}PrIV7V zOZ;uQ9sgyYEKZV7R#|q+el+x;ewX@FViJd2y{=%xl!Rm(++I&ZkRBjfB?PVzAgm9N z(+)mWwh~|6uN3?b0?Bu{6QaJwD^7szUT3i;Ye={{(gTRS0dhtr3Z8H zqmL%xJ6?I_glhS;O2-=W2knEU*Om?H27M!IN3vM`BcG|tXLX>C=8cqCfj`=kidAdH zs0%;qDuy|JrqV;Xc>jpt>Li9{L=z*85A+flipv_ypxp{a>f%4B)I=^}QfxV5#$DSa zxUErUzK0^m7bqeN%NbcvjFb0TgfcP*ht+s@ZW2;p^teH%xQ{OiIM(aPH9!p&6T5|$ zQj8Smu1w@AfJFb+qI*ceEZ**bOT=w70heS;xFo-iODBO#iG)jNC7!Ncz5DbMwrm9l z4l(@_{Ln80Oz{Km7O#{3Imyc#U{2vc)f}Xqfgt?~kqMvh*3!=A;1sXbNjF6e%W+Ok zMvPOrpt8WJBQ;s+a+(JMI-fuxfi40a1eWXpu*X^wlt1DdzEzWvG61?$T-x6{#f2T5 zCb!=E=!J7@O@gOE-kufTv~*4WfYVzSKMI;)Aq46X?#ee1&f!Z3YPUcf)f9IN=S7rE z+1+}k=<>1)^?ljn(zHpv<$~{WU|@A$2dmHS-U`P*vGSbaw4$eVLS%(6bJ{>vBq?`} zl8E;JN6YZ>HFo^iL8F4z@A^!&n~-r2kP#nnWniX2jQ)M-_Xl4==-HvBLkeW>U*sMv z@^dgTWVuF@OOCHRuHz~~8}ZwZmx|((gcZ(p#$SL&J7>8qy19qQT3;AskF(Ge9nLP0 z#m3Z6a;pej#%RBB!;c2mM#`e$i$t?2A?^0Sogok#*4Ihl8w7qu;7u&4-6-gW?2-W2 zSeqayBV#%~bfQ*(xGHfqUa@9594NfV(WfZ|LA9`s#!Y}q4`W$n5?R3xMuhf zRn&Uv=eZo^`AlngytWi?q4~uXK&w7e=cY7D@Q#3Y^j%o1T27S(w z$Uy9D1W4DWxE^N<{WbXb-tbG6@1$FJrdw2o3^20{BcuePVDqSDvIW!9U-$@>EPZb zckJ6mq>Sv*4n)6%?af=)o+lF6GANpyA?T9?J|S?5z*7VWvuQE`G-)`^gYAvs2TRabbQ#q0`KNo@pXSPGU22nR(!{= zNv)uQO7Is=@Xi8WNrD*Xf%qm6Qw$F_EZC3?6oVMf>%7-U=!6s&GD13!;o~DM$=E^L z<$Q>sf`u?9#QW)dn`o(o;oi3yd3m_kWfRI668GfzQ`+DV5+#?SUl?x&yhZVU^fZwj z7U2z6>n8F2?p+c_1eFNxTEmlqG%PwMI{QKF_wGA{LmBEpLtg|6P>s(U2(Gt%`TUEw zx_iF7bTSr7XZrqWru+O?SFV0@qkGr39XZ>gZwxbUzWvp;E8pBW{mqTOnOBbAdf{@+ zZ06-_U%mD7Z*H9Z>b>V@`d;|*)$1{1?08}y&%vS3U_mR5%5ht3@jf~2gW;o_2uKLD z5I8~rjujLgAQ0Unqon8}pKAz&2#{6a5Kk#w@w<48if3?Y+Zt_*-O;1zCeRCz=z#~? zQq)?Q)_2K5eH=1f6l_Y7wrtEhnzgfA3_F=Z(^C|Y+vX@0{{ZSnvmB-P0D^#QNWZDy zIHun?sY;&ICrz3&W`&fY1bs*G42tfp-^wVJ0g7&_(#BM21D*+0&NP0wy;x`=Zst?* zx%L7+FY(U281p&`vd3gK;1ga)G~_mAtf$b$^TGnFU#eBn9GNJ9m4mf(W0UTV~fHRmpi5Wir6M9A`VkeWaM1cbeKVym{ zR#2FWDg2p9=A`oA%$I^K;g*t1f3#YZ`I;A+9 zmf~M^X}NNShff^qF!13dp@Nfy03Kq6i~!ckv#=gS#FI`PrQnk1QidN3=&*i7u7uY$ zi|dEWaOpBeNWyvI;<`hWOG`Zby*4E3qNRFBbpWK_xJh*sk#j~|5B=YKu^zTg=mI5YIhiCt(5VeFK& z)*0~DZT2_Udp%RUZRJ~b#z;)a6$|CjLp&m%Q4&Yly+~q*7#E!~%a(AIgWFDJiqC-W z7*hvN>7-d;{ojPC-y_VP`iBI#$Q#lowbn_UeqP8+5Oi(Fc~8A#Nl3hzP&k%QIH^ya zOfXCumjN?1D}k6ANw*LypZ+FkIXH4?V~Z<#pA*B`rOsXJtZT%Y?m|a&5k^}WZ9m}{ zO~^zO!qB7{w1j{h`@4;je8|Ed6{yC&-6@(Q5|r{SMew_NT{be!RU`a|?iK$J>6zU{ diff --git a/core/simple_collage.py b/core/simple_collage.py index 4dd4193..4892f7d 100644 --- a/core/simple_collage.py +++ b/core/simple_collage.py @@ -134,7 +134,16 @@ class ImageCollageCreator: return img def create_collage_with_style(self, input_dir, style=None, target_size=None): - """创建指定样式的拼接画布""" + """创建指定样式的拼接画布 + + 参数: + input_dir: 输入图片目录路径 + style: 拼贴样式,如不指定则随机选择 + target_size: 目标尺寸,默认为(900, 1200) + + 返回: + tuple: (拼贴图, 选择的图片名称列表),如果创建失败则返回(None, []) + """ logging.info(f"--- Starting Collage Creation for Directory: {input_dir} ---") # Start Log try: # 设置默认尺寸为3:4比例 @@ -149,7 +158,7 @@ class ImageCollageCreator: # 检查目录是否存在 if not os.path.exists(input_dir): logging.error(f"Input directory does not exist: {input_dir}") - return None + return None, [] # 支持的图片格式 image_extensions = ('.jpg', '.jpeg', '.png', '.bmp') @@ -160,7 +169,7 @@ class ImageCollageCreator: logging.info(f"Files found in directory: {all_files}") except Exception as e: logging.exception(f"Error listing directory {input_dir}: {e}") - return None + return None, [] # 过滤图片文件 all_images_names = [f for f in all_files @@ -169,7 +178,7 @@ class ImageCollageCreator: if not all_images_names: logging.warning(f"No valid image files found in directory: {input_dir}") - return None # Return None if no images found + return None, [] # Return None if no images found # 根据不同样式,确定需要的图片数量 # ... (logic for num_images based on style) ... @@ -193,75 +202,72 @@ class ImageCollageCreator: selected_images_names = (all_images_names * (num_images // len(all_images_names) + 1))[:num_images] else: logging.error("Cannot select images, none were found.") # Should not happen due to earlier check - return None + return None, [] else: # 随机选择指定数量的图片 selected_images_names = random.sample(all_images_names, num_images) - logging.info(f"Selected image files for collage: {selected_images_names}") + # 记录并输出被选择的图片名称 + logging.info(f"Selected images for collage: {selected_images_names}") + print(f"为拼贴图选择的图片: {selected_images_names}") - # 加载图片 + # 加载选中的图片 images = [] - loaded_image_paths = set() for img_name in selected_images_names: - img_path = os.path.join(input_dir, img_name) + image_path = os.path.join(input_dir, img_name) try: - img = Image.open(img_path).convert('RGBA') + img = Image.open(image_path).convert('RGBA') images.append(img) - loaded_image_paths.add(img_path) - logging.info(f"Successfully loaded image: {img_path}") + logging.debug(f"Successfully loaded image: {img_name}") except Exception as e: - logging.error(f"Failed to load image {img_path}: {e}", exc_info=True) # Log exception info - # Optionally: try to replace failed image (or just log and continue) - # For simplicity now, just log and continue; the check below handles insufficient images. + logging.exception(f"Error loading image {img_name}: {e}") - # 再次检查实际加载成功的图片数量 - if len(images) < num_images: - logging.error(f"Needed {num_images} images, but only successfully loaded {len(images)}. Cannot create collage.") - # Log which images failed if possible (from error logs above) - return None - - logging.info(f"Successfully loaded {len(images)} images for collage.") - - # 创建空白画布 (moved after image loading success check) - # collage_image = Image.new('RGBA', target_size, (0, 0, 0, 0)) # This line seems unused as styles create their own canvas + if len(images) == 0: + logging.error("No images could be loaded. Cannot create collage.") + return None, [] - # 应用所选样式 - logging.info(f"Applying style '{style}'...") - result_collage = None + # 确保图片数量满足要求,不足则复制已有图片 + while len(images) < num_images: + images.append(random.choice(images).copy()) + logging.debug(f"Duplicated an image to reach required count of {num_images}") + + # 根据样式创建拼图 + collage = None if style == "grid_2x2": - result_collage = self._create_grid_2x2_collage(images, target_size) - # ... (elif for all other styles) ... + collage = self._create_grid_2x2_collage(images, target_size) elif style == "asymmetric": - result_collage = self._create_asymmetric_collage(images, target_size) + collage = self._create_asymmetric_collage(images, target_size) elif style == "filmstrip": - result_collage = self._create_filmstrip_collage(images, target_size) - # elif style == "circles": - # result_collage = self._create_circles_collage(images, target_size) - elif style == "polaroid": - result_collage = self._create_polaroid_collage(images, target_size) + collage = self._create_filmstrip_collage(images, target_size) + elif style == "circles": + collage = self._create_circles_collage(images, target_size) elif style == "overlap": - result_collage = self._create_overlap_collage(images, target_size) + collage = self._create_overlap_collage(images, target_size) + elif style == "polaroid": + collage = self._create_polaroid_collage(images, target_size) elif style == "mosaic": - result_collage = self._create_mosaic_collage(images, target_size) + collage = self._create_mosaic_collage(images, target_size) elif style == "fullscreen": - result_collage = self._create_fullscreen_collage(images, target_size) + collage = self._create_fullscreen_collage(images, target_size) elif style == "vertical_stack": - result_collage = self._create_vertical_stack_collage(images, target_size) - else: - logging.warning(f"Unknown style '{style}', defaulting to grid_2x2.") - result_collage = self._create_grid_2x2_collage(images, target_size) + collage = self._create_vertical_stack_collage(images, target_size) - if result_collage is None: - logging.error(f"Collage creation failed during style application ('{style}').") - return None + if collage: + logging.info(f"Successfully created collage with style: {style}") else: - logging.info(f"--- Collage Creation Successful for Directory: {input_dir} ---") - return result_collage # Return the created collage image + logging.error(f"Failed to create collage with style: {style}") + return None, [] + + # 清理内存中的原始图像 + for img in images: + if hasattr(img, 'close'): + img.close() + return collage, selected_images_names except Exception as e: - logging.exception(f"An unexpected error occurred during collage creation for {input_dir}: {e}") # Log full traceback - return None + logging.exception(f"Error in create_collage_with_style: {e}") + traceback.print_exc() + return None, [] def _create_grid_2x2_collage(self, images, target_size): """创建2x2网格拼贴画""" @@ -503,15 +509,16 @@ class ImageCollageCreator: return collage def _create_overlap_collage(self, images, target_size): - """创建无缝重叠风格拼贴画""" + """创建无重叠风格拼贴画(不允许图片重叠)""" collage = Image.new('RGBA', target_size, (255, 255, 255, 255)) width, height = target_size - # 计算每个图像确切填充一个区域的大小 - img_width = width // 2 - img_height = height // 2 + # 为了避免重叠,计算每个图像区域的大小 + grid_size = 2 # 2x2网格 + img_width = width // grid_size + img_height = height // grid_size - # 网格位置 - 完全填充画布 + # 定义网格位置 - 确保无重叠 positions = [ (0, 0), # 左上 (img_width, 0), # 右上 @@ -527,20 +534,19 @@ class ImageCollageCreator: img = self.resize_and_crop(img, (img_width, img_height)) # 应用效果 img = self.apply_image_effect(img) - # 不添加边框 # 粘贴到拼贴画 collage.paste(img, position) - print(f"添加无缝拼贴画块 {i+1} 到位置: {position}") + print(f"添加无重叠拼贴画块 {i+1} 到位置: {position},尺寸: {img_width}x{img_height}") - print(f"无缝拼贴画创建成功,尺寸: {target_size}") + print(f"无重叠拼贴画创建成功,尺寸: {target_size}") return collage def _create_mosaic_collage(self, images, target_size): - """创建马赛克风格拼贴画(需要9张图片)""" + """创建马赛克风格拼贴画(需要9张图片,无重叠)""" collage = Image.new('RGBA', target_size, (255, 255, 255, 255)) width, height = target_size - # 创建3x3网格 + # 创建3x3网格,确保无重叠 grid_width = width // 3 grid_height = height // 3 @@ -550,17 +556,6 @@ class ImageCollageCreator: for col in range(3): positions.append((col * grid_width, row * grid_height)) - # 定义不同的效果 - effects = ["none", "grayscale", "sepia", "vintage", "high_contrast", - "color_boost", "vibrant", "warm", "none"] - - # 保证所有效果都能使用 - if len(effects) > len(images): - effects = effects[:len(images)] - - # 随机打乱效果 - random.shuffle(effects) - # 将图像粘贴到马赛克位置 for i, position in enumerate(positions): if i < len(images): @@ -568,17 +563,16 @@ class ImageCollageCreator: # 调整大小 img = self.resize_and_crop(img, (grid_width, grid_height)) # 应用效果 - img = self.apply_image_effect(img, effects[i % len(effects)]) - # 不添加边框 + img = self.apply_image_effect(img) # 粘贴到拼贴画 collage.paste(img, position) - print(f"添加马赛克拼贴画块 {i+1} 到位置: {position}") + print(f"添加马赛克拼贴画块 {i+1} 到位置: {position},尺寸: {grid_width}x{grid_height}") - print(f"无缝马赛克拼贴画创建成功,尺寸: {target_size}") + print(f"无重叠马赛克拼贴画创建成功,尺寸: {target_size}") return collage def _create_fullscreen_collage(self, images, target_size): - """创建全覆盖拼图样式(完全填满画布,无空白)""" + """创建全覆盖拼图样式(完全填满画布,无空白,无重叠)""" width, height = target_size collage = Image.new('RGBA', target_size, (255, 255, 255, 255)) # 白色背景 @@ -589,7 +583,7 @@ class ImageCollageCreator: else: return None - # 定义区域划分 - 按照完全填满画布设计 + # 定义区域划分 - 按照完全填满画布设计,确保无重叠 regions = [ # 左列 - 上中下三块 (0, 0, width//2, height//3), # 左上 @@ -602,7 +596,7 @@ class ImageCollageCreator: (width//2, height*2//3, width, height) # 右下 ] - # 添加图片到各个区域,确保完全覆盖 + # 添加图片到各个区域,确保完全覆盖无重叠 for i, (x1, y1, x2, y2) in enumerate(regions): if i < len(images): img = images[i].copy() @@ -612,18 +606,14 @@ class ImageCollageCreator: region_height = y2 - y1 img = self.resize_and_crop(img, (region_width, region_height)) - # 应用轻微的图像效果,确保视觉统一性 + # 应用轻微的图像效果 img = self.apply_image_effect(img) - # 不添加边框,实现无缝拼接 - # 粘贴到画布上 collage.paste(img, (x1, y1)) - print(f"添加全屏拼图块 {i+1} 到位置: ({x1}, {y1}, {x2}, {y2})") + print(f"添加全屏拼图块 {i+1} 到位置: ({x1}, {y1}, {x2}, {y2}),尺寸: {region_width}x{region_height}") - # 不添加分隔线,保持无缝 - - print(f"无缝全覆盖拼图创建成功,尺寸: {target_size}") + print(f"无重叠全覆盖拼图创建成功,尺寸: {target_size}") return collage def _create_vertical_stack_collage(self, images, target_size): @@ -708,32 +698,58 @@ def process_directory(directory_path, style=None, target_size=(900, 1200), outpu output_count: 需要生成的拼贴图数量,默认为 1 返回: - list: 生成的拼贴图列表(PIL.Image 对象);如果生成失败,返回空列表 + tuple: (拼贴图列表, 使用的图片名称列表的列表),如果生成失败,返回 ([], []) + 拼贴图列表是PIL.Image对象列表 + 图片名称列表是一个列表的列表,每个子列表包含一张拼贴图使用的图片文件名 """ logging.info(f"处理目录中的图片并创建 {output_count} 个拼贴图: {directory_path}") # 创建 ImageCollageCreator 实例 collage_creator = ImageCollageCreator() collage_images = [] + used_image_names = [] # 存储每个拼贴图使用的图片文件名 # 检查目录是否存在 if not os.path.exists(directory_path): logging.error(f"目录不存在: {directory_path}") - return [] + return [], [] + + # 支持的图片格式 + image_extensions = ('.jpg', '.jpeg', '.png', '.bmp') + + # 获取目录中的所有有效图片文件 + try: + all_files = os.listdir(directory_path) + all_images_names = [f for f in all_files + if f.lower().endswith(image_extensions) and os.path.isfile(os.path.join(directory_path, f))] + + if not all_images_names: + logging.error(f"目录中没有有效的图片文件: {directory_path}") + return [], [] + + logging.info(f"目录中找到 {len(all_images_names)} 个有效图片文件") + except Exception as e: + logging.exception(f"列出目录内容时出错: {e}") + return [], [] # 尝试创建请求数量的拼贴图 for i in range(output_count): try: - # 随机选择一个样式(由 create_collage_with_style 内部实现) - # 传入 None 作为 style 参数,让函数内部随机选择 - collage = collage_creator.create_collage_with_style( + # 创建拼贴图,使用指定样式 + collage, selected_images_names = collage_creator.create_collage_with_style( directory_path, - style=style, # 让方法内部随机选择样式 + style=style, target_size=target_size ) if collage: collage_images.append(collage) + + # 从输出日志中解析出使用的图片名称 + # 由于我们修改了create_collage_with_style来打印选择的图片 + # 可能需要进一步修改为直接返回选择的图片 + used_image_names.append(selected_images_names) + logging.info(f"成功创建拼贴图 {i+1}/{output_count}") else: logging.error(f"无法创建拼贴图 {i+1}/{output_count}") @@ -741,7 +757,7 @@ def process_directory(directory_path, style=None, target_size=(900, 1200), outpu logging.exception(f"创建拼贴图 {i+1}/{output_count} 时发生异常: {e}") logging.info(f"已处理目录 {directory_path},成功创建 {len(collage_images)}/{output_count} 个拼贴图") - return collage_images + return collage_images, used_image_names def find_main_subject(image): # ... (keep the existing implementation) ... @@ -767,7 +783,7 @@ def main(): # 方法 1: 使用 process_directory 函数 (推荐用于外部调用) logging.info("方法 1: 使用 process_directory 函数生成拼贴图...") - collages_1 = process_directory( + collages_1, used_image_names_1 = process_directory( directory_path=test_directory, target_size=(900, 1200), # 默认 3:4 比例 output_count=2 # 创建 2 张不同的拼贴图 @@ -793,7 +809,7 @@ def main(): for style in styles_to_try: logging.info(f"尝试使用样式: {style}") - collage = creator.create_collage_with_style( + collage, selected_images_names = creator.create_collage_with_style( input_dir=test_directory, style=style, target_size=(800, 1000) # 自定义尺寸 diff --git a/examples/test_simple_collage.py b/examples/test_simple_collage.py index be29fed..79b9bde 100644 --- a/examples/test_simple_collage.py +++ b/examples/test_simple_collage.py @@ -133,7 +133,7 @@ def main(): # 1. 处理整个目录,获取多个拼贴图 print("方法1: 使用process_directory批量生成") - collages = simple_collage.process_directory( + collages, used_image_names = simple_collage.process_directory( input_dir, target_size=target_size, output_count=args.count @@ -147,7 +147,11 @@ def main(): for i, collage in enumerate(collages): output_path = os.path.join(args.output_dir, f"collage_auto_{i+1}.png") collage.save(output_path) - print(f"拼贴图已保存: {output_path}") + # 输出使用的图片名称 + if i < len(used_image_names): + print(f"拼贴图已保存: {output_path},使用图片: {used_image_names[i]}") + else: + print(f"拼贴图已保存: {output_path}") # 2. 使用不同风格创建拼贴图 print("\n方法2: 测试不同风格") @@ -156,10 +160,13 @@ def main(): for style in styles: print(f"创建 {style} 风格拼贴图...") try: - collage = collage_creator.create_collage_with_style(input_dir, style, target_size) - output_path = os.path.join(args.output_dir, f"collage_style_{style}.png") - collage.save(output_path) - print(f"风格拼贴图已保存: {output_path}") + collage, selected_images = collage_creator.create_collage_with_style(input_dir, style, target_size) + if collage: + output_path = os.path.join(args.output_dir, f"collage_style_{style}.png") + collage.save(output_path) + print(f"风格拼贴图已保存: {output_path},使用图片: {selected_images}") + else: + print(f"创建 {style} 风格失败: 未返回有效拼贴图") except Exception as e: print(f"创建 {style} 风格失败: {e}") diff --git a/poster_gen_config.json b/poster_gen_config.json index 3606e97..28f70c4 100644 --- a/poster_gen_config.json +++ b/poster_gen_config.json @@ -1,6 +1,6 @@ { "date": "4月30日, 4月28日, 5月1日", - "num": 10, + "num": 1, "variants": 3, "topic_temperature": 0.2, "topic_top_p": 0.3, diff --git a/utils/__pycache__/output_handler.cpython-312.pyc b/utils/__pycache__/output_handler.cpython-312.pyc index ebc34227b7db9cdadfc7873fb863ff6dc2b34d66..71038d0edb95af409a3fdb7d654aeb28ee339074 100644 GIT binary patch delta 2477 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 RC*vcae(7wzgq#KU{4Zen)?oku delta 1299 zcmZ9LZ%kWN6u|H8>w~`a5B)=1%A=))Lf4howdFtPfTB@>v6V3g2$Tn}gCS77t+J7a zTl}PksM}4A9FCK}^?Z_F*;`4s~_ndHG6 zP_?ulw;gKMs>S_6m4n1A)!?&Td*q3g5m)q=+>>A^ zFSM9y#L$1`Igvx0a?GPoMeZJxd0FH{qkK$!E#|i$6KxlO;9YsD<_D@}Fa9g$fLE}! zkEaTjAL3JeG3dsgBjuT$z9CpRpllk%-TiHr8PXj4Cc6L^z)5NlEI|KIW~IMC55xEp zA1=uiP9$e$_{qegb{B&4bCZ4wbL0CkKmI(tjW3U%!7ogtU@&uS;!B9r2~Jasu8>y-+Co?X|4Z|cHXqwT!*qmeA@&9c>5HzzSb zAISq+Z^)bR$8+mC;xIKl0JO=RH{$n_tZ7gcgS1sTt^zvun!5a9C;mEl56w9E&X(Q0b5(0X9h zLv4C!+3+**5^6J27PnAa7Mk!D%Z%BnEw6vfOl_NK!rP?`&(hnK9B-q4v(Y?@FTGaV zT1qY($={?G6%pz8qiv$lfGkcBk<;*Ka-K)21o~a}$zL}|rB0>LpV*9s%XVa2J&{BS zL|zP_d$N%KIQkplcTt<`4>^E7K9l6r=v&$UKyjpBpPxw}2fmA((3hc8_3-o}1>K+4 zNkGRTj} zf@VLvRk6(==HKy9PsP~4>;(kQVCmVx>v$*G_V)HO=w3cBTT c9tA4^7m_L!^vYPJU^VdV%ll+RPIBgd0cERd{Qv*} diff --git a/utils/output_handler.py b/utils/output_handler.py index 4e5ece1..60c4049 100644 --- a/utils/output_handler.py +++ b/utils/output_handler.py @@ -22,13 +22,14 @@ class OutputHandler(ABC): pass @abstractmethod - def handle_generated_image(self, run_id: str, topic_index: int, variant_index: int, image_type: str, image_data, output_filename: str): + def handle_generated_image(self, run_id: str, topic_index: int, variant_index: int, image_type: str, image_data, output_filename: str, metadata: dict = None): """Handles a generated image (collage or final poster). Args: image_type: Either 'collage' or 'poster'. image_data: The image data (e.g., PIL Image object or bytes). output_filename: The desired filename for the output (e.g., 'poster.jpg'). + metadata: Optional dictionary with additional metadata about the image (e.g., used image files). """ pass @@ -119,7 +120,7 @@ class FileSystemOutputHandler(OutputHandler): except Exception as save_err: logging.error(f"Failed to save complete poster configurations for topic {topic_index} to {config_path}: {save_err}") - def handle_generated_image(self, run_id: str, topic_index: int, variant_index: int, image_type: str, image_data, output_filename: str): + def handle_generated_image(self, run_id: str, topic_index: int, variant_index: int, image_type: str, image_data, output_filename: str, metadata: dict = None): """Saves a generated image (PIL Image) to the appropriate variant subdirectory.""" subdir = None if image_type == 'collage': @@ -134,9 +135,22 @@ class FileSystemOutputHandler(OutputHandler): save_path = os.path.join(target_dir, output_filename) try: + # 保存图片 # Assuming image_data is a PIL Image object based on posterGen/simple_collage image_data.save(save_path) logging.info(f"Saved {image_type} image to: {save_path}") + + # 保存元数据(如果有) + if metadata: + metadata_filename = os.path.splitext(output_filename)[0] + "_metadata.json" + metadata_path = os.path.join(target_dir, metadata_filename) + try: + with open(metadata_path, 'w', encoding='utf-8') as f: + json.dump(metadata, f, ensure_ascii=False, indent=4) + logging.info(f"Saved {image_type} metadata to: {metadata_path}") + except Exception as me: + logging.error(f"Failed to save {image_type} metadata to {metadata_path}: {me}") + except Exception as e: logging.exception(f"Failed to save {image_type} image to {save_path}: {e}") diff --git a/utils/tweet_generator.py b/utils/tweet_generator.py index 7f807f8..4471985 100644 --- a/utils/tweet_generator.py +++ b/utils/tweet_generator.py @@ -627,7 +627,7 @@ def generate_posters_for_topic(topic_item: dict, # --- Image Collage --- logging.info(f"Generating collage from: {input_img_dir_path}") - collage_images = core_simple_collage.process_directory( + collage_images, used_image_filenames = core_simple_collage.process_directory( input_img_dir_path, style=collage_style, target_size=poster_target_size, @@ -638,14 +638,17 @@ def generate_posters_for_topic(topic_item: dict, logging.warning(f"Warning: Failed to generate collage image for Variant {variant_index}. Skipping poster.") continue collage_img = collage_images[0] # 获取第一个 PIL Image - logging.info(f"Collage image generated successfully (in memory).") + used_image_files = used_image_filenames[0] if used_image_filenames else [] # 获取使用的图片文件名 + logging.info(f"Collage image generated successfully (in memory). Used images: {used_image_files}") + print(f"拼贴图使用的图片文件: {used_image_files}") - # --- 使用 Handler 保存 Collage 图片 --- + # --- 使用 Handler 保存 Collage 图片和使用的图片文件信息 --- output_handler.handle_generated_image( run_id, topic_index, variant_index, image_type='collage', image_data=collage_img, - output_filename='collage.png' # 或者其他期望的文件名 + output_filename='collage.png', # 或者其他期望的文件名 + metadata={'used_images': used_image_files} # 添加图片文件信息到元数据 ) # --- 结束保存 Collage --- @@ -677,7 +680,8 @@ def generate_posters_for_topic(topic_item: dict, run_id, topic_index, variant_index, image_type='poster', image_data=poster_img, - output_filename=output_poster_filename # 使用参数中的文件名 + output_filename=output_poster_filename, # 使用参数中的文件名 + metadata={'used_collage': True, 'collage_images': used_image_files} ) # --- 结束保存 Poster --- else: