AnyText2 在图片里玩文字而且还是所想即所得
AnyText2 在图片里玩文字而且还是所想即所得
flyfish
AnyText2 是一个超厉害的「图片文字编辑器」,能像玩「文字积木」一样随意控制图片里的文字,而且生成的文字超级逼真。
它能做什么?
-
在图片里生成指定文字,想写啥写啥
- 比如你想做一张奶茶店海报,用 AnyText2 输入“第二杯半价”,选一个手写体字体,再调个粉色渐变颜色,文字就能“长”到海报背景里,连阴影和光线都和背景融为一体,看起来就像真实拍的照片一样自然。
-
修改现有图片里的文字
- 比如拍了一张街景图,想把路边“整顿”的牌子改成“正常营业”,用 AnyText2 圈出文字区域,直接输入新内容,还能提取原图的字体和颜色,一键替换,完全看不出修改痕迹。
AnyText2是一种全新的方法,可在自然场景图像生成与编辑中实现对多语言文本属性的精准控制。我包含两个主要组件:
首先,WriteNet+AttnX架构,将文本渲染能力注入预训练的文本到图像(T2I)模型中。与前身AnyText相比,新方法不仅提升了图像真实感,还使推理速度提高了19.8%。
其次,从场景图像中提取字体和颜色的技术,并开发了文本嵌入模块(Text Embedding Module),将这些文本属性作为条件分别编码。作为AnyText的扩展,该方法允许为每行文本定制属性,使中文和英文的文本准确率分别提升了3.3%和9.3%。
技术架构与创新点
(1)WriteNet+AttnX架构:文本渲染能力的深度融合
- 核心思路:在预训练的T2I模型(如Stable Diffusion)中插入自定义模块,使其具备文本渲染能力。
- 技术突破:
- 图像真实感提升:通过优化文本与背景的融合算法(如阴影、透视、光照一致性),使生成的文本更贴近真实场景中的物理呈现;
- 推理速度优化:采用轻量化注意力机制(AttnX)和层间特征复用技术,在不损失画质的前提下,推理速度较AnyText提升19.8%,更适合实时编辑场景。
(2)文本嵌入模块:多属性解耦与条件编码
- 功能定位:从图像中提取文本的字体、颜色、大小、倾斜角度等属性,并将其编码为独立的条件向量,输入T2I模型以控制生成过程。
- 关键技术:
- 字体与颜色提取:基于卷积神经网络(CNN)的图像特征解析器,可从复杂场景中分离文本区域并识别字体类型(如宋体、Times New Roman)和颜色值;
- 属性解耦编码:通过独立的嵌入空间(Embedding Space)将字体、颜色等属性与文本内容分离,支持每行文本独立设置属性(如标题用粗体红色,正文用常规黑色);
- 多语言适配:针对中文字符的高复杂性,设计了基于字形特征的嵌入层,使中文文本准确率提升3.3%,英文提升9.3%(通过OCR验证)。
AnyText2 处理和修改提示词(prompt)
两处重要代码
代码1
def modify_prompt(self, prompt):# 1. 处理引号:将中文引号替换为英文引号prompt = prompt.replace('“', '"')prompt = prompt.replace('”', '"')# 2. 提取双引号内的文本内容p = '"(.*?)"' # 正则表达式:匹配双引号内的任意字符(非贪婪模式)strs = re.findall(p, prompt) # 获取所有匹配结果# 3. 处理无引号的情况:若没有引号内容,默认添加一个空格作为占位if len(strs) == 0:strs = [' ']else:# 4. 将引号内的原始文本替换为占位符PLACE_HOLDERfor s in strs:prompt = prompt.replace(f'"{s}"', f'{PLACE_HOLDER}', 1)# 5. 检测是否包含中文if self.is_chinese(prompt):# 若翻译模型未初始化,直接返回if self.trans_pipe is None:return None, Noneold_prompt = prompt# 6. 调用翻译模型将中文提示词转为英文# 注意:翻译前添加句号,翻译后移除,可能是为了适配翻译模型的输入格式prompt = self.trans_pipe(input=prompt + ' .')['translation'][:-1]# 7. 确保占位符周围有空格,保持格式一致性prompt = prompt.replace(f'{PLACE_HOLDER}', f' {PLACE_HOLDER} ')print(f'Translate: {old_prompt} --> {prompt}')# 8. 返回处理后的提示词和被替换的原始文本列表return prompt, strsdef is_chinese(self, text):# 清理文本:移除特殊字符、空格等(checker._clean_text是内部方法)text = checker._clean_text(text)# 遍历每个字符,检查是否有中文字符for char in text:cp = ord(char) # 获取字符的Unicode码点# _is_chinese_char是内部方法,判断是否为中文字符(如0x4E00-0x9FFF范围)if checker._is_chinese_char(cp):return Truereturn False
代码2
# img_prompt 是对图像整体的描述(如"木质桌面背景")
# text_prompt 是需要添加/编辑的文本内容(如'"新品:生椰拿铁",颜色用棕色')
img_prompt, _ = self.modify_prompt(img_prompt)
text_prompt, texts = self.modify_prompt(text_prompt)# 2. 错误检查:若翻译失败且无有效文本,返回错误信息
if (img_prompt is None or text_prompt is None) and texts is None:return None, -1, "You have input Chinese prompt but the translator is not loaded!", ""# 3. 统计需要处理的文本行数(引号内的文本片段数量)
n_lines = len(texts)# 4. 根据模式选择处理方式
if mode in ['text-generation', 'gen']:# 文本生成模式:创建空白图像作为基础edit_image = np.zeros((h, w, 3))elif mode in ['text-editing', 'edit']:# 文本编辑模式:需要参考图像和文本位置信息if draw_pos is None or ori_image is None:return None, -1, "Reference image and position image are needed for text editing!", ""# 5. 加载参考图像(ori_image):支持多种格式if isinstance(ori_image, str):# 从路径加载图像(BGR转RGB)ori_image = cv2.imread(ori_image)[..., ::-1]assert ori_image is not None, f"Can't read ori_image image from{ori_image}!"elif isinstance(ori_image, torch.Tensor):# 从PyTorch张量转换ori_image = ori_image.cpu().numpy()else:# 直接使用NumPy数组assert isinstance(ori_image, np.ndarray), f'Unknown format of ori_image: {type(ori_image)}'# 6. 预处理参考图像edit_image = ori_image.clip(1, 255) # 限制像素值范围,避免掩码处理问题edit_image = check_channels(edit_image) # 确保图像是RGB三通道edit_image = resize_image(edit_image, max_length=1024) # 调整图像大小(长边不超过1024,且尺寸为64的倍数)h, w = edit_image.shape[:2] # 更新图像尺寸,用于后续处理
Image Prompt(图像提示词) 和 Text Prompt(文字提示词) 的处理
AnyText2实现了“图像场景由翻译后的英文提示词驱动,文本内容由原始多语言文本控制”的协同机制,既保证了T2I模型的兼容性,又实现了多语言文本属性的精准定制。
一、核心处理函数:modify_prompt
的作用
1. 格式统一:中英文引号标准化
prompt = prompt.replace('“', '"').replace('”', '"') # 统一为英文双引号
- 作用:确保输入的提示词(无论是中文还是英文)中的引号格式一致,便于后续正则表达式匹配。
2. 提取双引号内文本:分离内容与属性
strs = re.findall('"(.*?)"', prompt) # 提取所有带引号的文本
for s in strs:prompt = prompt.replace(f'"{s}"', f'{PLACE_HOLDER}', 1) # 替换为占位符*
- 关键逻辑:
- Text Prompt专用:假设输入为
'"Hello","世界"'
,则strs = ["Hello", "世界"]
,prompt
变为"*", "*"
。 - Image Prompt可能包含描述性文本:若
Image Prompt
为"木质桌面,上面有写着"新品"的标牌"
,则提取["新品"]
,prompt
变为"木质桌面,上面有写着*的标牌"
。
- Text Prompt专用:假设输入为
3. 中文翻译:适配英文T2I模型
if self.is_chinese(prompt):prompt = self.trans_pipe(input=prompt + ' .')['translation'][:-1] # 翻译为英文prompt = prompt.replace(f'{PLACE_HOLDER}', f' {PLACE_HOLDER} ') # 确保占位符前后有空格
- 触发条件:当检测到中文(包括占位符外的字符)时,调用翻译模型(如
nlp_csanmt_translation_zh2en
)。 - 示例:
- 中文
Image Prompt
:"一只猫举着写有"你好"的牌子"
→ 翻译为英文"A cat holds a sign with * on it"
。 - 中文
Text Prompt
:'"你好","世界"'
→ 翻译为'"*", "*"'
(保留占位符,仅翻译外围描述)。
- 中文
二、双Prompt协同处理:img_prompt
vs text_prompt
1. 输入示例
假设用户输入:
- Image Prompt:
"咖啡馆场景,木质桌面,柔和灯光"
(中文) - Text Prompt:
'"Special Offer","买一送一"'
(中英文混合)
2. 处理流程
Step 1:分别调用modify_prompt
-
处理
img_prompt
(中文):- 无引号文本 →
strs = [' ']
(默认占位)。 - 检测到中文 → 翻译为英文:
"coffee shop scene, wooden table, soft lighting"
。 - 输出:
img_prompt = "coffee shop scene, wooden table, soft lighting"
,strs = [' ']
(无实际文本)。
- 无引号文本 →
-
处理
text_prompt
(含引号):- 提取
strs = ["Special Offer", "买一送一"]
。 - 替换为
"*", "*"
→prompt = "*", "*"
。 - 检测到中文(
"买一送一"
)→ 翻译外围描述(但此处prompt
中无外围中文,故不翻译)。 - 输出:
text_prompt = "*", "*"
,strs = ["Special Offer", "买一送一"]
。
- 提取
Step 2:生成/编辑图像的前置准备
-
模式判断:
- 若为
gen
(生成模式):创建空白图像edit_image = np.zeros((h, w, 3))
。 - 若为
edit
(编辑模式):加载参考图像并预处理(如调整尺寸、通道检查)。
- 若为
-
文本位置处理:
- 通过
draw_pos
(位置掩码)确定文本区域,例如用户提供的掩码图像会被分割为多个区域,对应texts
中的每行文本(n_lines = len(strs) = 2
)。
- 通过
Step 3:构建模型输入条件
-
核心参数:
info['glyphs']
:存储文本的字形信息(基于字体渲染)。info['positions']
:文本在图像中的位置掩码。info['colors']
:文本颜色(支持RGB值,如text_colors="255,0,0 0,255,0"
对应两行文本的颜色)。
-
翻译后的提示词拼接:
cond = self.model.get_learned_conditioning(c_crossattn=[[img_prompt + ', ' + a_prompt] * img_count, # 图像描述 + 质量提示[text_prompt] * img_count # 处理后的文本提示(含占位符)] )
- 最终传递给模型的文本提示为:
"*, *"
(英文),但实际生成时会结合strs
中的原始文本("Special Offer"
和"买一送一"
)的字形、位置和颜色信息。
- 最终传递给模型的文本提示为:
三、关键技术点:占位符的「分离-翻译-还原」机制
1. 为什么需要占位符?
- 避免翻译干扰:带引号的文本通常是需要保留的具体内容(如品牌名、标语),不应被翻译。
- 属性解耦:将文本内容(如“买一送一”)与图像描述(如“咖啡馆场景”)分离,便于后续单独控制文本的字体、颜色等属性。
2. 占位符的生命周期
# 输入阶段(用户输入)
text_prompt = '"中文文本", "English Text"'# modify_prompt处理后
text_prompt = "*", "*" # 占位符替换
texts = ["中文文本", "English Text"] # 保存原始内容# 模型生成阶段(内部处理)
# 用占位符生成英文提示词,确保T2I模型理解布局和属性
prompt_for_model = "write * in red font on a wooden table"# 输出阶段(结果拼接)
input_prompt = prompt_for_model.replace('*', f'"{text}"', 1) # 还原为原始文本
# 最终输出提示词:"write "中文文本" in red font on a wooden table"
四、多语言场景下的特殊处理
1. 纯中文场景
- 输入:
img_prompt
:"书店场景,书架上有写着"畅销书籍"的标签"
text_prompt
:'"小说专区"'
- 处理流程:
img_prompt
提取["畅销书籍"]
,翻译为"bookstore scene, bookshelf with * label"
。text_prompt
提取["小说专区"]
,替换为"*"
,因无外围中文不翻译。- 模型生成时,结合中文文本的字形(通过
draw_glyph
渲染)和翻译后的场景描述,生成包含中文文本的图像。
2. 中英文混合场景
- 输入:
img_prompt
:"A restaurant with a sign saying "今日特价""
(英文中夹杂中文)text_prompt
:'"Lunch Set", "¥50"'
- 处理流程:
img_prompt
检测到中文"今日特价"
,提取并翻译外围英文描述,最终为"A restaurant with a sign saying *"
。text_prompt
无中文,直接保留'"Lunch Set", "¥50"'
,替换为"*, *"
。- 模型生成时,
img_prompt
中的*
对应"今日特价"
(中文),text_prompt
中的*
对应英文和符号,分别渲染为对应语言的文本。
五、代码如何保证文本属性的精准控制?
1. 字体与颜色的独立编码
info['colors'] = [np.array([int(p) for p in s.split(',')]) for s in text_colors.split()]
font_paths = glyline_font_path.split() # 支持每行单独指定字体路径
- 机制:通过
text_colors
和glyline_font_path
参数,为每行文本(对应texts
中的元素)独立设置颜色和字体,与modify_prompt
提取的文本内容一一对应。
2. 位置与布局的精准匹配
pos_imgs = self.separate_pos_imgs(pos_imgs, sort_priority) # 按优先级排序文本区域
poly, pos_img = self.find_polygon(input_pos) # 获取文本区域的多边形轮廓
- 作用:根据用户提供的
draw_pos
(如掩码图像),将文本内容精准定位到图像的指定区域,确保生成的文本位置与提示词一致。
脉络
阶段 | Image Prompt 处理 | Text Prompt 处理 |
---|---|---|
格式清洗 | 统一引号,无引号则默认占位[' '] | 提取引号内文本,替换为* |
语言检测 | 若含中文则翻译为英文(保留* ) | 若含中文则翻译外围描述(引号内文本不翻译) |
模型输入 | 拼接英文场景描述(如"coffee shop, ..." ) | 传递含* 的提示词(如"write * in red" ) |
内容还原 | 无(纯描述性文本) | 生成前用原始文本(texts )替换* |
属性控制 | 通过a_prompt 调整图像质量(如"4k, HD" ) | 通过text_colors 、font_paths 控制文本样式 |