import cv2 import torch import numpy as np from .AnyText2_utils import check_channels, resize_image, separate_pos_imgs, find_polygon, arr2tensor from .AnyText2_t3_dataset import draw_glyph, draw_glyph2, draw_font_hint from AnyText2ControlDiffusion.cldm.recognizer import crop_image from ....UL_common.common import tensor2pil import os from .AnyText_Infer import generate_rectangles mimic_list = font_hint_image = font_hint_mask =[] def encode( model, font, fonts, mask, prompt, texts, latent, mode, sort_radio, a_prompt, n_prompt, revise_pos, random_mask, Random_Gen_Mask_path, image, font_apply, show_glyph, **extra_kwargs ): max_chars = 50 batch_size, height, width = latent["samples"].shape[0], latent["samples"].shape[2] * 8, latent["samples"].shape[3] * 8 # B, C, H, W sort_radio = '↔' if sort_radio else '↕' img_count = batch_size h, w = height, width glyline_font_path, text_colors = fonts.glyline_font_path, fonts.text_colors n_lines = len(texts) if not random_mask: pos_image = np.array(tensor2pil(mask)) pos_image = 255 - resize_image(pos_image, max(height, width))#[..., :3] else: generate_rectangles(width, height, n_lines, 500, Random_Gen_Mask_path) pos_image = cv2.imread(Random_Gen_Mask_path)[..., ::-1] if random_mask: pos_image = pos_image[..., 0:1] draw_pos = pos_image h, w = draw_pos.shape[:2] if mode: mode = 'text-generation' revise_pos = revise_pos else: if image == None: raise ValueError('Edit mode need a image.') ori_image = np.array(tensor2pil(image)) mode = 'text-editing' revise_pos = False 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!", "" if isinstance(ori_image, str): 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): ori_image = ori_image.cpu().numpy() else: assert isinstance(ori_image, np.ndarray), f'Unknown format of ori_image: {type(ori_image)}' # edit_image = ori_image[..., ::-1] #bgr2rgb edit_image = ori_image.clip(1, 255) # for mask reason edit_image = check_channels(edit_image) edit_image = resize_image(edit_image, max_length=max(height, width)) # make w h multiple of 64, resize if w or h > max_length h, w = edit_image.shape[:2] # change h, w by input ref_img # preprocess pos_imgs(if numpy, make sure it's white pos in black bg) if draw_pos is None: pos_imgs = np.zeros((w, h, 1)) if isinstance(draw_pos, str): draw_pos = cv2.imread(draw_pos)[..., ::-1] assert draw_pos is not None, f"Can't read draw_pos image from{draw_pos}!" pos_imgs = 255-draw_pos elif isinstance(draw_pos, torch.Tensor): pos_imgs = draw_pos.cpu().numpy() else: # assert isinstance(draw_pos, np.ndarray), f'Unknown format of draw_pos: {type(draw_pos)}' pos_imgs = cv2.resize(draw_pos, (w, h)) if mode in ['text-editing', 'edit']: pos_imgs = cv2.resize(pos_imgs, (w, h)) pos_imgs = 255 - pos_imgs#[..., 0:1] pos_imgs = cv2.convertScaleAbs(pos_imgs) _, pos_imgs = cv2.threshold(pos_imgs, 254, 255, cv2.THRESH_BINARY) # seprate pos_imgs pos_imgs = separate_pos_imgs(pos_imgs, sort_radio) if len(pos_imgs) == 0: pos_imgs = [np.zeros((h, w, 1))] if len(pos_imgs) < n_lines: if n_lines == 1 and texts[0] == ' ': # pass # text-to-image without text print('\033[93m', f'Warning: text-to-image without text.', '\033[0m') else: raise ValueError(f'Found {len(pos_imgs)} positions that < needed {n_lines} from prompt, check and try again(手绘遮罩数少于要绘制的文本数,检查修改再重试)!') elif len(pos_imgs) > n_lines: print('\033[93m', f'Warning: found {len(pos_imgs)} positions that > needed {n_lines} from prompt.', '\033[0m') # get pre_pos, poly_list, hint that needed for anytext pre_pos = [] poly_list = [] for input_pos in pos_imgs: if input_pos.mean() != 0: input_pos = input_pos[..., np.newaxis] if len(input_pos.shape) == 2 else input_pos poly, pos_img = find_polygon(input_pos) pre_pos += [pos_img/255.] poly_list += [poly] else: pre_pos += [np.zeros((h, w, 1))] poly_list += [None] np_hint = np.sum(pre_pos, axis=0).clip(0, 1) # prepare info dict info = {} info['glyphs'] = [] info['gly_line'] = [] info['positions'] = [] info['n_lines'] = [len(texts)]*img_count font_hint = [] font_paths = ['None' for i in range(len(texts))] if glyline_font_path: glyline_font_path = glyline_font_path[:len(texts)] font_paths[:len(glyline_font_path)] = glyline_font_path info['colors'] = [np.array([500, 500, 500]) for i in range(len(texts))] if text_colors: text_colors = text_colors.strip().split()[:len(texts)] info['colors'][:len(text_colors)] = [np.array([int(p) for p in s.split(',')]) for s in text_colors] gly_pos_imgs = [] font_hint_mimic_imgs = [] gly_line_list = [] glyphs_list = [] for i in range(len(texts)): text = texts[i] if len(text) > max_chars: # str_warning = f'"{text}" length > max_chars: {max_chars}, will be cut off...' print(f'"{text}" length > max_chars: {max_chars}, will be cut off...') text = text[:max_chars] gly_scale = model.model.control_model.glyph_scale if pre_pos[i].mean() != 0: if font_apply: gly_line = draw_glyph(font, text, info['colors'][i], show_glyph) glyphs = draw_glyph2(font, text, poly_list[i], info['colors'][i], scale=gly_scale, width=w, height=h, add_space=True, show_glyph=show_glyph) else: gly_line = draw_glyph(font_paths[i], text, info['colors'][i], show_glyph) glyphs = draw_glyph2(font_paths[i], text, poly_list[i], info['colors'][i], scale=gly_scale, width=w, height=h, add_space=True, show_glyph=show_glyph) gly_line_list+=[gly_line] glyphs_list+=[glyphs] if i < len(font_hint_image) and font_hint_image[i] is not None: hint_poly = font_hint_mask[i] poly, _ = find_polygon(hint_poly) font_hint_mimic_img, _ = draw_font_hint((font_hint_image[i]/127.5-1), poly) font_hint_mimic_img = torch.from_numpy(font_hint_mimic_img*255).permute(2, 0, 1).repeat(3, 1, 1) font_hint_mimic_imgs += [crop_image(font_hint_mimic_img, hint_poly)] # chw, tensor, 0-255 font_paths[i] = 'None' # not render else: font_hint_mimic_imgs += [None] if font_apply: font_hint_line = draw_glyph2(font_paths[i], text, poly_list[i], np.array([255, 255, 255]), scale=1, width=w, height=h, add_space=True, show_glyph=show_glyph) else: font_hint_line = draw_glyph2(font_paths[i], text, poly_list[i], info['colors'][i], scale=1, width=w, height=h, add_space=True, show_glyph=show_glyph) gly_pos_img = cv2.drawContours(glyphs*255, [poly_list[i]*gly_scale], 0, (255, 255, 255), 1) if revise_pos: resize_gly = cv2.resize(glyphs, (pre_pos[i].shape[1], pre_pos[i].shape[0])) new_pos = cv2.morphologyEx((resize_gly*255).astype(np.uint8), cv2.MORPH_CLOSE, kernel=np.ones((resize_gly.shape[0]//10, resize_gly.shape[1]//10), dtype=np.uint8), iterations=1) new_pos = new_pos[..., np.newaxis] if len(new_pos.shape) == 2 else new_pos contours, _ = cv2.findContours(new_pos[..., 0:1], cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) if len(contours) != 1: str_warning = f'Fail to revise position {i} to bounding rect, remain position unchanged...' else: rect = cv2.minAreaRect(contours[0]) poly = np.int0(cv2.boxPoints(rect)) pre_pos[i] = cv2.drawContours(new_pos, [poly], -1, 255, -1) / 255. gly_pos_img = cv2.drawContours(glyphs*255, [poly*gly_scale], 0, (255, 255, 255), 1) gly_pos_imgs += [gly_pos_img] # for show else: glyphs = np.zeros((h*gly_scale, w*gly_scale, 3)) gly_line = np.zeros((80, 512, 1)) gly_pos_imgs += [np.zeros((h*gly_scale, w*gly_scale, 1))] # for show font_hint_line = np.zeros((h, w, 3)) pos = pre_pos[i][..., 0:1] info['glyphs'] += [arr2tensor(glyphs, img_count, model.load_device, model.dtype)] info['gly_line'] += [arr2tensor(gly_line, img_count, model.load_device, model.dtype)] info['positions'] += [arr2tensor(pos, img_count, model.load_device, model.dtype)] info['colors'][i] = arr2tensor(info['colors'][i], img_count, model.load_device, model.dtype)/255. font_hint += [font_hint_line] font_hint_mimic_imgs = [font_hint_mimic_imgs] * img_count model.model.embedding_manager.font_hint_mimic_imgs = font_hint_mimic_imgs # get masked_x masked_img = ((edit_image.astype(np.float32) / 127.5) - 1.0 - np_hint*10).clip(-1, 1) masked_img = np.transpose(masked_img, (2, 0, 1)) masked_img = torch.from_numpy(masked_img.copy()).float().cuda(0) # if self.use_fp16: # masked_img = masked_img.half() masked_img = masked_img.to(model.dtype) encoder_posterior = model.model.encode_first_stage(masked_img[None, ...]) masked_x = model.model.get_first_stage_encoding(encoder_posterior).detach() # if self.use_fp16: # masked_x = masked_x.half() masked_x = masked_x.to(model.dtype) info['masked_x'] = torch.cat([masked_x for _ in range(img_count)], dim=0) hint = arr2tensor(np_hint, img_count, model.load_device, model.dtype) font_hint_fg = np.sum(font_hint, axis=0).clip(0, 1)[..., 0:1]*255 if fonts.font_hollow and font_hint_fg.mean() > 0: img = cv2.imread(os.path.join(os.path.dirname(os.path.abspath(__file__)), "bg_noise.png")) img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img = cv2.resize(img, (font_hint_fg.shape[1], font_hint_fg.shape[0])) img[img < 230] = 0 font_hint_bg = cv2.adaptiveThreshold(img.astype(np.uint8), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) kernel1 = np.ones((2, 2), dtype=np.uint8) kernel2 = np.ones((3, 3), dtype=np.uint8) dilate_img1 = cv2.dilate(font_hint_fg[..., 0].astype(np.uint8), kernel1, iterations=1) dilate_img2 = cv2.dilate(font_hint_fg[..., 0].astype(np.uint8), kernel2, iterations=1) dilate_text = dilate_img2 - dilate_img1 result = (font_hint_fg[..., 0]-font_hint_bg + dilate_text).clip(0, 255) font_hint_bg[font_hint_fg[..., 0] > 0] = 0 result = (result + font_hint_bg).clip(0, 255) font_hint_bg = result[..., None] else: font_hint_bg = font_hint_fg text_prompt = "#" for i in texts: text_prompt+=" *" info['font_hint'] = arr2tensor((font_hint_bg/255), img_count, model.load_device, model.dtype) cond = model.model.get_learned_conditioning(dict(c_concat=[hint], c_crossattn=[[[prompt + ', ' + a_prompt] * img_count, [text_prompt.replace("# ", "")] * img_count]], text_info=info)) un_cond = model.model.get_learned_conditioning(dict(c_concat=[hint], c_crossattn=[[[n_prompt] * img_count, [""] * img_count]], text_info=info)) # font_img = [] # for f_i in gly_pos_imgs: # f_i = torch.from_numpy(f_i).permute(2,0,1) # font_img.append(f_i) # font_img = torch.cat(font_img, dim=0) # gly_line_img = [] # glyphs_img = [] # for g_la, g_lb in zip(gly_line_list, glyphs_list): # g_la, g_lb = torch.from_numpy(g_la).permute(2,0,1), torch.from_numpy(g_lb).permute(2,0,1) # gly_line_img.append(g_la) # glyphs_img.append(g_lb) # gly_line_img = torch.cat(gly_line_img, dim=0) # glyphs_img = torch.cat(glyphs_img, dim=0) # glyph_img = torch.from_numpy(np.sum(np.stack(gly_pos_imgs), axis=0).clip(0, 255).astype(np.uint8)).permute(2,0,1) # font_hint_img = torch.from_numpy(np.repeat(font_hint_bg.astype(np.uint8), 3, axis=2)).permute(2,0,1) glyph_img = font_hint_img = [] # return cond, un_cond, masked_x, font_img, gly_line_img, glyphs_img, masked_img.clone().cpu().float(), h, w, font_hint_img, glyph_img return cond, un_cond, h, w, font_hint_img, glyph_img