Spaces:
Running
Running
| import numpy as np | |
| import onnxruntime as rt | |
| import pandas as pd | |
| from PIL import Image | |
| import io | |
| import base64 | |
| import huggingface_hub | |
| MODEL_FILENAME = "model.onnx" | |
| LABEL_FILENAME = "selected_tags.csv" | |
| KAOMOJIS = { | |
| "0_0", "(o)_(o)", "+_+", "+_-", "._.", "<o>_<o>", "<|>_<|>", | |
| "=_=", ">_<", "3_3", "6_9", ">_o", "@_@", "^_^", "o_o", | |
| "u_u", "x_x", "|_|", "||_||", | |
| } | |
| def load_labels(dataframe): | |
| name_series = dataframe["name"].map( | |
| lambda x: x.replace("_", " ") if x not in KAOMOJIS else x | |
| ) | |
| tag_names = name_series.tolist() | |
| rating_indexes = list(np.where(dataframe["category"] == 9)[0]) | |
| general_indexes = list(np.where(dataframe["category"] == 0)[0]) | |
| character_indexes = list(np.where(dataframe["category"] == 4)[0]) | |
| return tag_names, rating_indexes, general_indexes, character_indexes | |
| def mcut_threshold(probs): | |
| sorted_probs = probs[probs.argsort()[::-1]] | |
| difs = sorted_probs[:-1] - sorted_probs[1:] | |
| t = difs.argmax() | |
| return (sorted_probs[t] + sorted_probs[t + 1]) / 2 | |
| class TaggerPredictor: | |
| def __init__(self): | |
| self.model_target_size = None | |
| self.last_loaded_repo = None | |
| self.model = None | |
| self.tag_names = [] | |
| self.rating_indexes = [] | |
| self.general_indexes = [] | |
| self.character_indexes = [] | |
| def download_model(self, model_repo): | |
| csv_path = huggingface_hub.hf_hub_download(model_repo, LABEL_FILENAME) | |
| model_path = huggingface_hub.hf_hub_download(model_repo, MODEL_FILENAME) | |
| return csv_path, model_path | |
| def load_model(self, model_repo): | |
| if model_repo == self.last_loaded_repo: | |
| return | |
| csv_path, model_path = self.download_model(model_repo) | |
| tags_df = pd.read_csv(csv_path) | |
| sep_tags = load_labels(tags_df) | |
| self.tag_names = sep_tags[0] | |
| self.rating_indexes = sep_tags[1] | |
| self.general_indexes = sep_tags[2] | |
| self.character_indexes = sep_tags[3] | |
| self.model = rt.InferenceSession(model_path) | |
| _, height, width, _ = self.model.get_inputs()[0].shape | |
| self.model_target_size = height | |
| self.last_loaded_repo = model_repo | |
| def prepare_image(self, image: Image.Image): | |
| target_size = self.model_target_size | |
| canvas = Image.new("RGBA", image.size, (255, 255, 255)) | |
| canvas.alpha_composite(image.convert("RGBA")) | |
| image = canvas.convert("RGB") | |
| max_dim = max(image.size) | |
| pad_left = (max_dim - image.size[0]) // 2 | |
| pad_top = (max_dim - image.size[1]) // 2 | |
| padded = Image.new("RGB", (max_dim, max_dim), (255, 255, 255)) | |
| padded.paste(image, (pad_left, pad_top)) | |
| if max_dim != target_size: | |
| padded = padded.resize((target_size, target_size), Image.BICUBIC) | |
| arr = np.asarray(padded, dtype=np.float32) | |
| arr = arr[:, :, ::-1] # RGB -> BGR | |
| return np.expand_dims(arr, axis=0) | |
| def predict(self, image_b64, model_repo, general_thresh, general_mcut, | |
| character_thresh, character_mcut): | |
| self.load_model(model_repo) | |
| img_bytes = base64.b64decode(image_b64) | |
| image = Image.open(io.BytesIO(img_bytes)) | |
| prepared = self.prepare_image(image) | |
| input_name = self.model.get_inputs()[0].name | |
| label_name = self.model.get_outputs()[0].name | |
| preds = self.model.run([label_name], {input_name: prepared})[0] | |
| labels = list(zip(self.tag_names, preds[0].astype(float))) | |
| # Ratings | |
| ratings = {labels[i][0]: float(labels[i][1]) for i in self.rating_indexes} | |
| # General tags | |
| general_names = [labels[i] for i in self.general_indexes] | |
| if general_mcut: | |
| probs = np.array([x[1] for x in general_names]) | |
| general_thresh = float(mcut_threshold(probs)) | |
| general_res = {n: float(s) for n, s in general_names if s > general_thresh} | |
| # Characters | |
| character_names = [labels[i] for i in self.character_indexes] | |
| if character_mcut: | |
| probs = np.array([x[1] for x in character_names]) | |
| character_thresh = max(0.15, float(mcut_threshold(probs))) | |
| character_res = {n: float(s) for n, s in character_names if s > character_thresh} | |
| # Sorted string | |
| sorted_tags = sorted(general_res.items(), key=lambda x: x[1], reverse=True) | |
| tags_string = ", ".join( | |
| x[0].replace("(", "\\(").replace(")", "\\)") | |
| for x in sorted_tags | |
| ) | |
| return { | |
| "tags": tags_string, | |
| "rating": ratings, | |
| "characters": character_res, | |
| "general": general_res, | |
| } | |
| # Singleton | |
| tagger_predictor = TaggerPredictor() |