Greatly cleaned up the python script and added a dockerfile
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
FROM python:3.11-slim
|
||||||
|
WORKDIR /app
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
COPY . .
|
||||||
|
ENTRYPOINT ["python", "SkinDetection.py"]
|
||||||
+116
-88
@@ -1,114 +1,142 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
#SkinDetection.py
|
"""SkinDetection.py
|
||||||
|
|
||||||
# This program runs all 4 skin detection algorithms one after the other
|
Runs several skin-detection algorithms on a single image and saves results.
|
||||||
# Run `python3 SkinDetection.py Path/To/Image` to specify
|
"""
|
||||||
# inital image, removing this command arguements will result in
|
|
||||||
# the script attempting hard coded locations so errors may arise
|
|
||||||
# The
|
|
||||||
|
|
||||||
from PIL import Image
|
from __future__ import annotations
|
||||||
import math
|
|
||||||
|
import argparse
|
||||||
import colorsys
|
import colorsys
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
#Explicitly Defined Skin Region Model
|
from PIL import Image
|
||||||
def EDSRModel(image_path):
|
|
||||||
im = Image.open(image_path)
|
|
||||||
pixels = im.load()
|
|
||||||
rgb_im = im.convert('RGB')
|
|
||||||
for i in range(im.size[0]):
|
|
||||||
for j in range(im.size[1]):
|
|
||||||
r,g,b = rgb_im.getpixel((i,j))
|
|
||||||
if 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:
|
|
||||||
x = j
|
|
||||||
y = i
|
|
||||||
else:
|
|
||||||
pixels[i,j] = (0,0,0)
|
|
||||||
|
|
||||||
im.save("Results/Explicitly-Defined-Skin-Region.png")
|
|
||||||
|
|
||||||
#Colour Segmentation in Normalization rg Color Model
|
def load_image(path: Path) -> Image.Image:
|
||||||
def NormalizationrgModel(image_path):
|
image = Image.open(path)
|
||||||
im = Image.open(image_path)
|
return image.convert("RGB")
|
||||||
pixels = im.load()
|
|
||||||
rgb_im = im.convert('RGB')
|
|
||||||
for i in range(im.size[0]):
|
|
||||||
for j in range(im.size[1]):
|
|
||||||
r,g,b = rgb_im.getpixel((i,j))
|
|
||||||
|
|
||||||
R = 0.0
|
|
||||||
G = 0.0
|
|
||||||
B = 0.0
|
|
||||||
|
|
||||||
if r > 0 or g > 0 or b > 0:
|
def save_result(image: Image.Image, path: Path) -> None:
|
||||||
R = r/float(r+g+b)
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
G = g/float(r+g+b)
|
image.save(path)
|
||||||
B = b/float(r+g+b)
|
|
||||||
|
|
||||||
if 0.465 >= R >= 0.36 and 0.363 >= G >= 0.28:
|
|
||||||
x = j
|
|
||||||
y = i
|
|
||||||
else:
|
|
||||||
pixels[i,j] = (0,0,0)
|
|
||||||
|
|
||||||
im.save("Results/Normalizationrg.png")
|
def mask_image(
|
||||||
|
image: Image.Image,
|
||||||
|
predicate: Callable[[int, int, int], bool],
|
||||||
|
invert: bool = False,
|
||||||
|
) -> Image.Image:
|
||||||
|
result = image.copy()
|
||||||
|
pixels = result.load()
|
||||||
|
|
||||||
#Color Segmentation in HSV Color Model
|
for x in range(result.width):
|
||||||
def HSVModel(image_path):
|
for y in range(result.height):
|
||||||
im = Image.open(image_path)
|
r, g, b = image.getpixel((x, y))
|
||||||
pixels = im.load()
|
if predicate(r, g, b) ^ invert:
|
||||||
rgb_im = im.convert('RGB')
|
continue
|
||||||
for i in range(im.size[0]):
|
pixels[x, y] = (0, 0, 0)
|
||||||
for j in range(im.size[1]):
|
|
||||||
r,g,b = rgb_im.getpixel((i,j))
|
|
||||||
|
|
||||||
r = r/255.0
|
return result
|
||||||
g = g/255.0
|
|
||||||
b = b/255.0
|
|
||||||
|
|
||||||
h,s,v = colorsys.rgb_to_hsv(r,g,b)
|
|
||||||
|
|
||||||
h = h*255
|
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
|
||||||
|
)
|
||||||
|
|
||||||
if 50 >= h >= 0 and 0.68 >= s >= 0.2 and 1 >= v >= 0.35:
|
|
||||||
x = j
|
|
||||||
y = i
|
|
||||||
else:
|
|
||||||
pixels[i,j] = (0,0,0)
|
|
||||||
|
|
||||||
im.save("Results/HSV.png")
|
def normalized_rg_skin_region(r: int, g: int, b: int) -> bool:
|
||||||
|
total = r + g + b
|
||||||
|
if total == 0:
|
||||||
|
return False
|
||||||
|
|
||||||
#Color Segmentation in YCBCR Color Model
|
R = r / float(total)
|
||||||
def YCBCRModel(image_path):
|
G = g / float(total)
|
||||||
im = Image.open(image_path)
|
return 0.36 <= R <= 0.465 and 0.28 <= G <= 0.363
|
||||||
pixels = im.load()
|
|
||||||
ycbcr_im = im.convert('YCbCr')
|
|
||||||
for i in range(im.size[0]):
|
|
||||||
for j in range(im.size[1]):
|
|
||||||
y,Cb,Cr = ycbcr_im.getpixel((i,j))
|
|
||||||
|
|
||||||
if 142.5 >= Cb >= 97.5 and 134 >= Cr >= 17:
|
|
||||||
pixels[i,j] = (0,0,0)
|
|
||||||
else:
|
|
||||||
x = i
|
|
||||||
y = j
|
|
||||||
|
|
||||||
im.save("Results/YCBCR.png")
|
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 main():
|
|
||||||
args = sys.argv[1:]
|
|
||||||
|
|
||||||
if len(sys.argv) == 1:
|
def ycbcr_skin_region(r: int, g: int, b: int) -> bool:
|
||||||
image_path = "Images/skin.jpg"
|
y = 0.299 * r + 0.587 * g + 0.114 * b
|
||||||
else:
|
cb = 128 - 0.168736 * r - 0.331264 * g + 0.5 * b
|
||||||
image_path = args[0]
|
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
|
||||||
|
|
||||||
EDSRModel(image_path)
|
|
||||||
NormalizationrgModel(image_path)
|
|
||||||
HSVModel(image_path)
|
|
||||||
YCBCRModel(image_path)
|
|
||||||
return
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
raise SystemExit(main())
|
||||||
|
|||||||
Reference in New Issue
Block a user