I can't remember how many bugs/problems I had because of not converting the coordinates well or using the incorrect coordinate system when I was at the uni. So I decided to write this little post so my great audience (approx. 6 viewers per day) can benefit from it :)
CoreImage Coordinate system
In CoreImage, each image has its own coordinate space its origin in at the left bottom corner of the image. Each images's coordinate systems is device independent.
UIKit Coordinate System
(From View Programming Guide for iOS)
The origin of an UIViews frame is at the top left corner and its coordinates are in their superview coordinate space. They are not independent.
This means that in CoreImage each image has its origin as
{0.0}
while in UIKit views are not necessarily like so
Converting coordinates
We should convert CoreImage coordinates to UIKit coordinates system, not the other way around, because chances are that most of your code will be in UIKit coordinates and because other iOS developers will expect{0,0}
to be at the top left corner, be nice and don't change everything just because of CoreImage. :) This is easily done with an affine transform: Where ui and ci subindexes mean UIKit and CoreImage coordinates respectively and h is the height of the image in regard.
We could do this manually but happily there are a bunch of functions for this task like:
CGAffineTransformMakeScale
, CGAffineTransformTranslate
, CGPointApplyAffineTransform
and even CGRectApplyAffineTransform
!. Thanks to Apple for making our life easier. The code
// Create the image and detector CIImage* image = [CIImage imageWithCGImage:imageView.image.CGImage]; CIDetector* detector = [CIDetector detectorOfType:CIDetectorTypeFace context:... options:...]; // CoreImage coordinate system origin is at the bottom left corner // and UIKit is at the top left corner. So we need to translate // features positions before drawing them to screen. In order to do // so we make an affine transform CGAffineTransform transform = CGAffineTransformMakeScale(1, -1); transform = CGAffineTransformTranslate(transform, 0, -imageView.bounds.size.height); // Get features from the image NSArray* features = [detector featuresInImage:image]; for(CIFaceFeature* faceFeature in features) { // Get the face rect: Convert CoreImage to UIKit coordinates const CGRect faceRect = CGRectApplyAffineTransform(faceFeature.bounds, transform); // create a UIView using the bounds of the face UIView* faceView = [[UIView alloc] initWithFrame:faceRect]; ... if(faceFeature.hasLeftEyePosition) { // Get the left eye position: Convert CoreImage to UIKit coordinates const CGPoint leftEyePos = CGPointApplyAffineTransform(faceFeature.leftEyePosition, transform); ... } ... }
You can download the sample from here and see the result is pretty much the same as the original tutorial. Only this time we didn't scramble with the coordinate system :)
In case, you didn't notice, the original example changes the whole window coordinate system causing its origin to be at the bottom left (like Cocoa in the Mac) hence the imageView appears at the bottom.
3 comments :
Is there any way to compare the Eye between different images? I want to compare whether Eye is open or closed? Similarly for Smile detection? Whether it is mouth open or now?
What about when you have a greater image and your ImageView content mode is set to UIViewContentModeScaleAspectFit?
I am not able to transform coordinates.
Any suggestion?
You have a 7th follower in me!
Thanks... I was so confused by the coordinate mismatch and you clarified it for me nicely.
Post a Comment