Skip to content

Commit 3e6c242

Browse files
committed
improve debug markers, fix algo weighting
1 parent 1be5933 commit 3e6c242

File tree

1 file changed

+129
-78
lines changed

1 file changed

+129
-78
lines changed

modules/textual_inversion/autocrop.py

Lines changed: 129 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import cv2
2+
import os
23
from collections import defaultdict
34
from math import log, sqrt
45
import numpy as np
@@ -26,19 +27,9 @@ def crop_image(im, settings):
2627
scale_by = settings.crop_height / im.height
2728

2829
im = im.resize((int(im.width * scale_by), int(im.height * scale_by)))
30+
im_debug = im.copy()
2931

30-
if im.width == settings.crop_width and im.height == settings.crop_height:
31-
if settings.annotate_image:
32-
d = ImageDraw.Draw(im)
33-
rect = [0, 0, im.width, im.height]
34-
rect[2] -= 1
35-
rect[3] -= 1
36-
d.rectangle(rect, outline=GREEN)
37-
if settings.destop_view_image:
38-
im.show()
39-
return im
40-
41-
focus = focal_point(im, settings)
32+
focus = focal_point(im_debug, settings)
4233

4334
# take the focal point and turn it into crop coordinates that try to center over the focal
4435
# point but then get adjusted back into the frame
@@ -62,89 +53,143 @@ def crop_image(im, settings):
6253

6354
crop = [x1, y1, x2, y2]
6455

56+
results = []
57+
58+
results.append(im.crop(tuple(crop)))
59+
6560
if settings.annotate_image:
66-
d = ImageDraw.Draw(im)
61+
d = ImageDraw.Draw(im_debug)
6762
rect = list(crop)
6863
rect[2] -= 1
6964
rect[3] -= 1
7065
d.rectangle(rect, outline=GREEN)
66+
results.append(im_debug)
7167
if settings.destop_view_image:
72-
im.show()
68+
im_debug.show()
7369

74-
return im.crop(tuple(crop))
70+
return results
7571

7672
def focal_point(im, settings):
7773
corner_points = image_corner_points(im, settings)
7874
entropy_points = image_entropy_points(im, settings)
7975
face_points = image_face_points(im, settings)
8076

81-
total_points = len(corner_points) + len(entropy_points) + len(face_points)
82-
83-
corner_weight = settings.corner_points_weight
84-
entropy_weight = settings.entropy_points_weight
85-
face_weight = settings.face_points_weight
86-
87-
weight_pref_total = corner_weight + entropy_weight + face_weight
88-
89-
# weight things
9077
pois = []
91-
if weight_pref_total == 0 or total_points == 0:
92-
return pois
9378

94-
pois.extend(
95-
[ PointOfInterest( p.x, p.y, weight=p.weight * ( (corner_weight/weight_pref_total) / (len(corner_points)/total_points) )) for p in corner_points ]
96-
)
97-
pois.extend(
98-
[ PointOfInterest( p.x, p.y, weight=p.weight * ( (entropy_weight/weight_pref_total) / (len(entropy_points)/total_points) )) for p in entropy_points ]
99-
)
100-
pois.extend(
101-
[ PointOfInterest( p.x, p.y, weight=p.weight * ( (face_weight/weight_pref_total) / (len(face_points)/total_points) )) for p in face_points ]
102-
)
79+
weight_pref_total = 0
80+
if len(corner_points) > 0:
81+
weight_pref_total += settings.corner_points_weight
82+
if len(entropy_points) > 0:
83+
weight_pref_total += settings.entropy_points_weight
84+
if len(face_points) > 0:
85+
weight_pref_total += settings.face_points_weight
86+
87+
corner_centroid = None
88+
if len(corner_points) > 0:
89+
corner_centroid = centroid(corner_points)
90+
corner_centroid.weight = settings.corner_points_weight / weight_pref_total
91+
pois.append(corner_centroid)
92+
93+
entropy_centroid = None
94+
if len(entropy_points) > 0:
95+
entropy_centroid = centroid(entropy_points)
96+
entropy_centroid.weight = settings.entropy_points_weight / weight_pref_total
97+
pois.append(entropy_centroid)
98+
99+
face_centroid = None
100+
if len(face_points) > 0:
101+
face_centroid = centroid(face_points)
102+
face_centroid.weight = settings.face_points_weight / weight_pref_total
103+
pois.append(face_centroid)
103104

104105
average_point = poi_average(pois, settings)
105106

106107
if settings.annotate_image:
107108
d = ImageDraw.Draw(im)
108-
for f in face_points:
109-
d.rectangle(f.bounding(f.size), outline=RED)
110-
for f in entropy_points:
111-
d.rectangle(f.bounding(30), outline=BLUE)
112-
for poi in pois:
113-
w = max(4, 4 * 0.5 * sqrt(poi.weight))
114-
d.ellipse(poi.bounding(w), fill=BLUE)
115-
d.ellipse(average_point.bounding(25), outline=GREEN)
109+
max_size = min(im.width, im.height) * 0.07
110+
if corner_centroid is not None:
111+
color = BLUE
112+
box = corner_centroid.bounding(max_size * corner_centroid.weight)
113+
d.text((box[0], box[1]-15), "Edge: %.02f" % corner_centroid.weight, fill=color)
114+
d.ellipse(box, outline=color)
115+
if len(corner_points) > 1:
116+
for f in corner_points:
117+
d.rectangle(f.bounding(4), outline=color)
118+
if entropy_centroid is not None:
119+
color = "#ff0"
120+
box = entropy_centroid.bounding(max_size * entropy_centroid.weight)
121+
d.text((box[0], box[1]-15), "Entropy: %.02f" % entropy_centroid.weight, fill=color)
122+
d.ellipse(box, outline=color)
123+
if len(entropy_points) > 1:
124+
for f in entropy_points:
125+
d.rectangle(f.bounding(4), outline=color)
126+
if face_centroid is not None:
127+
color = RED
128+
box = face_centroid.bounding(max_size * face_centroid.weight)
129+
d.text((box[0], box[1]-15), "Face: %.02f" % face_centroid.weight, fill=color)
130+
d.ellipse(box, outline=color)
131+
if len(face_points) > 1:
132+
for f in face_points:
133+
d.rectangle(f.bounding(4), outline=color)
134+
135+
d.ellipse(average_point.bounding(max_size), outline=GREEN)
116136

117137
return average_point
118138

119139

120140
def image_face_points(im, settings):
121-
np_im = np.array(im)
122-
gray = cv2.cvtColor(np_im, cv2.COLOR_BGR2GRAY)
123-
124-
tries = [
125-
[ f'{cv2.data.haarcascades}haarcascade_eye.xml', 0.01 ],
126-
[ f'{cv2.data.haarcascades}haarcascade_frontalface_default.xml', 0.05 ],
127-
[ f'{cv2.data.haarcascades}haarcascade_profileface.xml', 0.05 ],
128-
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt.xml', 0.05 ],
129-
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt2.xml', 0.05 ],
130-
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt_tree.xml', 0.05 ],
131-
[ f'{cv2.data.haarcascades}haarcascade_eye_tree_eyeglasses.xml', 0.05 ],
132-
[ f'{cv2.data.haarcascades}haarcascade_upperbody.xml', 0.05 ]
133-
]
134-
135-
for t in tries:
136-
# print(t[0])
137-
classifier = cv2.CascadeClassifier(t[0])
138-
minsize = int(min(im.width, im.height) * t[1]) # at least N percent of the smallest side
139-
try:
140-
faces = classifier.detectMultiScale(gray, scaleFactor=1.1,
141-
minNeighbors=7, minSize=(minsize, minsize), flags=cv2.CASCADE_SCALE_IMAGE)
142-
except:
143-
continue
144-
145-
if len(faces) > 0:
146-
rects = [[f[0], f[1], f[0] + f[2], f[1] + f[3]] for f in faces]
147-
return [PointOfInterest((r[0] +r[2]) // 2, (r[1] + r[3]) // 2, size=abs(r[0]-r[2])) for r in rects]
141+
if settings.dnn_model_path is not None:
142+
detector = cv2.FaceDetectorYN.create(
143+
settings.dnn_model_path,
144+
"",
145+
(im.width, im.height),
146+
0.8, # score threshold
147+
0.3, # nms threshold
148+
5000 # keep top k before nms
149+
)
150+
faces = detector.detect(np.array(im))
151+
results = []
152+
if faces[1] is not None:
153+
for face in faces[1]:
154+
x = face[0]
155+
y = face[1]
156+
w = face[2]
157+
h = face[3]
158+
results.append(
159+
PointOfInterest(
160+
int(x + (w * 0.5)), # face focus left/right is center
161+
int(y + (h * 0)), # face focus up/down is close to the top of the head
162+
size = w,
163+
weight = 1/len(faces[1])
164+
)
165+
)
166+
return results
167+
else:
168+
np_im = np.array(im)
169+
gray = cv2.cvtColor(np_im, cv2.COLOR_BGR2GRAY)
170+
171+
tries = [
172+
[ f'{cv2.data.haarcascades}haarcascade_eye.xml', 0.01 ],
173+
[ f'{cv2.data.haarcascades}haarcascade_frontalface_default.xml', 0.05 ],
174+
[ f'{cv2.data.haarcascades}haarcascade_profileface.xml', 0.05 ],
175+
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt.xml', 0.05 ],
176+
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt2.xml', 0.05 ],
177+
[ f'{cv2.data.haarcascades}haarcascade_frontalface_alt_tree.xml', 0.05 ],
178+
[ f'{cv2.data.haarcascades}haarcascade_eye_tree_eyeglasses.xml', 0.05 ],
179+
[ f'{cv2.data.haarcascades}haarcascade_upperbody.xml', 0.05 ]
180+
]
181+
for t in tries:
182+
classifier = cv2.CascadeClassifier(t[0])
183+
minsize = int(min(im.width, im.height) * t[1]) # at least N percent of the smallest side
184+
try:
185+
faces = classifier.detectMultiScale(gray, scaleFactor=1.1,
186+
minNeighbors=7, minSize=(minsize, minsize), flags=cv2.CASCADE_SCALE_IMAGE)
187+
except:
188+
continue
189+
190+
if len(faces) > 0:
191+
rects = [[f[0], f[1], f[0] + f[2], f[1] + f[3]] for f in faces]
192+
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]
148193
return []
149194

150195

@@ -161,7 +206,7 @@ def image_corner_points(im, settings):
161206
np_im,
162207
maxCorners=100,
163208
qualityLevel=0.04,
164-
minDistance=min(grayscale.width, grayscale.height)*0.07,
209+
minDistance=min(grayscale.width, grayscale.height)*0.03,
165210
useHarrisDetector=False,
166211
)
167212

@@ -171,7 +216,7 @@ def image_corner_points(im, settings):
171216
focal_points = []
172217
for point in points:
173218
x, y = point.ravel()
174-
focal_points.append(PointOfInterest(x, y, size=4))
219+
focal_points.append(PointOfInterest(x, y, size=4, weight=1/len(points)))
175220

176221
return focal_points
177222

@@ -205,17 +250,22 @@ def image_entropy_points(im, settings):
205250
x_mid = int(crop_best[0] + settings.crop_width/2)
206251
y_mid = int(crop_best[1] + settings.crop_height/2)
207252

208-
return [PointOfInterest(x_mid, y_mid, size=25)]
253+
return [PointOfInterest(x_mid, y_mid, size=25, weight=1.0)]
209254

210255

211256
def image_entropy(im):
212257
# greyscale image entropy
213-
# band = np.asarray(im.convert("L"))
214-
band = np.asarray(im.convert("1"), dtype=np.uint8)
258+
band = np.asarray(im.convert("L"))
259+
# band = np.asarray(im.convert("1"), dtype=np.uint8)
215260
hist, _ = np.histogram(band, bins=range(0, 256))
216261
hist = hist[hist > 0]
217262
return -np.log2(hist / hist.sum()).sum()
218263

264+
def centroid(pois):
265+
x = [poi.x for poi in pois]
266+
y = [poi.y for poi in pois]
267+
return PointOfInterest(sum(x)/len(pois), sum(y)/len(pois))
268+
219269

220270
def poi_average(pois, settings):
221271
weight = 0.0
@@ -260,11 +310,12 @@ def bounding(self, size):
260310

261311

262312
class Settings:
263-
def __init__(self, crop_width=512, crop_height=512, corner_points_weight=0.5, entropy_points_weight=0.5, face_points_weight=0.5, annotate_image=False):
313+
def __init__(self, crop_width=512, crop_height=512, corner_points_weight=0.5, entropy_points_weight=0.5, face_points_weight=0.5, annotate_image=False, dnn_model_path=None):
264314
self.crop_width = crop_width
265315
self.crop_height = crop_height
266316
self.corner_points_weight = corner_points_weight
267317
self.entropy_points_weight = entropy_points_weight
268-
self.face_points_weight = entropy_points_weight
318+
self.face_points_weight = face_points_weight
269319
self.annotate_image = annotate_image
270-
self.destop_view_image = False
320+
self.destop_view_image = False
321+
self.dnn_model_path = dnn_model_path

0 commit comments

Comments
 (0)