Spaces:
Running
Running
| # copies or substantial portions of the Software. | |
| # | |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| # SOFTWARE. | |
| # | |
| ####################################################################################### | |
| # | |
| # This project is one of several repositories exploring image segmentation techniques. | |
| # All related projects and interactive demos can be found at: | |
| # https://huggingface.co/spaces/leonelhs/removators | |
| # Self app: https://huggingface.co/spaces/leonelhs/rembg | |
| # | |
| # Source code is based on or inspired by several projects. | |
| # For more details and proper attribution, please refer to the following resources: | |
| # | |
| # - [face-makeup.PyTorch] - [https://github.com/zllrunning/face-makeup.PyTorch] | |
| # - [BiSeNet] [https://github.com/CoinCheung/BiSeNet] | |
| import gradio as gr | |
| import cv2 | |
| import torch | |
| import numpy as np | |
| from PIL import Image | |
| from huggingface_hub import hf_hub_download | |
| import torchvision.transforms as transforms | |
| from bisnet import BiSeNet | |
| REPO_ID = "leonelhs/faceparser" | |
| MODEL_NAME = "79999_iter.pth" | |
| model = BiSeNet(n_classes=19) | |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| model_path = hf_hub_download(repo_id=REPO_ID, filename=MODEL_NAME) | |
| model.load_state_dict(torch.load(model_path, map_location=device)) | |
| model.eval() | |
| part_colors = [ | |
| {"part": "background", "color": [255, 0, 0]}, | |
| {"part": "face", "color": [219, 79, 66]}, | |
| {"part": "right_brow", "color": [255, 170, 0]}, | |
| {"part": "left_brow", "color": [255, 0, 85]}, | |
| {"part": "right_eye", "color": [255, 0, 170]}, | |
| {"part": "left_eye", "color": [ 0, 255, 0]}, | |
| {"part": "glasses", "color": [ 85, 255, 0]}, | |
| {"part": "right_ear", "color": [170, 255, 0]}, | |
| {"part": "left_ear", "color": [ 0, 255, 85]}, | |
| {"part": "earrings", "color": [ 0, 255, 170]}, | |
| {"part": "nose", "color": [ 0, 0, 255]}, | |
| {"part": "teeth", "color": [ 85, 0, 255]}, | |
| {"part": "upper_lip", "color": [170, 0, 255]}, | |
| {"part": "lower_lip", "color": [ 0, 85, 255]}, | |
| {"part": "neck", "color": [ 0, 170, 255]}, | |
| {"part": "collar", "color": [255, 255, 0]}, | |
| {"part": "cloths", "color": [255, 255, 85]}, | |
| {"part": "hair", "color": [199, 21, 133]}, | |
| {"part": "crown", "color": [255, 0, 255]}, | |
| {"part": "extra20", "color": [255, 85, 255]}, | |
| {"part": "extra21", "color": [255, 170, 255]}, | |
| {"part": "extra22", "color": [ 0, 255, 255]}, | |
| {"part": "extra23", "color": [ 85, 255, 255]}, | |
| {"part": "extra24", "color": [170, 255, 255]}, | |
| ] | |
| def image_to_tensor(image): | |
| return transforms.Compose([ | |
| transforms.ToTensor(), | |
| transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)), | |
| ])(image) | |
| def parse_face(mask): | |
| num_of_class = np.max(mask) | |
| face_parts = [] | |
| for index in range(1, num_of_class + 1): | |
| face_part = np.where(mask == index) | |
| canvas = np.full((512, 512, 3), 255, dtype=np.uint8) | |
| canvas[face_part[0], face_part[1], :] = part_colors[index]["color"] | |
| canvas = cv2.cvtColor(canvas, cv2.COLOR_BGR2GRAY) | |
| face_parts.append((canvas, part_colors[index]["part"])) | |
| return face_parts | |
| def predict(image): | |
| with torch.no_grad(): | |
| image = image.resize((512, 512), Image.Resampling.BILINEAR) | |
| input_tensor = image_to_tensor(image) | |
| input_tensor = torch.unsqueeze(input_tensor, 0) | |
| if torch.cuda.is_available(): | |
| input_tensor = input_tensor.cuda() | |
| mask = model(input_tensor)[0] | |
| mask = mask.squeeze(0).cpu().numpy().argmax(0) | |
| sections = parse_face(mask) | |
| return image, sections | |
| aboutme = r""" | |
| # PyTorch Image Face Parser | |
| Extracts facial features (hair, nose, eyes, etc.) from images using image segmentation. | |
| This project is part of a larger collection of repositories exploring image segmentation techniques. | |
| Related projects and interactive demos are available at: [Removators](https://huggingface.co/spaces/leonelhs/removators) | |
| ## Acknowledgments | |
| The source code is based on or inspired by the following projects: | |
| - [face-makeup.PyTorch](https://github.com/zllrunning/face-makeup.PyTorch) | |
| - [BiSeNet](https://github.com/CoinCheung/BiSeNet) | |
| ## Contact | |
| For questions, comments, or feedback, please contact: | |
| 📧 leonelhs@gmail.com | |
| """ | |
| with gr.Blocks(title="Face Parser") as app: | |
| navbar = gr.Navbar(visible=True, main_page_name="Workspace") | |
| gr.Markdown("## Face Parser Tool") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| inp = gr.Image(type="pil", label="Upload Image") | |
| btn_predict = gr.Button("Parse") | |
| with gr.Column(scale=2): | |
| out = gr.AnnotatedImage(label="Face parsed annotated") | |
| btn_predict.click(predict, inputs=[inp], outputs=[out]) | |
| with app.route("About this", "/about"): | |
| gr.Markdown(aboutme) | |
| app.launch(share=False, debug=True, show_error=True, mcp_server=True, pwa=True) | |
| app.queue() | |