ColorFM-L / app.py
heyh97791's picture
update app.py
1d338da
Raw
History Blame Contribute Delete
13.7 kB
import os
os.environ["HF_HUB_DISABLE_PROGRESS_BARS"] = "1"
import gradio as gr
import onnxruntime as ort
import importlib.util
from huggingface_hub import hf_hub_download
import numpy as np
import cv2
import psutil
import time
import sys
token_ = os.environ.get("HF_TOKEN")
model_repo_id = "heyh97791/NCF"
model_filename = "ncf.onnx"
file_path = "skin_protection.py"
pretrained_name = "skin.tflite"
protector_instance = None
I18N = {
"lang_btn": {
"en": "中文",
"zh": "English"
},
"title": {
"en": "# 🎨 ColorFM-L [CPU] ",
"zh": "# 🎨 ColorFM-L [CPU] "
},
"paper_code": {
"en": """
📄 **Paper:** [ColorFM: An Optimization-to-Learning Framework for Color Transfer via Flow Matching (ECCV 2026)](https://github.com/cszn/ColorFM) | 💻 **Code:** [GitHub](https://github.com/cszn/ColorFM) | ⚡ **Model:** Feed-forward model
""",
"zh": """
📄 **论文:** [ColorFM: An Optimization-to-Learning Framework for Color Transfer via Flow Matching (ECCV 2026)](https://github.com/cszn/ColorFM) | 💻 **代码:** [GitHub](https://github.com/cszn/ColorFM) | ⚡ **模型:** 前馈式模型
"""
},
"desc_header": {
"en": "💡 What is Color Transfer?",
"zh": "💡 追色是什么"
},
"desc_content": {
"en": """
**1. What is "Color Transfer"?**
It is a technique that extracts the **color palette and atmosphere** from a reference image (Style) and applies it to your target image (Content), while preserving the original structure.
**2. Use Cases:**
* 📸 **Photography:** Instantly mimic color grading styles from master photographers.
* 🎨 **Art & Design:** Unify the color theme of different assets quickly.
**3. Tips for Best Results:**
* ✅ **Match Content:** Results are best when the content and style images share similar scenes (e.g., Landscape to Landscape).
* 🎨 **Hue Similarity:** Images with closer hues between content and style may produce better color transfer results.
* ⚡ **High Resolution:** Supports processing high-resolution images (use the Settings menu to optimize speed).
**🛡️ Privacy Disclaimer:**
This demo runs entirely on the cloud instance. Your uploaded images are processed in memory and are **NOT saved** or stored permanently on our servers.
""",
"zh": """
**1. 什么是“追色”?**
追色(Color Transfer)是指从一张参考图(风格图)中提取**色调与氛围**,并将其“迁移”到你的目标图片(内容图)上,同时保留原图的细节纹理。
**2. 主要用途:**
* 📸 **摄影后期:** 一键复刻摄影大师的调色风格,无需手动调参。
* 🎨 **设计创作:** 快速统一多张素材的色调风格,提高创作效率。
**3. 如何获得最佳效果?**
* ✅ **内容匹配:** 当内容图与风格图的场景相似时(例如都是风景),效果通常最好。
* 🎨 **色相相似:** 内容图像和风格图像之间色相相似,可能会呈现更好的效果
* ⚡ **高清支持:** 支持高分辨率图片处理(可在“设置”中调整分辨率以获得更快速度)。
**🛡️ 免责声明:**
本Demo仅供演示体验。您的图片仅在内存中进行临时处理,**不会被保存**、存储或用于任何其他用途,处理结束后即刻销毁。
"""
},
"label_content": {
"en": "Content Image ",
"zh": "内容图"
},
"label_style": {
"en": "Style Reference",
"zh": "色彩参考图"
},
"settings_header": {
"en": "⚙️ Settings",
"zh": "⚙️ 设置"
},
"res_label": {
"en": "Max Resolution Limit",
"zh": "最大分辨率限制"
},
"res_info": {
"en": "Limits the input image size to speed up inference. 'Original' keeps original size.",
"zh": "限制输入图像尺寸以加快推理速度。选择“Original”将保持原图尺寸。"
},
"prot_label": {
"en": "🧪 Enable Skin Protection (Experimental)",
"zh": "🧪 肤色保护 (实验性功能)"
},
"btn_run": {
"en": "🚀 Start Color Transfer",
"zh": "🚀 开始追色"
},
"label_result": {
"en": "Result Image",
"zh": "追色结果"
},
"example_label": {
"en": "⚡ Quick Examples (Click to Load)",
"zh": "⚡ 追色样例 (点击加载示例)"
}
}
try:
model_path = hf_hub_download(
repo_id=model_repo_id,
filename=model_filename,
token=token_
)
except Exception as e:
print(f"{e}")
sess_options = ort.SessionOptions()
sess_options.enable_cpu_mem_arena = False
sess_options.intra_op_num_threads = 1
sess_options.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL
providers = ['CUDAExecutionProvider', 'CPUExecutionProvider']
try:
ort_session = ort.InferenceSession(model_path, providers=providers, sess_options=sess_options)
input_names = [node.name for node in ort_session.get_inputs()]
except Exception as e:
print(f"{e}")
ort_session = None
try:
script_path = hf_hub_download(
repo_id=model_repo_id,
filename=file_path,
token=token_
)
skin_model_path = hf_hub_download(
repo_id=model_repo_id,
filename=pretrained_name,
token=token_
)
spec = importlib.util.spec_from_file_location("skin_protection", script_path)
secret_module = importlib.util.module_from_spec(spec)
sys.modules["skin_protection"] = secret_module
spec.loader.exec_module(secret_module)
protector_instance = secret_module.SkinProtector(skin_model_path)
print("✅ Skin protection module loaded successfully.")
except Exception as e:
print(f"⚠️ Skin protection not loaded (Running in standard mode): {e}")
protector_instance = None
def get_memory_mb():
pid = os.getpid()
return psutil.Process(pid).memory_info().rss / 1024 / 1024
RESOLUTION_MAP = {
"Original": 0,
"4K": 3840 * 2160, # ~8.3MP
"2K": 2560 * 1440, # ~3.7MP
"1080p": 1920 * 1080, # ~2.1MP
"720p": 1280 * 720 # ~0.9MP
}
def preprocess_image(img_rgb, resolution_choice="Original"):
if img_rgb is None:
return None
max_pixel_limit = RESOLUTION_MAP.get(str(resolution_choice).split(" ")[0], 0)
if max_pixel_limit > 0:
h, w = img_rgb.shape[:2]
num_pixels = h * w
if num_pixels > max_pixel_limit:
scale_factor = np.sqrt(max_pixel_limit / num_pixels)
new_w = int(w * scale_factor)
new_h = int(h * scale_factor)
img_rgb = cv2.resize(img_rgb, (new_w, new_h), interpolation=cv2.INTER_AREA)
# print(f"Resized image from {w}x{h} to {new_w}x{new_h}")
img = img_rgb.astype(np.float32) / 255.0
img = img.transpose(2, 0, 1)
img = np.expand_dims(img, axis=0)
return img
def postprocess_tensor(tensor):
img = np.squeeze(tensor, axis=0)
img = np.clip(img, 0, 1)
img = img.transpose(1, 2, 0)
img = (img * 255.0).astype(np.uint8)
return img
def run_inference_numpy(content_np, style_np):
if ort_session is None: return content_np
c_in = content_np.astype(np.float32) / 255.0
c_in = c_in.transpose(2, 0, 1)[np.newaxis, :, :, :]
s_in = style_np.astype(np.float32) / 255.0
s_in = s_in.transpose(2, 0, 1)[np.newaxis, :, :, :]
input_feed = {input_names[0]: c_in, input_names[1]: s_in}
outputs = ort_session.run(None, input_feed)
out_tensor = outputs[0]
out_img = np.squeeze(out_tensor, axis=0)
out_img = np.clip(out_img, 0, 1)
out_img = out_img.transpose(1, 2, 0)
out_img = (out_img * 255.0).astype(np.uint8)
return out_img
def inference(content_img, style_img, resolution_selection, enable_skin_protection):
if ort_session is None:
raise gr.Error("model do not exists")
if content_img is None or style_img is None:
return None
# mem_start = get_memory_mb()
# t_start = time.time()
try:
content_input = preprocess_image(content_img, resolution_selection)
style_input = preprocess_image(style_img, resolution_selection)
input_feed = {
input_names[0]: content_input,
input_names[1]: style_input
}
outputs = ort_session.run(None, input_feed)
final_output_tensor = outputs[0]
result_img = postprocess_tensor(final_output_tensor)
if enable_skin_protection and protector_instance is not None:
# print("skin protection activated")
result_img = protector_instance.process(
content_img=content_img,
style_img=style_img,
global_result_img=result_img,
inference_callback=run_inference_numpy
)
return result_img
except Exception as e:
print(f"{e}")
raise gr.Error(f"{str(e)}")
def toggle_language(current_lang):
target_lang = "zh" if current_lang == "en" else "en"
return (
target_lang,
gr.update(value=I18N["lang_btn"][target_lang]), # Button
gr.update(value=I18N["title"][target_lang]), # Markdown
gr.update(label=I18N["desc_header"][target_lang]), # Accordion
gr.update(value=I18N["desc_content"][target_lang]), # Markdown
gr.update(label=I18N["label_content"][target_lang]), # Image Label
gr.update(label=I18N["label_style"][target_lang]), # Image Label
gr.update(value=I18N["btn_run"][target_lang]), # Button
gr.update(label=I18N["label_result"][target_lang]), # Image Label
gr.update(label=I18N["settings_header"][target_lang]), # Accordion Settings
gr.update(label=I18N["res_label"][target_lang], info=I18N["res_info"][target_lang]), # Radio Label
gr.update(label=I18N["prot_label"][target_lang]),
)
custom_css = """
#col-container {
margin: 0 auto;
max-width: 1100px;
}
.gallery-container img{
object-fit: contain;
}
"""
with gr.Blocks(css=custom_css, title="Color-Transfer") as demo:
lang_state = gr.State("en")
with gr.Column(elem_id="col-container"):
with gr.Row():
with gr.Column(scale=5):
md_title = gr.Markdown(I18N["title"]["en"])
with gr.Column(scale=0, min_width=80):
btn_lang = gr.Button(
value=I18N["lang_btn"]["en"],
variant="secondary",
size="sm"
)
md_paper_code = gr.Markdown(I18N["paper_code"]["en"])
with gr.Accordion(label=I18N["desc_header"]["en"], open=False) as acc_desc:
md_desc = gr.Markdown(I18N["desc_content"]["en"])
with gr.Row():
with gr.Column():
input_content = gr.Image(label=I18N["label_content"]["en"], type="numpy", height=300, interactive=True)
with gr.Column():
input_style = gr.Image(label=I18N["label_style"]["en"], type="numpy", height=300, interactive=True)
with gr.Accordion(label=I18N["settings_header"]["en"], open=False) as acc_settings:
res_choices = ["Original", "4K", "2K", "1080p", "720p"]
radio_res = gr.Radio(
choices=res_choices,
value="Original",
label=I18N["res_label"]["en"],
info=I18N["res_info"]["en"],
interactive=True
)
with gr.Row():
chk_skin = gr.Checkbox(
label=I18N["prot_label"]["en"],
value=False,
interactive=True
)
with gr.Row():
btn_run = gr.Button(I18N["btn_run"]["en"], variant="primary")
with gr.Row():
output_result = gr.Image(label=I18N["label_result"]["en"], type="numpy", interactive=False, height=450, format="png")
btn_run.click(
fn=lambda: None,
inputs=None,
outputs=output_result
).then(
fn=inference,
inputs=[input_content, input_style, radio_res, chk_skin],
outputs=output_result
)
gr.Examples(
label=I18N["example_label"]["en"],
examples=[
["./figs/01_c.jpg", "./figs/01_s.jpg", "./figs/01.jpg"],
["./figs/02_c.jpg", "./figs/02_s.jpg", "./figs/02.jpg"],
["./figs/03_c.jpg", "./figs/03_s.jpg", "./figs/03.jpg"],
["./figs/04_c.jpg", "./figs/04_s.jpg", "./figs/04.png"],
],
inputs=[input_content, input_style, output_result],
)
btn_lang.click(
fn=toggle_language,
inputs=[lang_state],
outputs=[
lang_state,
btn_lang,
md_title,
acc_desc,
md_desc,
input_content,
input_style,
btn_run,
output_result,
acc_settings,
radio_res,
chk_skin,
]
)
if __name__ == "__main__":
demo.launch()