Hi I am am working on a small script that would make repetitive picture cropping easier. I wrote a small script that gets the x,y coordinates from the image using mouse event and I am trying to use a code that i have found in the openCV tutorials for cropping an image with a face to certain sizes for a facial database.
import sys, math
import Image
import cv2
import numpy as np
import Tkinter
import tkFileDialog
from Tkinter import Tk
from tkFileDialog import askopenfilename
def Distance(p1,p2):
dx = p2[0] - p1[0]
dy = p2[1] - p1[1]
return math.sqrt(dx*dx+dy*dy)
def ScaleRotateTranslate(image, angle, center = None, new_center = None, scale = None, resample=Image.BICUBIC):
if (scale is None) and (center is None):
return image.rotate(angle=angle, resample=resample)
nx,ny = x,y = center
sx=sy=1.0
if new_center:
(nx,ny) = new_center
if scale:
(sx,sy) = (scale, scale)
cosine = math.cos(angle)
sine = math.sin(angle)
a = cosine/sx
b = sine/sx
c = x-nx*a-ny*b
d = -sine/sy
e = cosine/sy
f = y-nx*d-ny*e
return image.transform(image.size, Image.AFFINE, (a,b,c,d,e,f), resample=resample)
def CropFace(image, eye_left=(0,0), eye_right=(0,0), offset_pct=(0.2,0.2), dest_sz = (70,70)):
# calculate offsets in original image
offset_h = math.floor(float(offset_pct[0])*dest_sz[0])
offset_v = math.floor(float(offset_pct[1])*dest_sz[1])
# get the direction
eye_direction = (eye_right[0] - eye_left[0], eye_right[1] - eye_left[1])
# calc rotation angle in radians
rotation = -math.atan2(float(eye_direction[1]),float(eye_direction[0]))
# distance between them
dist = Distance(eye_left, eye_right)
# calculate the reference eye-width
reference = dest_sz[0] - 2.0*offset_h
# scale factor
scale = float(dist)/float(reference)
# rotate original around the left eye
image = ScaleRotateTranslate(image, center=eye_left, angle=rotation)
# crop the rotated image
crop_xy = (eye_left[0] - scale*offset_h, eye_left[1] - scale*offset_v)
crop_size = (dest_sz[0]*scale, dest_sz[1]*scale)
image = image.crop((int(crop_xy[0]), int(crop_xy[1]), int(crop_xy[0]+crop_size[0]), int(crop_xy[1]+crop_size[1])))
# resize it
image = image.resize(dest_sz, Image.ANTIALIAS)
return image
# mouse callback function
def getCoord(event,x,y,flags,param):
global click
global xs
global ys
global xs1
global ys1
if event == cv2.EVENT_LBUTTONDOWN:
print str(x) + " " + str(y)
with open("coords.txt", "a") as myfile:
if click == 0:
xs = x
ys = y
#myfile.write(str(x) + " " + str(y) + " ")
if click == 1:
xs1 = x
ys1 = y
#myfile.write(str(x) + " " + str(y) + "\n")
print "Press 'Esc' to go to next picture or to exit"
click += 1
if __name__ == "__main__":
global click
global xs
global ys
global xs1
global ys1
print "\n"
print "\n"
click = 0
fileList = []
root = Tkinter.Tk()
filez = tkFileDialog.askopenfilenames(parent=root,title='Choose a file/files')
fileList = root.tk.splitlist(filez)
print root.tk.splitlist(filez)
lenght = len(fileList)
print str(lenght)
for num in range(0,lenght):
if click == 0:
print "test"
image = cv2.imread(str(fileList[num]), cv2.IMREAD_COLOR)
cv2.namedWindow("Find and Cropp")
cv2.setMouseCallback("Find and Cropp", getCoord)
cv2.imshow("Find and Cropp", image)
if cv2.waitKey(0) & 0xFF == 27:
cv2.destroyWindow("Find and Cropp")
if click > 0:
elx = int(xs)
#print xs
ely = int(ys)
#print ys
erx = int(xs1)
#print xs1
ery = int(ys1)
#print ys1
if click == 2:
#CropFace(image, eye_left=(115,115), eye_right=(168,120), offset_pct=(0.1,0.1), dest_sz=(200,200)).save("01a.jpg")
CropFace(image, eye_left=(elx,ely), eye_right=(erx,ery), offset_pct=(0.2,0.2), dest_sz=(200,200)).save("data/" + str(num+1) + ".jpg")
CropFace(image, eye_left=(elx,ely), eye_right=(erx,ery), offset_pct=(0.3,0.3), dest_sz=(200,200)).save("data/" + str(num+1) + "a.jpg")
#CropFace(image, eye_left=(115,115), eye_right=(168,120), offset_pct=(0.2,0.2)).save("01d.jpg")
print "********************" + str(num+1) + ".jpg - saved"
click = 0
cv2.destroyWindow("Find and Cropp")
Te error that I am getting is
AttributeError: 'numpy.ndarray' object has no atribute 'transform'
The error starts on the line:
CropFace(image, eye_left=(elx,ely), eye_right=(erx,ery), offset_pct=(0.2,0.2), dest_sz=(200,200)).save("data/" + str(num+1) + ".jpg")
and finishesh at line:
return image.transform(image.size, Image.AFFINE, (a,b,c,d,e,f), resample=resample)
But when i run the code just for cropping the face (the coordinates were obtained earlier using a first script and saved to a text file) everything is running fine.
-
I don't immediately see the problem, but that's also a bit hard because it's just so much code. If you could narrow down your code to the minimal sample that shows when it works and doesn't work, it would be easier to help, plus you might come across the problem along the way yourself.Nicolas78– Nicolas782014年04月19日 22:43:44 +00:00Commented Apr 19, 2014 at 22:43
-
@Nicolas78: Added some comments...Saint– Saint2014年04月20日 00:37:15 +00:00Commented Apr 20, 2014 at 0:37
-
Did you trace your code line by line, seeing the value of every variables and making sure that they have the right value?!Hadi– Hadi2014年04月20日 11:46:35 +00:00Commented Apr 20, 2014 at 11:46
1 Answer 1
There is a difference between OpenCV and PIL dealing with images. OpenCV is dealing with images as a NumPy array, but PIL stores it as a PIL.Image object.
A NumPy array object doesn't have a transform function, but a PIL Image object has.
If you don't wish to implement the crop face function yourself, first change the NumPy array into a PIL image, and the change the PIL image into a NumPy array.
Change
CropFace(image, eye_left=(elx,ely), eye_right=(erx,ery), offset_pct=(0.3,0.3), dest_sz=(200,200)).save("data/" + str(num+1) + "a.jpg")
into
CropFace(Image.fromarray(np.uint8(image)), eye_left=(elx,ely), eye_right=(erx,ery), offset_pct=(0.3,0.3), dest_sz=(200,200)).save("data/" + str(num+1) + "a.jpg")