-
Notifications
You must be signed in to change notification settings - Fork 72
/
Copy pathpreprocess_cats.py
127 lines (110 loc) · 4.41 KB
/
preprocess_cats.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
### adapted from https://github.com/AlexiaJM/RelativisticGAN/blob/master/code/preprocess_cat_dataset.py
### original code from https://github.com/microe/angora-blue/blob/master/cascade_training/describe.py by Erik Hovland
import argparse
import cv2
import glob
import math
import os
from tqdm import tqdm
def rotateCoords(coords, center, angleRadians):
# Positive y is down so reverse the angle, too.
angleRadians = -angleRadians
xs, ys = coords[::2], coords[1::2]
newCoords = []
n = min(len(xs), len(ys))
i = 0
centerX = center[0]
centerY = center[1]
cosAngle = math.cos(angleRadians)
sinAngle = math.sin(angleRadians)
while i < n:
xOffset = xs[i] - centerX
yOffset = ys[i] - centerY
newX = xOffset * cosAngle - yOffset * sinAngle + centerX
newY = xOffset * sinAngle + yOffset * cosAngle + centerY
newCoords += [newX, newY]
i += 1
return newCoords
def preprocessCatFace(coords, image):
leftEyeX, leftEyeY = coords[0], coords[1]
rightEyeX, rightEyeY = coords[2], coords[3]
mouthX = coords[4]
if leftEyeX > rightEyeX and leftEyeY < rightEyeY and \
mouthX > rightEyeX:
# The "right eye" is in the second quadrant of the face,
# while the "left eye" is in the fourth quadrant (from the
# viewer's perspective.) Swap the eyes' labels in order to
# simplify the rotation logic.
leftEyeX, rightEyeX = rightEyeX, leftEyeX
leftEyeY, rightEyeY = rightEyeY, leftEyeY
eyesCenter = (0.5 * (leftEyeX + rightEyeX),
0.5 * (leftEyeY + rightEyeY))
eyesDeltaX = rightEyeX - leftEyeX
eyesDeltaY = rightEyeY - leftEyeY
eyesAngleRadians = math.atan2(eyesDeltaY, eyesDeltaX)
eyesAngleDegrees = eyesAngleRadians * 180.0 / math.pi
# Straighten the image and fill in gray for blank borders.
rotation = cv2.getRotationMatrix2D(
eyesCenter, eyesAngleDegrees, 1.0)
imageSize = image.shape[1::-1]
straight = cv2.warpAffine(image, rotation, imageSize,
borderValue=(128, 128, 128))
# Straighten the coordinates of the features.
newCoords = rotateCoords(
coords, eyesCenter, eyesAngleRadians)
# Make the face as wide as the space between the ear bases.
w = abs(newCoords[16] - newCoords[6])
# Make the face square.
h = w
# Put the center point between the eyes at (0.5, 0.4) in
# proportion to the entire face.
minX = eyesCenter[0] - w / 2
if minX < 0:
w += minX
minX = 0
minY = eyesCenter[1] - h * 2 / 5
if minY < 0:
h += minY
minY = 0
# Crop the face.
crop = straight[int(minY):int(minY + h), int(minX):int(minX + w)]
# Return the crop.
return crop
def describePositive(root, outdir):
filenames = glob.glob('%s/CAT_*/*.jpg' % root)
for imagePath in tqdm(filenames, total=len(filenames), desc='Process images...'):
# Open the '.cat' annotation file associated with this
# image.
if not os.path.isfile('%s.cat' % imagePath):
print('.cat file missing at %s' % imagePath)
continue
input = open('%s.cat' % imagePath, 'r')
# Read the coordinates of the cat features from the
# file. Discard the first number, which is the number
# of features.
coords = [int(i) for i in input.readline().split()[1:]]
# Read the image.
image = cv2.imread(imagePath)
# Straighten and crop the cat face.
crop = preprocessCatFace(coords, image)
if crop is None:
print('Failed to preprocess image at %s' % imagePath)
continue
# Save the crop to folders based on size
h, w, colors = crop.shape
if min(h, w) >= 64:
Path1 = imagePath.replace(root, outdir)
os.makedirs(os.path.dirname(Path1), exist_ok=True)
resized_crop = cv2.resize(crop, (64, 64))
cv2.imwrite(Path1, resized_crop)
if __name__ == '__main__':
# Arguments
parser = argparse.ArgumentParser(
description='Crop cats from the CatDataset.'
)
parser.add_argument('root', type=str, help='Path to data directory containing "CAT_00" - "CAT_06" folders.')
args = parser.parse_args()
outdir = './cats'
os.makedirs(outdir, exist_ok=True)
describePositive(args.root, outdir)
print('Preprocessed {} images.'.format(len(glob.glob(os.path.join(outdir, '*/*.jpg')))))