Files

143 lines
3.6 KiB
Python
Raw Permalink Normal View History

2021-07-04 17:22:39 -04:00
#!/usr/bin/env python3
"""SkinDetection.py
2021-07-04 17:22:39 -04:00
Runs several skin-detection algorithms on a single image and saves results.
"""
2021-07-04 17:22:39 -04:00
from __future__ import annotations
import argparse
2021-07-04 17:22:39 -04:00
import colorsys
import sys
from pathlib import Path
from typing import Callable
from PIL import Image
def load_image(path: Path) -> Image.Image:
image = Image.open(path)
return image.convert("RGB")
def save_result(image: Image.Image, path: Path) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
image.save(path)
def mask_image(
image: Image.Image,
predicate: Callable[[int, int, int], bool],
invert: bool = False,
) -> Image.Image:
result = image.copy()
pixels = result.load()
for x in range(result.width):
for y in range(result.height):
r, g, b = image.getpixel((x, y))
if predicate(r, g, b) ^ invert:
continue
pixels[x, y] = (0, 0, 0)
return result
def explicitly_defined_skin_region(r: int, g: int, b: int) -> bool:
return (
r > 95
and g > 40
and b > 20
and (max(r, g, b) - min(r, g, b)) > 15
and abs(r - g) > 15
and r > g
and r > b
)
def normalized_rg_skin_region(r: int, g: int, b: int) -> bool:
total = r + g + b
if total == 0:
return False
R = r / float(total)
G = g / float(total)
return 0.36 <= R <= 0.465 and 0.28 <= G <= 0.363
def hsv_skin_region(r: int, g: int, b: int) -> bool:
r_norm = r / 255.0
g_norm = g / 255.0
b_norm = b / 255.0
h, s, v = colorsys.rgb_to_hsv(r_norm, g_norm, b_norm)
h *= 255
return 0 <= h <= 50 and 0.2 <= s <= 0.68 and 0.35 <= v <= 1
def ycbcr_skin_region(r: int, g: int, b: int) -> bool:
y = 0.299 * r + 0.587 * g + 0.114 * b
cb = 128 - 0.168736 * r - 0.331264 * g + 0.5 * b
cr = 128 + 0.5 * r - 0.418688 * g - 0.081312 * b
return 97.5 <= cb <= 142.5 and 17 <= cr <= 134
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Run multiple skin detection algorithms on an input image."
)
parser.add_argument(
"image_path",
nargs="?",
default="Images/skin.jpg",
help="Path to the input image.",
)
parser.add_argument(
"--output-dir",
default="Results",
help="Directory to write output images.",
)
parser.add_argument(
"--show",
action="store_true",
help="Open each generated result image after processing.",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
image_path = Path(args.image_path)
output_dir = Path(args.output_dir)
show_results = args.show
if not image_path.exists():
print(f"Error: image path not found: {image_path}", file=sys.stderr)
return 1
try:
image = load_image(image_path)
except OSError as error:
print(f"Error opening image: {error}", file=sys.stderr)
return 1
algorithms = [
("Explicitly-Defined-Skin-Region.png", explicitly_defined_skin_region, False),
("Normalizationrg.png", normalized_rg_skin_region, False),
("HSV.png", hsv_skin_region, False),
("YCBCR.png", ycbcr_skin_region, True),
]
for filename, predicate, invert in algorithms:
output_path = output_dir / filename
result = mask_image(image, predicate, invert=invert)
save_result(result, output_path)
if show_results:
result.show(title=filename)
print(f"Saved {len(algorithms)} results to {output_dir}")
return 0
2021-07-04 17:22:39 -04:00
if __name__ == "__main__":
raise SystemExit(main())