| import streamlit as st |
| import numpy as np |
| import cv2 as cv |
| from scipy.special import softmax |
| import time |
| import io |
| from threading import Lock |
|
|
| |
| class HairSegmentation(object): |
| def __init__(self): |
| self.net = cv.dnn.readNet("hair_segmentation.tflite") |
|
|
| def _mix_prev_mask(self, prev_mask, new_mask): |
| combine_with_prev_ratio = 0.9 |
| eps = 1e-3 |
| uncertainty_alpha = 1.0 + (new_mask * np.log(new_mask + eps) + (1.0 - new_mask) * np.log(1.0 - new_mask + eps)) / np.log(2.0) |
| uncertainty_alpha = np.clip(uncertainty_alpha, 0, 1) |
| uncertainty_alpha *= 2.0 - uncertainty_alpha |
|
|
| mixed_mask = new_mask * uncertainty_alpha + prev_mask * (1.0 - uncertainty_alpha) |
| return mixed_mask * combine_with_prev_ratio + (1.0 - combine_with_prev_ratio) * new_mask |
|
|
| def process_image(self, frame, color, num_runs=2): |
| prev_mask = np.zeros((512, 512), dtype=np.float32) |
| color = np.ones(frame.shape, dtype=np.uint8) * color |
|
|
| |
| blob = cv.dnn.blobFromImage(frame, 1.0 / 255, (512, 512), swapRB=True) |
| blob = np.concatenate((blob, prev_mask.reshape(1, 1, 512, 512)), axis=1) |
|
|
| for i in range(num_runs): |
| |
| blob[0, 3] = prev_mask |
|
|
| |
| self.net.setInput(blob) |
| out = self.net.forward() |
|
|
| out = softmax(out, axis=1) |
| mask = out[0, 1] |
|
|
| prev_mask = self._mix_prev_mask(prev_mask, mask) |
|
|
| mask = cv.resize(prev_mask, (frame.shape[1], frame.shape[0])) |
| lum = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) / 255 |
| mask *= lum |
|
|
| mask = np.repeat(np.expand_dims(mask, axis=-1), 3, axis=-1) |
| result = (mask * (color.astype(np.float32) - frame) + frame).astype(np.uint8) |
|
|
| return result |
|
|
|
|
| |
| model = HairSegmentation() |
|
|
| |
| colors = {} |
| timestamps = {} |
|
|
| |
| st.title("Hair Color Segmentation") |
| st.write("Upload a photo, choose a color for the hair, and see the transformation!") |
|
|
| |
| uploaded_image = st.file_uploader("Upload your image", type=["jpg", "jpeg", "png"]) |
|
|
| |
| color = st.color_picker("Pick a hair color", "#FF0000") |
|
|
| |
| if color.startswith('#') and len(color) == 7: |
| |
| hex_color = color[1:] |
| color_bgr = [int(hex_color[i:i+2], 16) for i in (4, 2, 0)] |
| else: |
| st.error("Invalid color code! Please select a valid color.") |
|
|
| |
| if uploaded_image is not None: |
| |
| img = np.frombuffer(uploaded_image.read(), dtype=np.uint8) |
| img = cv.imdecode(img, cv.IMREAD_COLOR) |
|
|
| |
| stylized_image = model.process_image(img, color_bgr) |
|
|
| |
| st.image(cv.cvtColor(img, cv.COLOR_BGR2RGB), caption="Original Image", use_column_width=True) |
| st.image(cv.cvtColor(stylized_image, cv.COLOR_BGR2RGB), caption="Stylized Image with New Hair Color", use_column_width=True) |
|
|
| |
| save_result = st.button("Save Result") |
| if save_result: |
| |
| _, buffer = cv.imencode(".jpg", stylized_image) |
| st.download_button( |
| label="Download Stylized Image", |
| data=buffer.tobytes(), |
| file_name="stylized_image.jpg", |
| mime="image/jpeg" |
| ) |
|
|