Spaces:
Sleeping
Sleeping
| from PIL import Image | |
| import piexif | |
| import json | |
| import html | |
| import re | |
| import gzip | |
| re_param_code = r'\s*([\w ]+):\s*("(?:\\.|[^\\"])+"|[^,]*)(?:,|$)' | |
| re_param = re.compile(re_param_code) | |
| re_imagesize = re.compile(r"^(\d+)x(\d+)$") | |
| IGNORED_INFO_KEYS = { | |
| 'jfif', 'jfif_version', 'jfif_unit', 'jfif_density', 'dpi', 'exif', | |
| 'loop', 'background', 'timestamp', 'duration', 'progressive', 'progression', | |
| 'icc_profile', 'chromaticity', 'photoshop', 'srgb', 'gamma', 'dpi' | |
| } | |
| def plaintext_to_html(text, classname=None): | |
| content = "<br>\n".join(html.escape(x) for x in text.split('\n')) | |
| return f"<p class='{classname}'>{content}</p>" if classname else f"<p>{content}</p>" | |
| def to_digit(v): | |
| try: | |
| return float(v) | |
| except: | |
| return v | |
| def read_stealth_info(image): | |
| width, height = image.size | |
| pixels = image.load() | |
| has_alpha = True if image.mode == 'RGBA' else False | |
| mode = None | |
| compressed = False | |
| binary_data = '' | |
| buffer_a = '' | |
| buffer_rgb = '' | |
| index_a = 0 | |
| index_rgb = 0 | |
| sig_confirmed = False | |
| confirming_signature = True | |
| reading_param_len = False | |
| reading_param = False | |
| read_end = False | |
| for x in range(width): | |
| for y in range(height): | |
| if has_alpha: | |
| r, g, b, a = pixels[x, y] | |
| buffer_a += str(a & 1) | |
| index_a += 1 | |
| else: | |
| r, g, b = pixels[x, y] | |
| buffer_rgb += str(r & 1) | |
| buffer_rgb += str(g & 1) | |
| buffer_rgb += str(b & 1) | |
| index_rgb += 3 | |
| if confirming_signature: | |
| if index_a == len('stealth_pnginfo') * 8: | |
| decoded_sig = bytearray(int(buffer_a[i:i + 8], 2) for i in | |
| range(0, len(buffer_a), 8)).decode('utf-8', errors='ignore') | |
| if decoded_sig in {'stealth_pnginfo', 'stealth_pngcomp'}: | |
| confirming_signature = False | |
| sig_confirmed = True | |
| reading_param_len = True | |
| mode = 'alpha' | |
| if decoded_sig == 'stealth_pngcomp': | |
| compressed = True | |
| buffer_a = '' | |
| index_a = 0 | |
| else: | |
| read_end = True | |
| break | |
| elif index_rgb == len('stealth_pnginfo') * 8: | |
| decoded_sig = bytearray(int(buffer_rgb[i:i + 8], 2) for i in | |
| range(0, len(buffer_rgb), 8)).decode('utf-8', errors='ignore') | |
| if decoded_sig in {'stealth_rgbinfo', 'stealth_rgbcomp'}: | |
| confirming_signature = False | |
| sig_confirmed = True | |
| reading_param_len = True | |
| mode = 'rgb' | |
| if decoded_sig == 'stealth_rgbcomp': | |
| compressed = True | |
| buffer_rgb = '' | |
| index_rgb = 0 | |
| elif reading_param_len: | |
| if mode == 'alpha': | |
| if index_a == 32: | |
| param_len = int(buffer_a, 2) | |
| reading_param_len = False | |
| reading_param = True | |
| buffer_a = '' | |
| index_a = 0 | |
| else: | |
| if index_rgb == 33: | |
| pop = buffer_rgb[-1] | |
| buffer_rgb = buffer_rgb[:-1] | |
| param_len = int(buffer_rgb, 2) | |
| reading_param_len = False | |
| reading_param = True | |
| buffer_rgb = pop | |
| index_rgb = 1 | |
| elif reading_param: | |
| if mode == 'alpha': | |
| if index_a == param_len: | |
| binary_data = buffer_a | |
| read_end = True | |
| break | |
| else: | |
| if index_rgb >= param_len: | |
| diff = param_len - index_rgb | |
| if diff < 0: | |
| buffer_rgb = buffer_rgb[:diff] | |
| binary_data = buffer_rgb | |
| read_end = True | |
| break | |
| else: | |
| # impossible | |
| read_end = True | |
| break | |
| if read_end: | |
| break | |
| if sig_confirmed and binary_data != '': | |
| # Convert binary string to UTF-8 encoded text | |
| byte_data = bytearray(int(binary_data[i:i + 8], 2) for i in range(0, len(binary_data), 8)) | |
| try: | |
| if compressed: | |
| decoded_data = gzip.decompress(bytes(byte_data)).decode('utf-8') | |
| else: | |
| decoded_data = byte_data.decode('utf-8', errors='ignore') | |
| geninfo = decoded_data | |
| return json.loads(geninfo) | |
| except: | |
| pass | |
| return {} | |
| def read_info_from_image(image): | |
| if image is None: | |
| return '', {} | |
| elif type(image) == str: | |
| image = Image.open(image) | |
| items = (image.info or {}).copy() | |
| info ='' | |
| if len(items) == 0: | |
| items = read_stealth_info(image) | |
| if "exif" in items: | |
| exif = piexif.load(items["exif"]) | |
| exif_comment = (exif or {}).get("Exif", {}).get(piexif.ExifIFD.UserComment, b'') | |
| try: | |
| exif_comment = piexif.helper.UserComment.load(exif_comment) | |
| except ValueError: | |
| exif_comment = exif_comment.decode('utf8', errors="ignore") | |
| if exif_comment: | |
| items['exif comment'] = exif_comment | |
| geninfo = exif_comment | |
| elif "comment" in items: # for gif | |
| if isinstance(items["comment"], bytes): | |
| info = items["comment"].decode('utf8', errors="ignore") | |
| else: | |
| info = items["comment"] | |
| for field in IGNORED_INFO_KEYS: | |
| items.pop(field, None) | |
| res = {} | |
| if items.get("Software", None) == "NovelAI": | |
| json_info = json.loads(items["Comment"]) | |
| geninfo = f"""{items["Description"]} | |
| Negative prompt: {json_info["uc"]} | |
| Steps: {json_info["steps"]}, Sampler: {json_info['sampler']}, CFG scale: {json_info["scale"]}, Seed: {json_info["seed"]}, Size: {image.width}x{image.height}, Clip skip: 2, ENSD: 31337""" | |
| items = {**{'parameters': geninfo}, **items} | |
| res = json.loads(items['Comment']) | |
| res['model'] = items['Source'] | |
| elif 'parameters' in items: | |
| res = {} | |
| prompt = "" | |
| negative_prompt = "" | |
| done_with_prompt = False | |
| *lines, lastline = items['parameters'].strip().split("\n") | |
| if len(re_param.findall(lastline)) < 3: | |
| lines.append(lastline) | |
| lastline = '' | |
| for line in lines: | |
| line = line.strip() | |
| if line.startswith("Negative prompt:"): | |
| done_with_prompt = True | |
| line = line[16:].strip() | |
| if done_with_prompt: | |
| negative_prompt += ("" if negative_prompt == "" else "\n") + line | |
| else: | |
| prompt += ("" if prompt == "" else "\n") + line | |
| res["prompt"] = prompt | |
| res["negative prompt"] = negative_prompt | |
| for k, v in re_param.findall(lastline): | |
| try: | |
| if v[0] == '"' and v[-1] == '"': | |
| v = to_digit(json.loads(v)) | |
| m = re_imagesize.match(v) | |
| if m is not None: | |
| res[f"{k.lower()}-1"] = to_digit(m.group(1)) | |
| res[f"{k.lower()}-2"] = to_digit(m.group(2)) | |
| else: | |
| res[k.lower()] = v | |
| except Exception: | |
| print(f"Error parsing \"{k}: {v}\"") | |
| return items['parameters'], res | |
| for key, text in items.items(): | |
| info += f""" | |
| <div> | |
| <p><b>{plaintext_to_html(str(key))}</b></p> | |
| <p>{plaintext_to_html(str(text))}</p> | |
| </div> | |
| """.strip()+"\n" | |
| if len(info) == 0: | |
| message = "Nothing found in the image." | |
| info = f"<div><p>{message}<p></div>" | |
| return info, res | |
| def send_paras(*args): | |
| if args[0] is None or len(args[0]) == 0: | |
| return args[1:] | |
| items, model, prompt, quality_tags, neg_prompt, seed, scale, width, height, steps, sampler, scheduler, smea, dyn, dyn_threshold, cfg_rescale, variety, num_chars, auto_pos, char_prompts, char_ucs, char_coords_x, char_coords_y, legacy, rerender = args | |
| paras = [prompt, quality_tags, neg_prompt, seed, scale, width, height, steps, sampler, scheduler, smea, dyn, dyn_threshold, cfg_rescale, variety] | |
| for i, keys in enumerate([('prompt',), ('',), ('uc', 'negative prompt'), ('seed',), ('scale', 'cfg scale'), ('width', 'size-1'), ('height', 'size-2'), ('steps',), ('sampler',), ('noise_schedule',), ('sm',), ('sm_dyn',), ('dynamic_thresholding',), ('cfg_rescale',), ('skip_cfg_above_sigma',)]): | |
| for key in keys: | |
| if key in items: | |
| paras[i] = items[key] | |
| prompt, quality_tags, neg_prompt, seed, scale, width, height, steps, sampler, scheduler, smea, dyn, dyn_threshold, cfg_rescale, variety = paras | |
| variety = (variety != None) if 'skip_cfg_above_sigma' in items else variety | |
| if 'model' in items: | |
| if items['model'].startswith('Stable Diffusion XL'): | |
| model = 'nai-diffusion-3' | |
| elif items['model'].startswith('NovelAI Diffusion V4'): | |
| if items['model'].endswith('7ABFFA2A'): | |
| model = 'nai-diffusion-4-curated-preview' | |
| elif items['model'].endswith('B5A2A797'): | |
| model = 'nai-diffusion-4-5-curated' | |
| elif items['model'].endswith('37442FCA'): | |
| model = 'nai-diffusion-4-full' | |
| else: | |
| model = 'nai-diffusion-4-5-full' | |
| legacy = items['v4_prompt']['legacy_uc'] if 'legacy_uc' in items['v4_prompt'] else legacy | |
| num_chars = len(items['v4_prompt']['caption']['char_captions']) | |
| auto_pos = not items['v4_prompt']['use_coords'] | |
| rerender = not rerender | |
| for i in range(num_chars): | |
| char_prompts[i] = items['v4_prompt']['caption']['char_captions'][i]['char_caption'] | |
| char_coords_x[i] = round((items['v4_prompt']['caption']['char_captions'][i]['centers'][0]['x'] + 0.1)*5) | |
| char_coords_y[i] = round((items['v4_prompt']['caption']['char_captions'][i]['centers'][0]['y'] + 0.1)*5) | |
| char_ucs[i] = items['v4_negative_prompt']['caption']['char_captions'][i]['char_caption'] | |
| if prompt.endswith(', ' + quality_tags): | |
| prompt = prompt[:-(len(quality_tags) + 2)] | |
| return model, prompt, quality_tags, neg_prompt, seed, scale, width, height, steps, sampler, scheduler, smea, dyn, dyn_threshold, cfg_rescale, variety, num_chars, auto_pos, char_prompts, char_ucs, char_coords_x, char_coords_y, legacy, rerender |