def read_and_find(in_paths, out_path): """ Reads an image from a path, and locates and encodes any face on it. :param path: path to an image :return: tuple of (cv2/np.ndarray, list of encodings, list of locations) or empty tuple if no face was found """ for path in in_paths: img = cv2.imread(path) if img is None: continue locations = detect(img) h, w = img.shape[:2] basepath, sep, name = path.rpartition("\\") name, dot, ext = name.rpartition(".") count = 0 print(f"Found {len(locations)} faces in {path}...") for loc in locations: l, t, r, b = loc fw, fh = r - l, b - t dw, dh = int(fw * 0.5), int(fh * 0.5) l, t, r, b = l - dw, t - dh, r + dw, b + dh x0, y0 = max(0, l), max(0, t) x1, y1 = min(w, r), min(h, b) sub_img = img[y0:y1, x0:x1] cv2.imwrite(f"{out_path}/{name}_{count}_{(x0, y0, x1, y1)}.{ext}", sub_img) return None
def read_and_find(path): """ Reads an image from a path, and locates and encodes any face on it. :param path: path to an image :return: tuple of (cv2/np.ndarray, list of encodings, list of locations) or empty tuple if no face was found """ print(f"Reading {path}...") img = cv2.imread(path) if img is None: return tuple() locations = detect(img) if not (locations): return tuple() if C_LOAD_ATLAS: # we don't have to do the encodings in this case return img, [], locations, path return img, encode(img, locations=locations), locations, path
def compose(img1, img2, mask): cv2.erode(mask, kern_erode, mask) mask = cv2.GaussianBlur(mask, (17, 17), 0) imask = -1. * (mask - 1.) ret = ((img1 * imask) + (img2 * mask)).astype(np.uint8) return ret ploc_count = 1000 while True: check, frame = video.read() if ploc_count > 4: ploc = detect(frame, upsample=0) ploc_count = 0 faces = landmark(frame, locations=ploc) ploc_count += 1 # frame = draw_faces(frame, faces) if faces: morphed = morph(frame, newface, faces[0], newface_marks[0]) faces_points = faces[0].points points = cv2.convexHull(np.int32(faces_points), returnPoints=False) mask = cv2.fillConvexPoly( np.zeros(frame.shape, dtype=np.float32), np.int32([faces_points[int(i)] for i in points]), (1.0, 1.0, 1.0)) #m2 = (mask * morphed).astype(np.uint8) #morphed = color_correct(frame, morphed, faces[0]) else: morphed = frame.copy()
"Santi": "c:/test/phantom/tests/santi.jpg", } for name, impath in known_faces.items(): img = cv2.imread(impath) if img is None: known_faces[name] = None continue known_faces[name] = encode(img) video = cv2.VideoCapture(0) while True: check, frame = video.read() faces = detect(frame, upsample=0) if faces: encodings = encode(frame, locations=faces) for i, e in enumerate(encodings): for k, v in known_faces.items(): if compare(e, v) <= 0.6: left, top, right, bottom = faces[i] x = int (left * 0.5 + right * 0.5) y = top cv2.putText(frame, k, (x,y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0)) cv2.imshow("Caras", frame) key = cv2.waitKey(1) if key == ord("q"): break video.release()
def tag(): """ Show images from a path waiting for user input to tag them. We're keeping only the images were dlibs HOG face detector picks up only one face. Using dlibs 5 point face landmark dataset a balanced female/male ratio is found at about ~600 images. We also skip images were landmarking goes wrong, as they can negatively affect the result of facial encoding. :return: list of TaggedFace for each image """ def age_table(age_counter, age, padding=2): lines = [[" "], ["m "], ["f "], [" "], [" "]] for idx, (low, high, name) in enumerate(age_tags[1:], start=1): lines[0].append(f"{low:{padding}}-{high:{padding}}") lines[1].append(f"{age_counter['m'][idx]:{padding * 2 + 1}}") lines[2].append(f"{age_counter['f'][idx]:{padding * 2 + 1}}") lines[3].append(f"{idx:^5}") lines[4].append(f"{'▲' if age == idx else ' ':^5}") lines[0].append("Total") lines[1].append(f"{sum(age_counter['m'][1:]): 5}") lines[2].append(f"{sum(age_counter['f'][1:]): 5}") lines[3].append( f"{sum(age_counter['m'][1:]) + sum(age_counter['f'][1:]): 5}") ret = [ "|".join(l) if i < 3 else " ".join(l) for i, l in enumerate(lines) ] return "\n".join(ret) def redraw(img, face, locations, color, text): """ Groups together all the frame drawing logic, since it was needed on many places. """ point1 = (locations[0][0], locations[0][1]) point2 = (locations[0][2], locations[0][3]) img_ = img.copy() face_and_rect = cv2.rectangle(img_, point1, point2, color=color, thickness=2) face_and_rect = draw_faces(img_, faces, color=color) face = ((face_and_rect * 0.5) + (img * 0.5)).astype(np.uint8) noface = img.copy() height, width = img.shape[:2] while height > 768 or width > 768: # hardcoded and hacky, but works face = cv2.resize(face, (int(width / 2), int(height / 2))) noface = cv2.resize(noface, (int(width / 2), int(height / 2))) height, width = face.shape[:2] if height < 768 or width < 768: new_face = np.zeros((768, 1024, 3), dtype=np.uint8) new_noface = np.zeros((768, 1024, 3), dtype=np.uint8) new_face[384 - (height // 2):384 - (height // 2) + height, 512 - (width // 2):512 - (width // 2) + width] = face new_noface[384 - (height // 2):384 - (height // 2) + height, 512 - (width // 2):512 - (width // 2) + width] = noface face = new_face noface = new_noface # a bit of an optimization to avoid multiple conversion between ndarray # and PIL.Image structures face = Image.fromarray(cv2.cvtColor(face, cv2.COLOR_BGR2RGB)) noface = Image.fromarray(cv2.cvtColor(noface, cv2.COLOR_BGR2RGB)) for y, line in enumerate(text.split("\n")): face = draw_text(face, line, (0, y * 20 + 20), CONST_FONT, 16, color) noface = draw_text(noface, line, (0, y * 20 + 20), CONST_FONT, 16, color) face = cv2.cvtColor(np.array(face), cv2.COLOR_RGB2BGR) noface = cv2.cvtColor(np.array(noface), cv2.COLOR_RGB2BGR) return face, noface tagged = [] age_counter = { "m": [0 for i in range(9)], "f": [0 for i in range(9)], } count_f = 0 count_m = 0 color = next(color_cycle) for filename in glob.glob(f"{PATH_TRAIN}/*.jpg"): img = cv2.imread(filename) locations = detect(img) faces = landmark(img, locations=locations) if len(faces) != 1: continue toggle_face = True age_tag = 0 text = (f"f: {count_f} " f"m: {count_m} " f"(total: {count_f + count_m})\n\n" + age_table(age_counter, age_tag)) frame_face, frame_noface = redraw(img, faces, locations, color, text) cv2.imshow("Tagger", frame_face) key = chr(cv2.waitKey()).lower() while key not in "q mf": if key == "k": color = next(color_cycle) frame_face, frame_noface = redraw(img, faces, locations, color, text) toggle_face = True cv2.imshow("Tagger", frame_face) if key == "l": if toggle_face: cv2.imshow("Tagger", frame_noface) toggle_face = False else: cv2.imshow("Tagger", frame_face) toggle_face = True if key in "12345678": age_tag = int(key) text = (f"f: {count_f} " f"m: {count_m} " f"(total: {count_f + count_m})\n\n" + age_table(age_counter, age_tag)) frame_face, frame_noface = redraw(img, faces, locations, color, text) toggle_face = True cv2.imshow("Tagger", frame_face) key = chr(cv2.waitKey()).lower() if key == "q": break if key == " ": continue if key == "m": tag = tags_male count_m += 1 age_counter["m"][age_tag] += 1 if key == "f": tag = tags_female count_f += 1 age_counter["f"][age_tag] += 1 tagged.append(TaggedFace(tag, age_tag, filename, img)) for t in tagged: print(t) cv2.destroyAllWindows() return tagged
def tag(): """ Show images from a path waiting for user input to tag them. We're keeping only the images were dlibs HOG face detector picks up only one face. Using dlibs 5 point face landmark dataset a balanced female/male ratio is found at about ~600 images. We also skip images were landmarking goes wrong, as they can negatively affect the result of facial encoding. :return: list of TaggedFace for each image """ def redraw(img, face, locations, color, text): """ Groups together all the frame drawing logic, since it was needed on many places. """ point1 = (locations[0][0], locations[0][1]) point2 = (locations[0][2], locations[0][3]) img_ = img.copy() face_and_rect = cv2.rectangle(img_, point1, point2, color=color, thickness=2) face_and_rect = draw_faces(img_, faces, color=color) face = ((face_and_rect * 0.5) + (img * 0.5)).astype(np.uint8) noface = img.copy() height, width = img.shape[:2] if height > 960 or width > 1800: # hardcoded and hacky, but works face = cv2.resize(face, (int(width / 2), int(height / 2))) noface = cv2.resize(noface, (int(width / 2), int(height / 2))) for y, line in enumerate(text.split("\n")): cv2.putText(face, line, (0, y * 20 + 20), CONST_FONT, 0.75, color, 2) cv2.putText(noface, line, (0, y * 20 + 20), CONST_FONT, 0.75, color, 2) return face, noface tagged = [] count_f = 0 count_m = 0 color = next(color_cycle) for filename in glob.glob(f"{PATH_TRAIN}/*.jpg"): img = cv2.imread(filename) locations = detect(img) faces = landmark(img, locations=locations) if len(faces) != 1: continue toggle_face = True text = (f"total : {count_f + count_m}\n" f"female: {count_f}\n" f"male : {count_m}") frame_face, frame_noface = redraw(img, faces, locations, color, text) cv2.imshow("Tagger", frame_face) key = chr(cv2.waitKey()).lower() while key not in "q mf": key = chr(cv2.waitKey()).lower() if key == "k": color = next(color_cycle) frame_face, frame_noface = redraw(img, faces, locations, color, text) toggle_face = True cv2.imshow("Tagger", frame_face) if key == "l": if toggle_face: cv2.imshow("Tagger", frame_noface) toggle_face = False else: cv2.imshow("Tagger", frame_face) toggle_face = True if key == "q": break if key == " ": continue if key == "m": tag = tags_male count_m += 1 if key == "f": tag = tags_female count_f += 1 tagged.append(TaggedFace(tag, filename, img)) for t in tagged: print(t) cv2.destroyAllWindows() return tagged