Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 733e747

Browse files
committed
Python package
1 parent cef8bb1 commit 733e747

File tree

5 files changed

+33466
-0
lines changed

5 files changed

+33466
-0
lines changed

MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include smartcrop/cascades/*

bin/smartcrop

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env python
2+
3+
import smartcrop
4+
smartcrop.main()

setup.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from setuptools import setup
2+
3+
setup(name='smartcrop',
4+
version='0.1',
5+
description='OpenCV smart crop',
6+
url='https://github.com/epixelic/python-smart-crop',
7+
author='Josua Gonzalez',
8+
author_email='[email protected]',
9+
include_package_data=True,
10+
license='MIT',
11+
packages=['smartcrop'],
12+
scripts=['bin/smartcrop'],
13+
zip_safe=False)

smartcrop/__init__.py

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
from __future__ import print_function
2+
from __future__ import division
3+
4+
import cv2
5+
import argparse
6+
import os
7+
8+
cascade_path = os.path.dirname(__file__) + '/cascades/haarcascade_frontalface_default.xml'
9+
10+
def center_from_faces(matrix):
11+
face_cascade = cv2.CascadeClassifier(cascade_path)
12+
faces = face_cascade.detectMultiScale(matrix, 1.3, 5)
13+
14+
x = 0
15+
y = 0
16+
weight = 0
17+
18+
for (x, y, w, h) in faces:
19+
print('Face detected at ', x, y, w, h)
20+
weight += w * h
21+
x += (x + w / 2) * w * h
22+
y += (y + h / 2) * w * h
23+
24+
if len(faces) == 0:
25+
return False
26+
27+
return {
28+
'x': x / weight,
29+
'y': y / weight
30+
}
31+
32+
33+
def center_from_good_features(matrix):
34+
x = 0
35+
y = 0
36+
weight = 0
37+
38+
max_corners = 50
39+
quality_level = 0.1
40+
min_distance = 10
41+
42+
corners = cv2.goodFeaturesToTrack(matrix, max_corners, quality_level, min_distance)
43+
44+
for point in corners:
45+
weight += 1
46+
x += point[0][0]
47+
y += point[0][1]
48+
49+
return {
50+
'x': x / weight,
51+
'y': y / weight
52+
}
53+
54+
55+
def crop_vertical_pos(center, original_width, original_height, target_width, target_height):
56+
new_height = target_height / target_width * original_width
57+
top = max(round(center['y'] - new_height / 2), 0)
58+
bottom = min(top + new_height, original_height)
59+
60+
return {
61+
'left': 0,
62+
'right': original_width,
63+
'top': top,
64+
'bottom': bottom
65+
}
66+
67+
68+
def crop_horizontal_pos(center, original_width, original_height, target_width, target_height):
69+
new_width = target_width / target_height * original_height
70+
left = max(round(center['x'] - new_width / 2), 0)
71+
right = min(left + new_width, original_width)
72+
73+
return {
74+
'left': left,
75+
'right': right,
76+
'top': 0,
77+
'bottom': original_height
78+
}
79+
80+
81+
def smart_crop(image, target_width, target_height, destination):
82+
# read grayscale image
83+
original = cv2.imread(image)
84+
85+
if original is None:
86+
print("Could not read source image")
87+
exit(1)
88+
89+
height, width, depth = original.shape
90+
91+
target_height = int(target_height)
92+
target_width = int(target_width)
93+
94+
matrix = cv2.imread(image, cv2.CV_LOAD_IMAGE_GRAYSCALE)
95+
96+
ratio = max(target_width / width, target_height / height)
97+
98+
width, height = original.shape[1] * ratio, original.shape[0] * ratio
99+
original = cv2.resize(original, (int(width), int(height)))
100+
matrix = cv2.resize(matrix, (int(width), int(height)))
101+
102+
center = center_from_faces(matrix)
103+
104+
if not center:
105+
print('Using Good Feature Tracking method')
106+
center = center_from_good_features(matrix)
107+
108+
print('Found center: ', center)
109+
110+
if width / height < target_width / target_height:
111+
crop_pos = crop_vertical_pos(center, width, height, target_width, target_height)
112+
else:
113+
crop_pos = crop_horizontal_pos(center, width, height, target_width, target_height)
114+
115+
print('Crop: ', crop_pos)
116+
117+
cropped = original[crop_pos['top']: crop_pos['bottom'], crop_pos['left']: crop_pos['right']]
118+
cv2.imwrite(destination, cropped)
119+
120+
121+
def main():
122+
ap = argparse.ArgumentParser()
123+
ap.add_argument("-W", "--width", required=True, help="Target width")
124+
ap.add_argument("-H", "--height", required=True, help="Target height")
125+
ap.add_argument("-i", "--image", required=True, help="Image to crop")
126+
ap.add_argument("-o", "--output", required=True, help="Output")
127+
128+
args = vars(ap.parse_args())
129+
130+
smart_crop(args["image"], args["width"], args["height"], args["output"])
131+
132+
133+
if __name__ == '__main__':
134+
main()

0 commit comments

Comments
 (0)