Merge pull request #14121 from AUTOMATIC1111/fix-Auto-focal-point-crop-for-opencv-4.8.x

Fix auto focal point crop for opencv >= 4.8
This commit is contained in:
AUTOMATIC1111 2023-12-02 09:46:00 +03:00 committed by GitHub
commit 9eadc4f146
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 121 additions and 116 deletions

View File

@ -3,6 +3,8 @@ import requests
import os import os
import numpy as np import numpy as np
from PIL import ImageDraw from PIL import ImageDraw
from modules import paths_internal
from pkg_resources import parse_version
GREEN = "#0F0" GREEN = "#0F0"
BLUE = "#00F" BLUE = "#00F"
@ -25,7 +27,6 @@ def crop_image(im, settings):
elif is_portrait(settings.crop_width, settings.crop_height): elif is_portrait(settings.crop_width, settings.crop_height):
scale_by = settings.crop_height / im.height scale_by = settings.crop_height / im.height
im = im.resize((int(im.width * scale_by), int(im.height * scale_by))) im = im.resize((int(im.width * scale_by), int(im.height * scale_by)))
im_debug = im.copy() im_debug = im.copy()
@ -69,6 +70,7 @@ def crop_image(im, settings):
return results return results
def focal_point(im, settings): def focal_point(im, settings):
corner_points = image_corner_points(im, settings) if settings.corner_points_weight > 0 else [] corner_points = image_corner_points(im, settings) if settings.corner_points_weight > 0 else []
entropy_points = image_entropy_points(im, settings) if settings.entropy_points_weight > 0 else [] entropy_points = image_entropy_points(im, settings) if settings.entropy_points_weight > 0 else []
@ -110,7 +112,7 @@ def focal_point(im, settings):
if corner_centroid is not None: if corner_centroid is not None:
color = BLUE color = BLUE
box = corner_centroid.bounding(max_size * corner_centroid.weight) box = corner_centroid.bounding(max_size * corner_centroid.weight)
d.text((box[0], box[1]-15), f"Edge: {corner_centroid.weight:.02f}", fill=color) d.text((box[0], box[1] - 15), f"Edge: {corner_centroid.weight:.02f}", fill=color)
d.ellipse(box, outline=color) d.ellipse(box, outline=color)
if len(corner_points) > 1: if len(corner_points) > 1:
for f in corner_points: for f in corner_points:
@ -118,7 +120,7 @@ def focal_point(im, settings):
if entropy_centroid is not None: if entropy_centroid is not None:
color = "#ff0" color = "#ff0"
box = entropy_centroid.bounding(max_size * entropy_centroid.weight) box = entropy_centroid.bounding(max_size * entropy_centroid.weight)
d.text((box[0], box[1]-15), f"Entropy: {entropy_centroid.weight:.02f}", fill=color) d.text((box[0], box[1] - 15), f"Entropy: {entropy_centroid.weight:.02f}", fill=color)
d.ellipse(box, outline=color) d.ellipse(box, outline=color)
if len(entropy_points) > 1: if len(entropy_points) > 1:
for f in entropy_points: for f in entropy_points:
@ -126,7 +128,7 @@ def focal_point(im, settings):
if face_centroid is not None: if face_centroid is not None:
color = RED color = RED
box = face_centroid.bounding(max_size * face_centroid.weight) box = face_centroid.bounding(max_size * face_centroid.weight)
d.text((box[0], box[1]-15), f"Face: {face_centroid.weight:.02f}", fill=color) d.text((box[0], box[1] - 15), f"Face: {face_centroid.weight:.02f}", fill=color)
d.ellipse(box, outline=color) d.ellipse(box, outline=color)
if len(face_points) > 1: if len(face_points) > 1:
for f in face_points: for f in face_points:
@ -159,8 +161,8 @@ def image_face_points(im, settings):
PointOfInterest( PointOfInterest(
int(x + (w * 0.5)), # face focus left/right is center int(x + (w * 0.5)), # face focus left/right is center
int(y + (h * 0.33)), # face focus up/down is close to the top of the head int(y + (h * 0.33)), # face focus up/down is close to the top of the head
size = w, size=w,
weight = 1/len(faces[1]) weight=1 / len(faces[1])
) )
) )
return results return results
@ -169,27 +171,29 @@ def image_face_points(im, settings):
gray = cv2.cvtColor(np_im, cv2.COLOR_BGR2GRAY) gray = cv2.cvtColor(np_im, cv2.COLOR_BGR2GRAY)
tries = [ tries = [
[ f'{cv2.data.haarcascades}haarcascade_eye.xml', 0.01 ], [f'{cv2.data.haarcascades}haarcascade_eye.xml', 0.01],
[ f'{cv2.data.haarcascades}haarcascade_frontalface_default.xml', 0.05 ], [f'{cv2.data.haarcascades}haarcascade_frontalface_default.xml', 0.05],
[ f'{cv2.data.haarcascades}haarcascade_profileface.xml', 0.05 ], [f'{cv2.data.haarcascades}haarcascade_profileface.xml', 0.05],
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt.xml', 0.05 ], [f'{cv2.data.haarcascades}haarcascade_frontalface_alt.xml', 0.05],
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt2.xml', 0.05 ], [f'{cv2.data.haarcascades}haarcascade_frontalface_alt2.xml', 0.05],
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt_tree.xml', 0.05 ], [f'{cv2.data.haarcascades}haarcascade_frontalface_alt_tree.xml', 0.05],
[ f'{cv2.data.haarcascades}haarcascade_eye_tree_eyeglasses.xml', 0.05 ], [f'{cv2.data.haarcascades}haarcascade_eye_tree_eyeglasses.xml', 0.05],
[ f'{cv2.data.haarcascades}haarcascade_upperbody.xml', 0.05 ] [f'{cv2.data.haarcascades}haarcascade_upperbody.xml', 0.05]
] ]
for t in tries: for t in tries:
classifier = cv2.CascadeClassifier(t[0]) classifier = cv2.CascadeClassifier(t[0])
minsize = int(min(im.width, im.height) * t[1]) # at least N percent of the smallest side minsize = int(min(im.width, im.height) * t[1]) # at least N percent of the smallest side
try: try:
faces = classifier.detectMultiScale(gray, scaleFactor=1.1, faces = classifier.detectMultiScale(gray, scaleFactor=1.1,
minNeighbors=7, minSize=(minsize, minsize), flags=cv2.CASCADE_SCALE_IMAGE) minNeighbors=7, minSize=(minsize, minsize),
flags=cv2.CASCADE_SCALE_IMAGE)
except Exception: except Exception:
continue continue
if faces: if faces:
rects = [[f[0], f[1], f[0] + f[2], f[1] + f[3]] for f in faces] rects = [[f[0], f[1], f[0] + f[2], f[1] + f[3]] for f in faces]
return [PointOfInterest((r[0] +r[2]) // 2, (r[1] + r[3]) // 2, size=abs(r[0]-r[2]), weight=1/len(rects)) for r in rects] return [PointOfInterest((r[0] + r[2]) // 2, (r[1] + r[3]) // 2, size=abs(r[0] - r[2]),
weight=1 / len(rects)) for r in rects]
return [] return []
@ -198,7 +202,7 @@ def image_corner_points(im, settings):
# naive attempt at preventing focal points from collecting at watermarks near the bottom # naive attempt at preventing focal points from collecting at watermarks near the bottom
gd = ImageDraw.Draw(grayscale) gd = ImageDraw.Draw(grayscale)
gd.rectangle([0, im.height*.9, im.width, im.height], fill="#999") gd.rectangle([0, im.height * .9, im.width, im.height], fill="#999")
np_im = np.array(grayscale) np_im = np.array(grayscale)
@ -206,7 +210,7 @@ def image_corner_points(im, settings):
np_im, np_im,
maxCorners=100, maxCorners=100,
qualityLevel=0.04, qualityLevel=0.04,
minDistance=min(grayscale.width, grayscale.height)*0.06, minDistance=min(grayscale.width, grayscale.height) * 0.06,
useHarrisDetector=False, useHarrisDetector=False,
) )
@ -216,7 +220,7 @@ def image_corner_points(im, settings):
focal_points = [] focal_points = []
for point in points: for point in points:
x, y = point.ravel() x, y = point.ravel()
focal_points.append(PointOfInterest(x, y, size=4, weight=1/len(points))) focal_points.append(PointOfInterest(x, y, size=4, weight=1 / len(points)))
return focal_points return focal_points
@ -247,8 +251,8 @@ def image_entropy_points(im, settings):
crop_current[move_idx[0]] += 4 crop_current[move_idx[0]] += 4
crop_current[move_idx[1]] += 4 crop_current[move_idx[1]] += 4
x_mid = int(crop_best[0] + settings.crop_width/2) x_mid = int(crop_best[0] + settings.crop_width / 2)
y_mid = int(crop_best[1] + settings.crop_height/2) y_mid = int(crop_best[1] + settings.crop_height / 2)
return [PointOfInterest(x_mid, y_mid, size=25, weight=1.0)] return [PointOfInterest(x_mid, y_mid, size=25, weight=1.0)]
@ -294,22 +298,23 @@ def is_square(w, h):
return w == h return w == h
def download_and_cache_models(dirname): model_dir_opencv = os.path.join(paths_internal.models_path, 'opencv')
download_url = 'https://github.com/opencv/opencv_zoo/blob/91fb0290f50896f38a0ab1e558b74b16bc009428/models/face_detection_yunet/face_detection_yunet_2022mar.onnx?raw=true' if parse_version(cv2.__version__) >= parse_version('4.8'):
model_file_name = 'face_detection_yunet.onnx' model_file_path = os.path.join(model_dir_opencv, 'face_detection_yunet_2023mar.onnx')
model_url = 'https://github.com/opencv/opencv_zoo/blob/b6e370b10f641879a87890d44e42173077154a05/models/face_detection_yunet/face_detection_yunet_2023mar.onnx?raw=true'
else:
model_file_path = os.path.join(model_dir_opencv, 'face_detection_yunet.onnx')
model_url = 'https://github.com/opencv/opencv_zoo/blob/91fb0290f50896f38a0ab1e558b74b16bc009428/models/face_detection_yunet/face_detection_yunet_2022mar.onnx?raw=true'
os.makedirs(dirname, exist_ok=True)
cache_file = os.path.join(dirname, model_file_name) def download_and_cache_models():
if not os.path.exists(cache_file): if not os.path.exists(model_file_path):
print(f"downloading face detection model from '{download_url}' to '{cache_file}'") os.makedirs(model_dir_opencv, exist_ok=True)
response = requests.get(download_url) print(f"downloading face detection model from '{model_url}' to '{model_file_path}'")
with open(cache_file, "wb") as f: response = requests.get(model_url)
with open(model_file_path, "wb") as f:
f.write(response.content) f.write(response.content)
return model_file_path
if os.path.exists(cache_file):
return cache_file
return None
class PointOfInterest: class PointOfInterest:

View File

@ -3,7 +3,7 @@ from PIL import Image, ImageOps
import math import math
import tqdm import tqdm
from modules import paths, shared, images, deepbooru from modules import shared, images, deepbooru
from modules.textual_inversion import autocrop from modules.textual_inversion import autocrop
@ -196,7 +196,7 @@ def preprocess_work(process_src, process_dst, process_width, process_height, pre
dnn_model_path = None dnn_model_path = None
try: try:
dnn_model_path = autocrop.download_and_cache_models(os.path.join(paths.models_path, "opencv")) dnn_model_path = autocrop.download_and_cache_models()
except Exception as e: except Exception as e:
print("Unable to load face detection model for auto crop selection. Falling back to lower quality haar method.", e) print("Unable to load face detection model for auto crop selection. Falling back to lower quality haar method.", e)