选自 贝克.哈吉斯《管道的故事》

博客讲述了很久以前,意大利一个小村子里,有两位名叫波罗波罗和布鲁诺的堂兄弟,他们雄心勃勃,是好朋友,也是大梦想者。

很久、很久以前,有两位名叫柏波罗和布鲁诺的年轻人,他们是堂兄弟,雄心勃勃,住在意大利的一个小村子里。 两位年轻人是最好的朋友。

他们是大梦想者。

他们是大梦想者。

   他们不停里谈着,渴望有一天能通过某种方式,让他们可以成为村里最富有的人。他们都很聪明而且勤奋。他们想他们需要的只是机会。

   一天,机会来了。村里决定雇两个人把附近河里的水运到村广场的水缸里去。这份工作交给了柏波罗和布鲁诺。两个人都抓起两个水桶奔向河边。一天结束后,他们把整镇上的水缸装满了。村里的长辈按每桶水一分钱的价线付钱给他们。”   

“我们的梦想实现了!”布鲁诺大喊着,“我简直无法相信我们的好福气。”

   但柏波罗不是非常确信。

   他的背又酸又痛,提那重重的大桶的手也起了泡。他害怕明天早上起来又要去工作。他发誓要想出更好的办法,将河里的水运到村里去。

“布鲁诺,我有一个计划,”第二天早上,当他们抓起水桶往河边奔时,柏波罗说,“一天才几分钱的报酬,而要这样来回提水,干脆我们修一条管道将水从河里引进村里去吧。”

 布鲁诺愣住了。

 “一条管道?谁听说过这样的事?”布鲁诺大声嚷嚷着,“柏波罗,我们有一份很不错的工作。我一天可以提100桶水。一分钱一桶水的话,一天就是1元钱!我是富人了!一个星期后,我就可以买双新鞋。一个月后,我就可以买一头母牛。六个月后,我可以盖一间新房子。我们有全镇最好的工作。我们一周只需工作五天,每年2周的有薪假期,我们这辈子可以享受生活了!放弃你的管道吧!

   但柏波罗不是容易气馁的人。他耐心地向他最好的朋友解释这个计划。柏波罗将一部分白天的时间用来提桶运水,用另一部分时间以及周末来建造管道。他知道,在岩石般硬的土壤中挖一条管道是多么艰难。因为他的薪酬是根据运水的桶数来支付的,他知道他的收入在。开始的时候会降低。而且他亦知道,要等12年,他的管道才开始产生可观的效益。但柏波罗相信他的梦想终会实现。于是他就去做了。

   布鲁诺和其他村民开始嘲笑柏波罗,称他“管道人柏波罗”。布鲁诺赚到比柏波罗多一倍的钱,·炫耀他新买的东西。他买了一头驴,配上全新的皮鞍,拴在他新盖的两层楼旁。
  
他买了亮闪闪的新衣服,在乡村饭馆里吃可口的食物。村民尊称他为布鲁诺先生。当他坐在酒吧里,为人们买上几杯,而人们则为他所讲的笑话开怀大笑。

 小小的行为等于巨大的结果

   当布鲁诺晚间和周末睡在吊床上悠然自得时,柏波罗还在继续挖管道。头几个月,柏波罗的努力并没有多大进展。他工作很辛苦二—比布鲁诺的工作更辛苦,因为柏波罗晚上和周末都在工作。

   但柏波罗不断地提醒自己,明天梦想的实现是建造在今天的牺牲上面的。一天一天过去了,他继续挖,每次只是一英寸。

   “一英寸、又一英寸成为一英尺,”他一边挥动凿子,打进岩石般硬的土壤中,一边重复这句话。一英寸变成一英尺,然后1020…尺……100尺……

   “短期的痛苦等于长期的回报,”每天完成工作后,筋疲力尽的他跌跌撞撞地回到他简陋的小屋时,他这样提醒自己。他通过设定和通过每天的目标来衡量工作成效。他知道,终有一天,回报将大大超过付出。

   “目光盯在回报上,”每当他慢慢入睡,耳边尽是酒馆中村民的笑声时,他一遍遍地重复这句话。

“目光盯在回报上。”

时来运转   

一天天、一月月过去了。有一天,柏波罗意识到他的管道完成了一半,这意味着他只需提桶走一半路程了!柏波罗把额外的时间用来建造管道。完工的日期终于越来越近了。

在他休息的日候,柏波罗看到他的老朋友布鲁诺在费力地运水。布鲁诺比以前更加驼背。由于长期劳累,步伐也变慢了。布鲁诺很生气,闷闷不乐,为他自己注定辈子提桶而愤恨。

他开始花较少的时间在吊床上,却花更多的时间在酒吧里。当布鲁诺进来时,酒吧的老顾客窃窃私语:“提涌人布鲁诺来了。”当镇上的醉汉

模仿布鲁诺驼背的姿势和拖着脚步走路的样子时,他们咯咯大笑。布鲁诺

不再给别人喝了,也不再讲笑话了。他宁愿独自坐在漆黑的角落里,被一大堆空瓶所包围。

   最后,的大日子终于来到子——管道完工了!村民们簇拥着来看水从管道中流入水槽里!现在村子源源不断地有新鲜供应了,附近村子的人都搬到这条村来,村子顿时繁荣起来。

   管道一完工,柏波罗便不用再提水桶了。无论他是否工作,水源源不断地流人。他吃饭时,水在流人。他睡觉时,水在流人。当他周末去玩时,水在流人。流人村子的水越多,流人柏波罗口袋的钱也越多。

   管道人柏波罗的名气大了,人们称他为奇迹制造者,政客们称赞他有远见,恳请他竞选市长。但柏波罗明白他所完成的并不是奇迹,这只是一个很大、很大梦想的第一步。知道吗,柏波罗的计划大大超越了这条村庄。

招募他的朋友帮忙   

 

 

 

 

 

   管道迫使提桶人布鲁诺失去了工作。看着他的老朋友向酒吧,老板讨免费的酒喝,柏波罗心里很难受。于是柏波罗安排了一次于布鲁诺的会面。

 

 

 

 

 

  “布鲁诺,我来这里是想请求你的帮助。”

 

    

    

    

    

布鲁诺挺起腰,眯着他那无神的眼睛,声音沙哑地说:别挖苦我了。

   “我不是来向你夸耀的,”柏波罗说,“我是来向你提供一个很好的生意机会。建造第一条管道花了我两年多的时间。但这两年里我学到很多!我知道使用什么工具、在哪里挖、如何排管。一路上我都做了笔记。我开发一个系统,能让我们建造另一条管道,然后另一条…另一条…

   “我自己一年可以建一条管道。但这并不是利用我的时间的最好方式。我想做的是教你和其他人建造管道…然后你教其他人…然后他们再教其他人…直到管道铺满本地区的每条村落…最后,全世界的每一条村子都有管道。”

   “只要想一想,”柏波罗继续说,“我们只须从流进这些管道的水中赚取一个很小的比例。越多的水流进管道,就有越多的钱流进我们的口袋。我所建的管道不是梦想的结束,而只是开始。”

   布鲁诺终于明白这幅宏伟的蓝图。他笑了,向他的老朋友伸出他那粗糙的手。他们紧紧地握住对方的手,像失散多年的老朋友那样拥抱。

在提桶世界里的管道梦想

    许多年过去了。柏波罗和布鲁诺已退休多年。他们遍布全球的管道生意每年把几百万的收入泵进他们的银行账户。当他们有时到全国各地旅行时,柏波罗和布鲁诺遇到那些提水桶的年轻人。

    这两个一起长大的朋友总是把车停下来,将自己的故事讲给年轻人听,帮助他们建立自己的管道。一些人愿意听,并且立即抓住这个机会,开始做管道生意。但悲哀的是,大部分提桶者总是不耐烦地拒绝这个建造管道的念头,柏波罗和布鲁诺无数次地听到相同的藉口。

    “我没有时间”。

    “我朋友告诉我,他认识的一个朋友的朋友试图建造管道,但失败了。”“只有那些很早加人的人才可以从管道那里赚到钱。

    “我这辈子一直都提水桶,我只想维持现状。

    “我知道有些人在管道的骗局中亏了钱,我可不会。

    柏波罗和布鲁诺为许多缺乏远见的人而感到悲裒。

    但他们承认,他们生活在一个提桶的世界里,只有一小部分人敢做管道的梦

你是谁?——提桶者?还是管道建造者?

   你是否只有来到公司、把工作于了才有收人?好像提桶人布鲁诺? 或者你做一次工作,然后一次又一次地得到回报,就像管道建造者柏波罗一样?

   如果你像大部分人一样,你在使用提桶计划换钱的陷阱”。你知道如下绕口令:

   一小时的工作换一小时的报酬

   一个月的工作换一个月的报酬

一年的工作换一年的报酬

这听起来熟悉吗?

        我称它为“时间换金钱”的陷阱。提桶的问题在于,当你停止提桶时,收入也停止了。即是说有保障的工作”或“梦想的工作”的概念只是一个幻觉。提桶的浅在危险在于,收入是暂时的,而不是持续的。

如果布鲁诺某天早上醒来发现自己背部扭伤,起不了床,那一天他可以赚多少钱?!

没有工作,就没有收入!

任何提桶的工作都是一样的。只要提桶者用完了病假日或休假日,如果他们不继续提桶,他们就不会得到薪水。

牙医不能再提水桶了

这是一个真实生活中的例子。我的前任牙医是我所见过的最好的牙医。她是个典型的专业人士,性格很好,医术高明,几乎每次拜访她都毫无痛楚。另外,她喜爱她的工作。她自己安排时间,她的诊所每周只营业3天,以便她有四天的时间和家人在—一起。

在这份她所喜欢的工作中.她每周工作3天,而每年收人为10万美元。如果曾经有过的话,这就是提桶者梦想中的工作。

有一个问题。40岁之前,她的手患了关节炎而无法再工作。现在她在当地一所大学教书,收入是做牙医时的l3。虽然她没有做错任何事,但她理想的工作消失了。

现在你是否明白,为什么我说从来没有一份有保障的提桶工作吧?你是否看到提桶的工作是多么脆弱?

   时间换钱陷阱的问题在于,如果你不交换时间,你就赚不到钱。

管道人柏波罗很早就意识到提桶的局限。于是他决心建立一个系统,无论是否投人更多的时间,他都能不断得到报酬。

他明白提桶是毫无保障可言的,他明白管道是他的生命线。

如果明天你不能付出时间,你会怎么样?

你呢?如果明天你的收入中断了,你怎么样?

如果你被解雇了,会有什么发生?

如果你病了或身体不适,无法继续提桶,会有什么发生?

如果某一次医疗意外耗尽了你所有的积蓄,怎么办?

如果你的公积金一夜之间无影无踪,怎么办?

如果明天你的收入中断了,你可继续支付房屋分期付款到何时呢?买车的分期付款,又如何呢?还有孩子上学的费用呢?

六个月?三个月?三个星期?!!!

如果灾难降临,你是否有生命线保护你和你的家人呢?抑或呢? 无论你是个清道夫、或者是个秘书、或者是个专业人士保障在哪里?
 

它称为管道——持续性收人——不管你有否继续付出时,你都可以从管道那里得到收入,当你玩乐时,管道在付钱给你,管道是一天二十四小时、一周七天、一年三百六十五天不停地为你工作。

管道是生命线。因为它们让人们摆脱时间换钱的陷阱

我们生活在几十年来最富裕的经济状况中,然而数百万的人仍从工资支票到工资支票地生活,工作时间越来越长,而仅仅为了收支平衡。为什么呢?因为他们在使用错误的计划,他们掉进了用时间换金钱的陷阱中。干一天的工作,拿一天的钱。听起来熟悉么?


    无论你是年收入仅一万元的洗碗工,还是十万元的医生,你都是用一个单位的时间换一个单位的钱。你仍然从一张工资支票活到另一张工质支票。至于“工作保障”————如果你因为被解雇、或疾病、或受伤、或受伤、或退休而不能工作,工资支票也会停止。

    无论你是年收入仅一万元的洗碗工,还是十万元的医生,你都是用一个单位的时间换一个单位的钱。你仍然从一张工资支票活到另一张工质支票。至于“工作保障”————如果你因为被解雇、或疾病、或受伤、或受伤、或退休而不能工作,工资支票也会停止。

    无论你是年收入仅一万元的洗碗工,还是十万元的医生,你都是用一个单位的时间换一个单位的钱。你仍然从一张工资支票活到另一张工质支票。至于“工作保障”————如果你因为被解雇、或疾病、或受伤、或受伤、或退休而不能工作,工资支票也会停止。


哪里还有什么保障呢?
你如何摆脱时间换金钱点的陷阱呢?通过建造来持续世袭收入的管道。有了世袭收入,你只要工作一次,就可以不断得到报酬。管道一天一天一年一年的连续泵,无论你是否在那里工作。
现在那就是保障———真正的财务保障
给生活修一条快乐,健康,美丽,自由的管道.

现在只需要做一个小的修改。将json_input_v2中最后一个"reference_path"作为用户在主输入中传入的"reference_path"值。 例如: [ { “reference_path”: “/root/onethingai-tmp/comfyui-tmp/哈吉斯第五批/男上衣/1合集” }, { “reference_path”: “/root/onethingai-tmp/comfyui-tmp/哈吉斯第五批/男上衣/2合集” }, { “reference_path”: “/root/onethingai-tmp/comfyui-tmp/哈吉斯第五批/男上衣/3合集” } ] 单独拿出最后一个"reference_path": "/root/onethingai-tmp/comfyui-tmp/哈吉斯第五批/男上衣/3合集"替换掉主输入的值。然后执行剩下的。 就仅执行: [ { “reference_path”: “/root/onethingai-tmp/comfyui-tmp/哈吉斯第五批/男上衣/1合集” }, { “reference_path”: “/root/onethingai-tmp/comfyui-tmp/哈吉斯第五批/男上衣/2合集” } ] 返回给我修正完成的完整代码,其他东西就不要优化了,已经很好用了。然后不要做任何省略,要注意之前的代码,不要做额外的修改。 import os import json import random import re import itertools from collections import defaultdict from typing import List, Tuple, Dict, Set class PatnMatchingNode: def __init__(self): self.all_notes = [] # 存储所有可用note路径 self.used_notes = set() self.specified_notes = defaultdict(list) # 存储指定参考衣服的网红note路径 @classmethod def INPUT_TYPES(cls): return { "required": { "reference_path": ("STRING", { "default": "", "tooltip": "参考衣服根目录路径(包含各类服装的文件夹)" }), "target_path": ("STRING", { "default": "", "tooltip": "网红照片根目录路径(包含各类网红照片的文件夹)" }), "seed": ("INT", { "default": 0, "min": 0, "max": 0xffffffffffffffff, "tooltip": "随机种子值(控制网红文件夹选择的随机性)" }), "batch_count": ("INT", { "default": 1, "min": 1, "max": 100, "tooltip": "批量处理次数(重复处理参考路径的次数)" }), "multiplier": ("INT", { "default": 1, "min": 1, "max": 10, "tooltip": "路径扩展倍数(最终输出的图像路径的复制倍数)" }), "X_add": ("INT", { "default": 0, "min": 0, "max": 100, "tooltip": "X值补偿(调整(括号内数字)_额外增加的组数)" }), "note_limit": ("INT", { "default": 3, "min": 0, "max": 100, "tooltip": "网红使用限制(每个网红最多使用的note数量)" }), "auto_add_suffix": ("BOOLEAN", { "default": False, "tooltip": "是否自动给note文件夹增加'已使用'后缀", "button": True }), "filter_used_notes": ("BOOLEAN", { "default": False, "tooltip": "是否过滤已使用note(包括后缀标记和JSON记录)", "button": True }), "reset_used_tags": ("BOOLEAN", { "default": False, "tooltip": "是否剔除所有已使用标签(移除后缀并重置记录,优先级最高)", "button": True }), }, "optional": { "used_json": ("STRING", { "default": "", "tooltip": "已使用记录(JSON格式保存的已使用网红路径)" }), "json_input_v2": ("STRING", { "default": "", "tooltip": "JSON格式输入,包含多个reference_path参数" }), } } RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING") RETURN_NAMES = ("参考衣服路径", "目标图像路径", "路径数量", "已使用_Json", "json_input_v2_output") FUNCTION = "match_paths" CATEGORY = "Sanmi Nodes/Customized Nodes" def normalize_path(self, path: str) -> str: """规范化路径,去除冗余部分""" return os.path.normpath(path) def has_reference_brackets(self, folder_name: str) -> bool: """检查参考衣服文件夹名是否包含括号""" return '(' in folder_name or '(' in folder_name def load_used_data(self, json_str: str) -> set: """从JSON字符串加载已使用的note路径""" if not json_str: return set() try: data = json.loads(json_str) return {self.normalize_path(p) for p in data.get("used_notes", [])} except: return set() def extract_ref_name(self, folder_name: str) -> str: """提取参考衣服名字""" # 剔除开头数字和下划线 without_prefix = re.sub(r'^\d+_', '', folder_name) # 提取括号前的内容 ref_name = re.split(r'[((]', without_prefix, 1)[0] return ref_name def extract_influencer_ref(self, influencer_name: str) -> Tuple[str, str]: """提取网红名和指定的参考衣服名""" parts = influencer_name.rsplit('_', 1) if len(parts) == 2: return parts[0], parts[1] return influencer_name, None def collect_notes(self, target_root: str, existing_used: set, filter_used_notes: bool): """收集未被使用的note路径,分离出指定参考衣服的网红""" self.all_notes = [] # 普通网红note路径 self.specified_notes = defaultdict(list) # 重置指定参考衣服的网红 # 遍历所有网红文件夹 for influencer in os.listdir(target_root): influencer_path = self.normalize_path(os.path.join(target_root, influencer)) if not os.path.isdir(influencer_path): continue # 提取网红名和可能的指定参考衣服名 original_name, ref_spec = self.extract_influencer_ref(influencer) # 遍历该网红下的note文件夹 for note in os.listdir(influencer_path): note_path = self.normalize_path(os.path.join(influencer_path, note)) if not os.path.isdir(note_path): continue # 检查是否已使用(根据后缀判断) is_used_by_suffix = note.endswith('已使用') # 如果启用了过滤已使用note,并且note已被使用(通过后缀或JSON记录),则跳过 if filter_used_notes and (is_used_by_suffix or note_path in existing_used): continue # 如果网红名包含指定参考衣服,则加入specified_notes if ref_spec: self.specified_notes[ref_spec].append(note_path) else: self.all_notes.append(note_path) def select_notes(self, candidate_list: List[str], x: int, note_limit: int, existing_counts: Dict[str, int], current_run_counts: Dict[str, int], target_path: str) -> Tuple[List[str], int]: """ 从候选列表中挑选note路径 candidate_list: 候选note路径列表 x: 需要选择的note数量 """ selected = [] missing = 0 remaining = x # 按网红分组并过滤可用note influencer_notes = defaultdict(list) for note_path in candidate_list: # 已使用的跳过 if note_path in self.used_notes: continue # 获取网红名称 rel_path = os.path.relpath(note_path, target_path) influencer = rel_path.split(os.sep)[0] # 检查使用限制 total_used = existing_counts.get(influencer, 0) + current_run_counts.get(influencer, 0) if total_used < note_limit: influencer_notes[influencer].append(note_path) # 多轮选择直到满足需求或无法继续 while remaining > 0: candidates = list(influencer_notes.keys()) if not candidates: missing = remaining break random.shuffle(candidates) added_this_round = 0 for influencer in candidates: if not influencer_notes[influencer]: continue # 选择该网红的一个note note_path = influencer_notes[influencer].pop(0) selected.append(note_path) self.used_notes.add(note_path) # 更新计数 current_run_counts[influencer] = current_run_counts.get(influencer, 0) + 1 remaining -= 1 added_this_round += 1 # 检查是否达到使用限制 total_used = existing_counts.get(influencer, 0) + current_run_counts[influencer] if total_used >= note_limit: del influencer_notes[influencer] if remaining <= 0: break if added_this_round == 0: missing = remaining break return selected, missing def process_images(self, note_path: str, auto_add_suffix: bool) -> List[str]: """处理单个note图像,如果需要则添加已使用后缀""" images = [] # 如果需要自动添加后缀,则重命名文件夹 if auto_add_suffix and not note_path.endswith('已使用'): parent_dir = os.path.dirname(note_path) base_name = os.path.basename(note_path) new_note_path = os.path.join(parent_dir, f"{base_name}_已使用") try: os.rename(note_path, new_note_path) note_path = new_note_path # 更新为新的路径 except OSError as e: print(f"重命名文件夹失败: {e}") # 收集图像文件 for fname in os.listdir(note_path): if fname.lower().endswith(('png', 'jpg', 'jpeg', 'webp')): images.append(self.normalize_path(os.path.join(note_path, fname))) return sorted(images) def reset_used_tags(self, target_path: str): """重置所有已使用标签,移除后缀并清空记录""" # 遍历所有网红文件夹 for influencer in os.listdir(target_path): influencer_path = self.normalize_path(os.path.join(target_path, influencer)) if not os.path.isdir(influencer_path): continue # 遍历该网红下的note文件夹 for note in os.listdir(influencer_path): note_path = self.normalize_path(os.path.join(influencer_path, note)) if not os.path.isdir(note_path): continue # 检查是否包含"已使用"后缀 if '已使用' in note: # 移除后缀 new_note_name = note.replace('_已使用', '') new_note_path = self.normalize_path(os.path.join(influencer_path, new_note_name)) try: os.rename(note_path, new_note_path) except OSError as e: print(f"重置文件夹名称失败: {e}") def simulate_execution(self, reference_path: str, target_path: str, seed: int, batch_count: int, multiplier: int, X_add: int, note_limit: int, used_json: str = "") -> str: """模拟执行,用于累积used_json""" random.seed(seed) # 加载历史使用数据 existing_used = self.load_used_data(used_json) self.used_notes = existing_used.copy() # 统计历史使用次数 existing_counts = defaultdict(int) for note_path in existing_used: rel_path = os.path.relpath(note_path, target_path) influencer = rel_path.split(os.sep)[0] existing_counts[influencer] += 1 # 收集可用note (分离普通网红和指定参考衣服的网红) # 模拟执行时filter_used_notes默认为True self.collect_notes(target_path, existing_used, True) # 准备候选列表(普通网红) available_notes = self.all_notes.copy() current_run_counts = defaultdict(int) # 开始批量处理 for _ in range(batch_count): clothing_folders = [ self.normalize_path(os.path.join(reference_path, f)) for f in os.listdir(reference_path) if os.path.isdir(self.normalize_path(os.path.join(reference_path, f))) ] for cloth_folder in clothing_folders: folder_name = os.path.basename(cloth_folder) # 关键修改:跳过没有括号的参考衣服 if not self.has_reference_brackets(folder_name): continue # 完全跳过不处理 ref_name = self.extract_ref_name(folder_name) x = max(0, self.extract_group_number(folder_name, X_add)) selected_notes = [] # 步骤1: 从指定参考衣服的网红中挑选 if ref_name in self.specified_notes: spec_candidates = self.specified_notes[ref_name].copy() selected, missing = self.select_notes( spec_candidates, x, note_limit, existing_counts, current_run_counts, target_path ) selected_notes.extend(selected) x -= len(selected) # 步骤2: 如果数量不足,从普通网红中挑选剩余数量 if x > 0: selected, missing = self.select_notes( available_notes, x, note_limit, existing_counts, current_run_counts, target_path ) selected_notes.extend(selected) # 处理目标图像(模拟执行时不实际处理图像,只记录使用的note) for note in selected_notes: self.process_images(note, False) # 模拟执行时auto_add_suffix为False # 生成使用记录 return json.dumps({"used_notes": list(self.used_notes)}, ensure_ascii=False, indent=2) def match_paths(self, reference_path: str, target_path: str, seed: int, batch_count: int, multiplier: int, X_add: int, note_limit: int, auto_add_suffix: bool, filter_used_notes: bool, reset_used_tags: bool, used_json: str = "", json_input_v2: str = "") -> Tuple[str, str, str, str, str]: """主函数:匹配参考衣服和网红照片路径""" # 处理json_input_v2模拟执行 all_reference_paths = [] cumulative_used_json = used_json if json_input_v2: try: input_data = json.loads(json_input_v2) for item in input_data: if "reference_path" in item: all_reference_paths.append(item["reference_path"]) # 模拟执行每个reference_path cumulative_used_json = self.simulate_execution( reference_path=item["reference_path"], target_path=target_path, seed=seed, batch_count=batch_count, multiplier=multiplier, X_add=X_add, note_limit=note_limit, used_json=cumulative_used_json ) except Exception as e: print(f"解析json_input_v2时出错: {e}") # 添加当前实际执行的reference_path all_reference_paths.append(reference_path) # 生成json_input_v2格式的输出 json_output = json.dumps([{"reference_path": path} for path in all_reference_paths], ensure_ascii=False, indent=2) # 实际执行 random.seed(seed) total_missing = 0 # 如果启用重置标签,先执行重置操作 if reset_used_tags: self.reset_used_tags(target_path) existing_used = set() else: # 加载历史使用数据(包括模拟执行累积的) existing_used = self.load_used_data(cumulative_used_json) self.used_notes = existing_used.copy() # 统计历史使用次数 existing_counts = defaultdict(int) for note_path in existing_used: rel_path = os.path.relpath(note_path, target_path) influencer = rel_path.split(os.sep)[0] existing_counts[influencer] += 1 # 收集可用note (分离普通网红和指定参考衣服的网红) self.collect_notes(target_path, existing_used, filter_used_notes) # 准备候选列表(普通网红) available_notes = self.all_notes.copy() current_run_counts = defaultdict(int) raw_clothes, raw_targets = [], [] # 开始批量处理 for _ in range(batch_count): clothing_folders = [ self.normalize_path(os.path.join(reference_path, f)) for f in os.listdir(reference_path) if os.path.isdir(self.normalize_path(os.path.join(reference_path, f))) ] for cloth_folder in clothing_folders: folder_name = os.path.basename(cloth_folder) # 关键修改:跳过没有括号的参考衣服 if not self.has_reference_brackets(folder_name): continue # 完全跳过不处理 ref_name = self.extract_ref_name(folder_name) x = max(0, self.extract_group_number(folder_name, X_add)) selected_notes = [] # 步骤1: 从指定参考衣服的网红中挑选 if ref_name in self.specified_notes: spec_candidates = self.specified_notes[ref_name].copy() selected, missing = self.select_notes( spec_candidates, x, note_limit, existing_counts, current_run_counts, target_path ) selected_notes.extend(selected) x -= len(selected) # 步骤2: 如果数量不足,从普通网红中挑选剩余数量 if x > 0: selected, missing = self.select_notes( available_notes, x, note_limit, existing_counts, current_run_counts, target_path ) selected_notes.extend(selected) total_missing += missing # 处理目标图像 target_images = list(itertools.chain.from_iterable( self.process_images(note, auto_add_suffix) for note in selected_notes )) # 生成参考路径 clothes = [] for img_path in target_images: fname = os.path.basename(img_path).lower() # 确定基础文件名 base_name = ( '左侧半' if 'lleft' in fname else # 新增左侧半 '右侧半' if 'rright' in fname else # 新增右侧半 '左侧' if 'left' in fname else '右侧' if 'right' in fname else '背面' if 'back' in fname else '正面' ) # 尝试两种图像格式 for ext in ['.jpg', '.png']: cloth = self.normalize_path(os.path.join(cloth_folder, base_name + ext)) if os.path.exists(cloth): clothes.append(cloth) break raw_clothes.extend(clothes) raw_targets.extend(target_images) # 应用倍数因子 clothes_paths = [p for p in raw_clothes for _ in range(multiplier)] target_paths = [p for p in raw_targets for _ in range(multiplier)] final_count = len(clothes_paths) # 构建路径数量信息 count_info = [] if total_missing > 0: count_info.append(f"网红note数量不足,无法满足所有的参考衣服,缺少了{total_missing}个note。") count_info.append(f"{final_count}") # 生成使用记录(包含模拟执行和实际执行的所有记录) new_used_json = json.dumps({"used_notes": list(self.used_notes)}, ensure_ascii=False, indent=2) return ( "\n".join(clothes_paths), "\n".join(target_paths), "\n".join(count_info), new_used_json, json_output ) def extract_group_number(self, folder_name: str, X_add: int) -> int: """提取文件夹编码差值""" # 尝试匹配括号内的数字 bracket_match = re.search(r'[((].*?(\d+).*?[))]', folder_name) if not bracket_match: return 0 # 没有括号内数字,返回0 # 只匹配"_"前的数值 underscore_match = re.search(r'^(\d+)_', folder_name) base_num = 0 if underscore_match: base_num = int(underscore_match.group(1)) # 提取括号内数字 bracket_num = int(bracket_match.group(1)) return bracket_num - base_num + X_add
08-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值