melanoma_classification / src /scripts /generate_skin_masks.py
Mhara's picture
Upload folder using huggingface_hub
dae5c90 verified
Raw
History Blame Contribute Delete
3.62 kB
import argparse
import cv2 as cv
import os
from tqdm import tqdm
import concurrent.futures
import multiprocessing
def generate_mask(image_path):
img = cv.imread(image_path)
# Isolate skin
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
lab = cv.cvtColor(img, cv.COLOR_BGR2LAB)
lab_planes = list(cv.split(lab))
lab_planes[0] = clahe.apply(lab_planes[0])
lab = cv.merge(lab_planes)
clahe_img = cv.cvtColor(lab, cv.COLOR_LAB2BGR)
## Dullrazor
grayscale = cv.cvtColor(clahe_img, cv.COLOR_BGR2GRAY) # [1] uses RGB2GRAY
kernel = cv.getStructuringElement(1, (9, 9)) # [1] uses a 3x3 kernel
blackhat = cv.morphologyEx(grayscale, cv.MORPH_BLACKHAT, kernel)
blurred = cv.GaussianBlur(blackhat, (3, 3), cv.BORDER_DEFAULT)
_, hair_mask = cv.threshold(
blurred, 20, 255, cv.THRESH_BINARY
) # [2] sets the threshold at 10, [1] at 25
masked_img = cv.bitwise_and(clahe_img, clahe_img, mask=255 - hair_mask)
## Threshold to remove pigmentations
hsv = cv.cvtColor(masked_img, cv.COLOR_BGR2HSV)
_, _, v = cv.split(hsv)
v = cv.GaussianBlur(v, (5, 5), 0)
_, v_thresh = cv.threshold(v, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)
thresh = v_thresh
kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (3, 3))
thresh = cv.morphologyEx(thresh, cv.MORPH_OPEN, kernel, iterations=5)
thresh = cv.dilate(thresh, kernel, iterations=5)
thresh = cv.morphologyEx(thresh, cv.MORPH_CLOSE, kernel, iterations=5)
thresh = cv.bitwise_or(thresh, hair_mask)
return thresh
def process_image(args):
image_path, mask_path = args
if os.path.exists(mask_path):
return False
try:
mask = generate_mask(image_path)
cv.imwrite(mask_path, mask)
return True
except Exception as e:
return False
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--source_path", help="dataset root path")
parser.add_argument("--num_workers", type=int, default=None,
help="Number of parallel workers (default: number of CPU cores)")
args = parser.parse_args()
dataset_root = args.source_path
num_workers = args.num_workers or max(1, multiprocessing.cpu_count() - 1)
print(f"Using {num_workers} workers")
os.makedirs(os.path.join(dataset_root, "masks"), exist_ok=True)
tasks = []
for folder in os.listdir(dataset_root):
if folder in ["train", "valid", "test"]:
os.makedirs(os.path.join(dataset_root, "masks", folder), exist_ok=True)
for subfolder in os.listdir(os.path.join(dataset_root, folder)):
os.makedirs(os.path.join(dataset_root, "masks", folder, subfolder), exist_ok=True)
for image_file in os.listdir(os.path.join(dataset_root, folder, subfolder)):
image_path = os.path.join(dataset_root, folder, subfolder, image_file)
mask_path = os.path.join(dataset_root, "masks", folder, subfolder, image_file.replace(".jpg", "_mask.jpg"))
tasks.append((image_path, mask_path))
processed_count = 0
with concurrent.futures.ProcessPoolExecutor(max_workers=num_workers) as executor:
results = list(tqdm(executor.map(process_image, tasks), total=len(tasks), desc="Generating masks"))
processed_count = sum(results)
print(f"Processed {processed_count} new images. {len(tasks) - processed_count} were already processed or failed.")