Spaces:
Runtime error
Runtime error
| import io | |
| import os | |
| import cv2 | |
| import base64 | |
| from typing import Dict, Any, List, Union, Literal | |
| from pathlib import Path | |
| import datetime | |
| from enum import Enum | |
| import numpy as np | |
| import requests | |
| from PIL import Image | |
| PayloadOverrideType = Dict[str, Any] | |
| timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S") | |
| test_result_dir = Path(__file__).parent / "results" / f"test_result_{timestamp}" | |
| test_expectation_dir = Path(__file__).parent / "expectations" | |
| os.makedirs(test_expectation_dir, exist_ok=True) | |
| resource_dir = Path(__file__).parents[2] / "images" | |
| def read_image(img_path: Path) -> str: | |
| img = cv2.imread(str(img_path)) | |
| _, bytes = cv2.imencode(".png", img) | |
| encoded_image = base64.b64encode(bytes).decode("utf-8") | |
| return encoded_image | |
| def read_image_dir(img_dir: Path, suffixes=('.png', '.jpg', '.jpeg', '.webp')) -> List[str]: | |
| """Try read all images in given img_dir.""" | |
| img_dir = str(img_dir) | |
| images = [] | |
| for filename in os.listdir(img_dir): | |
| if filename.endswith(suffixes): | |
| img_path = os.path.join(img_dir, filename) | |
| try: | |
| images.append(read_image(img_path)) | |
| except IOError: | |
| print(f"Error opening {img_path}") | |
| return images | |
| girl_img = read_image(resource_dir / "1girl.png") | |
| mask_img = read_image(resource_dir / "mask.png") | |
| mask_small_img = read_image(resource_dir / "mask_small.png") | |
| portrait_imgs = read_image_dir(resource_dir / "portrait") | |
| realistic_girl_face_img = portrait_imgs[0] | |
| living_room_img = read_image(resource_dir / "living_room.webp") | |
| general_negative_prompt = """ | |
| (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, | |
| ((monochrome)), ((grayscale)), skin spots, acnes, skin blemishes, age spot, | |
| backlight,(ugly:1.331), (duplicate:1.331), (morbid:1.21), (mutilated:1.21), | |
| (tranny:1.331), mutated hands, (poorly drawn hands:1.331), blurry, (bad anatomy:1.21), | |
| (bad proportions:1.331), extra limbs, (missing arms:1.331), (extra legs:1.331), | |
| (fused fingers:1.61051), (too many fingers:1.61051), (unclear eyes:1.331), bad hands, | |
| missing fingers, extra digit, bad body, easynegative, nsfw""" | |
| class StableDiffusionVersion(Enum): | |
| """The version family of stable diffusion model.""" | |
| UNKNOWN = 0 | |
| SD1x = 1 | |
| SD2x = 2 | |
| SDXL = 3 | |
| sd_version = StableDiffusionVersion( | |
| int(os.environ.get("CONTROLNET_TEST_SD_VERSION", StableDiffusionVersion.SD1x.value)) | |
| ) | |
| is_full_coverage = os.environ.get("CONTROLNET_TEST_FULL_COVERAGE", None) is not None | |
| class APITestTemplate: | |
| is_set_expectation_run = os.environ.get("CONTROLNET_SET_EXP", "True") == "True" | |
| def __init__( | |
| self, | |
| name: str, | |
| gen_type: Union[Literal["img2img"], Literal["txt2img"]], | |
| payload_overrides: PayloadOverrideType, | |
| unit_overrides: Union[PayloadOverrideType, List[PayloadOverrideType]], | |
| ): | |
| self.name = name | |
| self.url = "http://localhost:7860/sdapi/v1/" + gen_type | |
| self.payload = { | |
| **(txt2img_payload if gen_type == "txt2img" else img2img_payload), | |
| **payload_overrides, | |
| } | |
| unit_overrides = ( | |
| unit_overrides | |
| if isinstance(unit_overrides, (list, tuple)) | |
| else [unit_overrides] | |
| ) | |
| self.payload["alwayson_scripts"]["ControlNet"]["args"] = [ | |
| { | |
| **default_unit, | |
| **unit_override, | |
| } | |
| for unit_override in unit_overrides | |
| ] | |
| def exec(self, result_only: bool = True) -> bool: | |
| if not APITestTemplate.is_set_expectation_run: | |
| os.makedirs(test_result_dir, exist_ok=True) | |
| failed = False | |
| response = requests.post(url=self.url, json=self.payload).json() | |
| if "images" not in response: | |
| print(response) | |
| return False | |
| dest_dir = ( | |
| test_expectation_dir | |
| if APITestTemplate.is_set_expectation_run | |
| else test_result_dir | |
| ) | |
| results = response["images"][:1] if result_only else response["images"] | |
| for i, base64image in enumerate(results): | |
| img_file_name = f"{self.name}_{i}.png" | |
| Image.open(io.BytesIO(base64.b64decode(base64image.split(",", 1)[0]))).save( | |
| dest_dir / img_file_name | |
| ) | |
| if not APITestTemplate.is_set_expectation_run: | |
| try: | |
| img1 = cv2.imread(os.path.join(test_expectation_dir, img_file_name)) | |
| img2 = cv2.imread(os.path.join(test_result_dir, img_file_name)) | |
| except Exception as e: | |
| print(f"Get exception reading imgs: {e}") | |
| failed = True | |
| continue | |
| if img1 is None: | |
| print(f"Warn: No expectation file found {img_file_name}.") | |
| continue | |
| if not expect_same_image( | |
| img1, | |
| img2, | |
| diff_img_path=str(test_result_dir | |
| / img_file_name.replace(".png", "_diff.png")), | |
| ): | |
| failed = True | |
| return not failed | |
| def expect_same_image(img1, img2, diff_img_path: str) -> bool: | |
| # Calculate the difference between the two images | |
| diff = cv2.absdiff(img1, img2) | |
| # Set a threshold to highlight the different pixels | |
| threshold = 30 | |
| diff_highlighted = np.where(diff > threshold, 255, 0).astype(np.uint8) | |
| # Assert that the two images are similar within a tolerance | |
| similar = np.allclose(img1, img2, rtol=0.5, atol=1) | |
| if not similar: | |
| # Save the diff_highlighted image to inspect the differences | |
| cv2.imwrite(diff_img_path, diff_highlighted) | |
| matching_pixels = np.isclose(img1, img2, rtol=0.5, atol=1) | |
| similar_in_general = (matching_pixels.sum() / matching_pixels.size) >= 0.95 | |
| return similar_in_general | |
| default_unit = { | |
| "control_mode": 0, | |
| "enabled": True, | |
| "guidance_end": 1, | |
| "guidance_start": 0, | |
| "low_vram": False, | |
| "pixel_perfect": True, | |
| "processor_res": 512, | |
| "resize_mode": 1, | |
| "threshold_a": 64, | |
| "threshold_b": 64, | |
| "weight": 1, | |
| } | |
| img2img_payload = { | |
| "batch_size": 1, | |
| "cfg_scale": 7, | |
| "height": 768, | |
| "width": 512, | |
| "n_iter": 1, | |
| "steps": 10, | |
| "sampler_name": "Euler a", | |
| "prompt": "(masterpiece: 1.3), (highres: 1.3), best quality,", | |
| "negative_prompt": "", | |
| "seed": 42, | |
| "seed_enable_extras": False, | |
| "seed_resize_from_h": 0, | |
| "seed_resize_from_w": 0, | |
| "subseed": -1, | |
| "subseed_strength": 0, | |
| "override_settings": {}, | |
| "override_settings_restore_afterwards": False, | |
| "do_not_save_grid": False, | |
| "do_not_save_samples": False, | |
| "s_churn": 0, | |
| "s_min_uncond": 0, | |
| "s_noise": 1, | |
| "s_tmax": None, | |
| "s_tmin": 0, | |
| "script_args": [], | |
| "script_name": None, | |
| "styles": [], | |
| "alwayson_scripts": {"ControlNet": {"args": [default_unit]}}, | |
| "denoising_strength": 0.75, | |
| "initial_noise_multiplier": 1, | |
| "inpaint_full_res": 0, | |
| "inpaint_full_res_padding": 32, | |
| "inpainting_fill": 1, | |
| "inpainting_mask_invert": 0, | |
| "mask_blur_x": 4, | |
| "mask_blur_y": 4, | |
| "mask_blur": 4, | |
| "resize_mode": 0, | |
| } | |
| txt2img_payload = { | |
| "alwayson_scripts": {"ControlNet": {"args": [default_unit]}}, | |
| "batch_size": 1, | |
| "cfg_scale": 7, | |
| "comments": {}, | |
| "disable_extra_networks": False, | |
| "do_not_save_grid": False, | |
| "do_not_save_samples": False, | |
| "enable_hr": False, | |
| "height": 768, | |
| "hr_negative_prompt": "", | |
| "hr_prompt": "", | |
| "hr_resize_x": 0, | |
| "hr_resize_y": 0, | |
| "hr_scale": 2, | |
| "hr_second_pass_steps": 0, | |
| "hr_upscaler": "Latent", | |
| "n_iter": 1, | |
| "negative_prompt": "", | |
| "override_settings": {}, | |
| "override_settings_restore_afterwards": True, | |
| "prompt": "(masterpiece: 1.3), (highres: 1.3), best quality,", | |
| "restore_faces": False, | |
| "s_churn": 0.0, | |
| "s_min_uncond": 0, | |
| "s_noise": 1.0, | |
| "s_tmax": None, | |
| "s_tmin": 0.0, | |
| "sampler_name": "Euler a", | |
| "script_args": [], | |
| "script_name": None, | |
| "seed": 42, | |
| "seed_enable_extras": True, | |
| "seed_resize_from_h": -1, | |
| "seed_resize_from_w": -1, | |
| "steps": 10, | |
| "styles": [], | |
| "subseed": -1, | |
| "subseed_strength": 0, | |
| "tiling": False, | |
| "width": 512, | |
| } | |