Skip to content

Commit e5e5bd9

Browse files
committed
WiP on gravity and any sized image
1 parent 49008b6 commit e5e5bd9

File tree

3 files changed

+99
-31
lines changed

3 files changed

+99
-31
lines changed

finder/contrib/image/models.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,25 @@ class Meta:
2929
def summary(self):
3030
return "{width}×{height}px ({size})".format(size=super().summary, width=self.width, height=self.height)
3131

32-
def get_thumbnail_path(self, crop_x=None, crop_y=None, crop_size=None, gravity=None):
32+
def get_thumbnail_path(self, width, height):
3333
id = str(self.id)
3434
thumbnail_folder = self.filer_public_thumbnails / f'{id[0:2]}/{id[2:4]}/{id}'
3535
thumbnail_path = Path(self.file_name)
36+
crop_x, crop_y, crop_size, gravity = (
37+
self.meta_data.get('crop_x'),
38+
self.meta_data.get('crop_y'),
39+
self.meta_data.get('crop_size'),
40+
self.meta_data.get('gravity'),
41+
)
3642
if crop_x is None or crop_y is None or crop_size is None:
3743
thumbnail_path_template = '{stem}__{width}x{height}{suffix}'
3844
else:
3945
crop_x, crop_y, crop_size = int(crop_x), int(crop_y), int(crop_size)
4046
thumbnail_path_template = '{stem}__{width}x{height}__{crop_x}_{crop_y}_{crop_size}{gravity}{suffix}'
4147
return thumbnail_folder / thumbnail_path_template.format(
4248
stem=thumbnail_path.stem,
43-
width=self.thumbnail_size,
44-
height=self.thumbnail_size,
49+
width=width,
50+
height=height,
4551
crop_x=crop_x,
4652
crop_y=crop_y,
4753
crop_size=crop_size,

finder/contrib/image/pil/models.py

Lines changed: 89 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,10 @@ def save(self, **kwargs):
4444
super().save(**kwargs)
4545

4646
def get_thumbnail_url(self):
47-
crop_x, crop_y, crop_size, gravity = (
48-
self.meta_data.get('crop_x'), self.meta_data.get('crop_y'), self.meta_data.get('crop_size'),
49-
self.meta_data.get('gravity')
50-
)
51-
thumbnail_path = self.get_thumbnail_path(crop_x, crop_y, crop_size, gravity)
52-
if not default_storage.exists(thumbnail_path):
47+
thumbnail_path = self.get_thumbnail_path(self.thumbnail_size, self.thumbnail_size)
48+
if str(self.id) == 'XXX-561d6907-5fd7-40b9-a6f2-5db45a67eda6' or not default_storage.exists(thumbnail_path):
5349
try:
54-
image = Image.open(default_storage.open(self.file_path))
55-
image = self.orientate_top(image)
56-
if crop_x is None or crop_y is None or crop_size is None:
57-
image = self.crop_centered(image)
58-
else:
59-
image = self.crop_eccentric(image, crop_x, crop_y, crop_size, gravity)
60-
image.thumbnail((self.thumbnail_size, self.thumbnail_size))
61-
(default_storage.base_location / thumbnail_path.parent).mkdir(parents=True, exist_ok=True)
62-
image.save(default_storage.open(thumbnail_path, 'wb'), image.format)
50+
self.crop(thumbnail_path, self.thumbnail_size, self.thumbnail_size * 0.75)
6351
except Exception:
6452
# thumbnail image could not be created
6553
return self.fallback_thumbnail_url
@@ -84,19 +72,96 @@ def orientate_top(self, image):
8472
image = image.transpose(Image.ROTATE_90)
8573
return image
8674

75+
def crop(self, thumbnail_path, width, height):
76+
aspect_ratio = width / height
77+
image = Image.open(default_storage.open(self.file_path))
78+
image = self.orientate_top(image)
79+
print(f"Wanted site: width={width}, height={height}, aspect_ratio={aspect_ratio} orig={image.size}")
80+
orig_width, orig_height = image.size
81+
crop_x, crop_y, crop_size, gravity = (
82+
self.meta_data.get('crop_x'),
83+
self.meta_data.get('crop_y'),
84+
self.meta_data.get('crop_size'),
85+
self.meta_data.get('gravity'),
86+
)
87+
if crop_x is None or crop_y is None or crop_size is None:
88+
# crop in the center of the image
89+
if orig_width > orig_height:
90+
crop_x = (orig_width - orig_height) / 2
91+
crop_y = 0
92+
crop_size = orig_height
93+
else:
94+
crop_x = 0
95+
crop_y = (orig_height - orig_width) / 2
96+
crop_size = orig_width
97+
98+
print(f"Crop parameters: crop_x={crop_x}, crop_y={crop_y}, crop_size={crop_size}, gravity={gravity}")
99+
100+
# horizontal thumbnailing
101+
if aspect_ratio < 1:
102+
min_width = max(crop_size * aspect_ratio * 3 / 2, width)
103+
else:
104+
min_width = max(crop_size, width)
105+
if aspect_ratio < 1:
106+
min_x = crop_x + (crop_size - min_width) / 2
107+
elif crop_size < min_width:
108+
min_x = crop_y - (min_width - crop_size) / 2
109+
else:
110+
min_x = crop_x
111+
if crop_size < min_width:
112+
if gravity in ('e', 'ne', 'se'):
113+
max_x = min(crop_x + min_width, image.width)
114+
min_x = max(max_x - min_width, 0)
115+
elif gravity in ('w', 'nw', 'sw'):
116+
min_x = max(crop_x - min_width + crop_size, 0)
117+
if min_x + min_width > image.width:
118+
min_x = max(image.width - min_width, 0)
119+
max_x = image.width
120+
else:
121+
max_x = min_x + min_width
122+
123+
# vertical thumbnailing
124+
if orig_width < orig_height:
125+
min_height = max(crop_size / aspect_ratio, height)
126+
else:
127+
min_height = max(min_width / aspect_ratio, height)
128+
if aspect_ratio > 1:
129+
min_y = crop_y + (crop_size - min_height) / 2
130+
elif crop_size < min_height:
131+
min_y = crop_y - (min_height - crop_size) / 2
132+
else:
133+
min_y = crop_y
134+
if crop_size < min_height:
135+
if gravity in ('s', 'se', 'sw'):
136+
max_y = min(crop_y + min_height, image.height)
137+
min_y = max(max_y - min_height, 0)
138+
elif gravity in ('n', 'ne', 'nw'):
139+
min_y = max(crop_y - min_height + crop_size, 0)
140+
if min_y + min_height > image.height:
141+
min_y = max(image.height - min_height, 0)
142+
max_y = image.height
143+
else:
144+
max_y = min_y + min_height
145+
146+
print(f"Crop area: ({min_x}, {min_y}) to ({max_x}, {max_y}) = {max_x - min_x} x {max_y - min_y}")
147+
image = image.crop((min_x, min_y, max_x, max_y))
148+
image.thumbnail((width, height))
149+
(default_storage.base_location / thumbnail_path.parent).mkdir(parents=True, exist_ok=True)
150+
image.save(default_storage.open(thumbnail_path, 'wb'), image.format)
151+
87152
def crop_centered(self, image):
88153
width, height = image.size
89154
if width > height:
90-
left = (width - height) / 2
91-
top = 0
92-
right = (width + height) / 2
93-
bottom = height
155+
min_x = (width - height) / 2
156+
min_y = 0
157+
max_x = (width + height) / 2
158+
max_y = height
94159
else:
95-
left = 0
96-
top = (height - width) / 2
97-
right = width
98-
bottom = (height + width) / 2
99-
return image.crop((left, top, right, bottom))
160+
min_x = 0
161+
min_y = (height - width) / 2
162+
max_x = width
163+
max_y = (height + width) / 2
164+
return image.crop((min_x, min_y, max_x, max_y))
100165

101166
def crop_eccentric(self, image, crop_x, crop_y, crop_size, gravity):
102167
"""

finder/contrib/image/svg/models.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,7 @@ def save(self, **kwargs):
4141
super().save(**kwargs)
4242

4343
def get_thumbnail_url(self):
44-
crop_x, crop_y, crop_size = (
45-
self.meta_data.get('crop_x'), self.meta_data.get('crop_y'), self.meta_data.get('crop_size')
46-
)
47-
thumbnail_path = self.get_thumbnail_path(crop_x, crop_y, crop_size)
44+
thumbnail_path = self.get_thumbnail_path(self.thumbnail_size, self.thumbnail_size)
4845
if not default_storage.exists(thumbnail_path):
4946
drawing = svg2rlg(default_storage.path(self.file_path))
5047
if not drawing:

0 commit comments

Comments
 (0)