@property (nonatomic, strong) GPUImageVideoCamera *videoCamera;
@property (nonatomic, strong) GPUImageUIElement *element;
@property (nonatomic, strong) GPUImageView *filterView;
@property (nonatomic, strong) UIView *elementView;
@property (nonatomic, strong) UIImageView *capImageView;
@property (nonatomic, assign) CGRect faceBounds;
@property (nonatomic, strong) CIDetector *faceDetector;
// 人脸识别框
//@property (nonatomic, strong) UIView *faceView;
@property (nonatomic, assign) BOOL faceThinking;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset1280x720 cameraPosition:AVCaptureDevicePositionFront];
self.videoCamera.delegate = self;
// 竖屏方向
self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
self.videoCamera.horizontallyMirrorFrontFacingCamera = YES;
// 滤镜
GPUImageFilter *filter = [[GPUImageFilter alloc] init];
[self.videoCamera addTarget:filter];
self.element = [[GPUImageUIElement alloc] initWithView:self.elementView];
//混合滤镜
GPUImageAlphaBlendFilter *blendFilter = [[GPUImageAlphaBlendFilter alloc] init];
blendFilter.mix = 1.0;
[filter addTarget:blendFilter];
[self.element addTarget:blendFilter];
self.filterView = [[GPUImageView alloc] initWithFrame:self.view.frame];
self.filterView.center = self.view.center;
[self.view addSubview:self.filterView];
[blendFilter addTarget:self.filterView];
// 帧处理
__weak typeof (self) weakSelf = self;
[filter setFrameProcessingCompletionBlock:^(GPUImageOutput *output, CMTime time) {
__strong typeof (self) strongSelf = weakSelf;
// update capImageView's frame
CGRect rect = strongSelf.faceBounds;
CGSize size = strongSelf.capImageView.frame.size;
strongSelf.capImageView.frame = CGRectMake(rect.origin.x + (rect.size.width - size.width)/2, rect.origin.y - size.height, size.width, size.height);
[strongSelf.element update];
}];
[self.videoCamera startCameraCapture];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// 输出采样
- (void)willOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer {
if (!_faceThinking) {
CFAllocatorRef allocator = CFAllocatorGetDefault();
CMSampleBufferRef sbufCopyOut;
CMSampleBufferCreateCopy(allocator,sampleBuffer,&sbufCopyOut);
[self performSelectorInBackground:@selector(grepFacesForSampleBuffer:) withObject:CFBridgingRelease(sbufCopyOut)];
}
}
//处理
- (void)grepFacesForSampleBuffer:(CMSampleBufferRef)sampleBuffer{
_faceThinking = YES;
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, sampleBuffer, kCMAttachmentMode_ShouldPropagate);
CIImage *convertedImage = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer options:(__bridge NSDictionary *)attachments];
if (attachments)
CFRelease(attachments);
NSDictionary *imageOptions = nil;
UIDeviceOrientation curDeviceOrientation = [[UIDevice currentDevice] orientation];
int exifOrientation;
/* kCGImagePropertyOrientation values
The intended display orientation of the image. If present, this key is a CFNumber value with the same value as defined
by the TIFF and EXIF specifications -- see enumeration of integer constants.
The value specified where the origin (0,0) of the image is located. If not present, a value of 1 is assumed.
used when calling featuresInImage: options: The value for this key is an integer NSNumber from 1..8 as found in kCGImagePropertyOrientation.
If present, the detection will be done based on that orientation but the coordinates in the returned features will still be based on those of the image. */
// 位置
enum {
PHOTOS_EXIF_0ROW_TOP_0COL_LEFT = 1, // 1 = 0th row is at the top, and 0th column is on the left (THE DEFAULT).
PHOTOS_EXIF_0ROW_TOP_0COL_RIGHT = 2, // 2 = 0th row is at the top, and 0th column is on the right.
PHOTOS_EXIF_0ROW_BOTTOM_0COL_RIGHT = 3, // 3 = 0th row is at the bottom, and 0th column is on the right.
PHOTOS_EXIF_0ROW_BOTTOM_0COL_LEFT = 4, // 4 = 0th row is at the bottom, and 0th column is on the left.
PHOTOS_EXIF_0ROW_LEFT_0COL_TOP = 5, // 5 = 0th row is on the left, and 0th column is the top.
PHOTOS_EXIF_0ROW_RIGHT_0COL_TOP = 6, // 6 = 0th row is on the right, and 0th column is the top.
PHOTOS_EXIF_0ROW_RIGHT_0COL_BOTTOM = 7, // 7 = 0th row is on the right, and 0th column is the bottom.
PHOTOS_EXIF_0ROW_LEFT_0COL_BOTTOM = 8 // 8 = 0th row is on the left, and 0th column is the bottom.
};
BOOL isUsingFrontFacingCamera = FALSE;
AVCaptureDevicePosition currentCameraPosition = [self.videoCamera cameraPosition];
if (currentCameraPosition != AVCaptureDevicePositionBack)
{
isUsingFrontFacingCamera = TRUE;
}
switch (curDeviceOrientation) {
case UIDeviceOrientationPortraitUpsideDown: // Device oriented vertically, home button on the top
exifOrientation = PHOTOS_EXIF_0ROW_LEFT_0COL_BOTTOM;
break;
case UIDeviceOrientationLandscapeLeft: // Device oriented horizontally, home button on the right
if (isUsingFrontFacingCamera)
exifOrientation = PHOTOS_EXIF_0ROW_BOTTOM_0COL_RIGHT;
else
exifOrientation = PHOTOS_EXIF_0ROW_TOP_0COL_LEFT;
break;
case UIDeviceOrientationLandscapeRight: // Device oriented horizontally, home button on the left
if (isUsingFrontFacingCamera)
exifOrientation = PHOTOS_EXIF_0ROW_TOP_0COL_LEFT;
else
exifOrientation = PHOTOS_EXIF_0ROW_BOTTOM_0COL_RIGHT;
break;
case UIDeviceOrientationPortrait: // Device oriented vertically, home button on the bottom
default:
exifOrientation = PHOTOS_EXIF_0ROW_RIGHT_0COL_TOP;
break;
}
imageOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:exifOrientation] forKey:CIDetectorImageOrientation];
NSArray *features = [self.faceDetector featuresInImage:convertedImage options:imageOptions];
// get the clean aperture
// the clean aperture is a rectangle that defines the portion of the encoded pixel dimensions
// that represents image data valid for display.
CMFormatDescriptionRef fdesc = CMSampleBufferGetFormatDescription(sampleBuffer);
CGRect clap = CMVideoFormatDescriptionGetCleanAperture(fdesc, false /*originIsTopLeft == false*/);
[self GPUVCWillOutputFeatures:features forClap:clap andOrientation:curDeviceOrientation];
_faceThinking = NO;
}
// 输出
- (void)GPUVCWillOutputFeatures:(NSArray*)featureArray forClap:(CGRect)clap
andOrientation:(UIDeviceOrientation)curDeviceOrientation
{
dispatch_async(dispatch_get_main_queue(), ^{
CGRect previewBox = self.view.frame;
if (featureArray.count) {
self.capImageView.hidden = NO;
}
else {
self.capImageView.hidden = YES;
// [self.faceView removeFromSuperview];
// self.faceView = nil;
}
for ( CIFaceFeature *faceFeature in featureArray) {
// find the correct position for the square layer within the previewLayer
// the feature box originates in the bottom left of the video frame.
// (Bottom right if mirroring is turned on)
//Update face bounds for iOS Coordinate System
CGRect faceRect = [faceFeature bounds];
// flip preview width and height
CGFloat temp = faceRect.size.width;
faceRect.size.width = faceRect.size.height;
faceRect.size.height = temp;
temp = faceRect.origin.x;
faceRect.origin.x = faceRect.origin.y;
faceRect.origin.y = temp;
// scale coordinates so they fit in the preview box, which may be scaled
CGFloat widthScaleBy = previewBox.size.width / clap.size.height;
CGFloat heightScaleBy = previewBox.size.height / clap.size.width;
faceRect.size.width *= widthScaleBy;
faceRect.size.height *= heightScaleBy;
faceRect.origin.x *= widthScaleBy;
faceRect.origin.y *= heightScaleBy;
faceRect = CGRectOffset(faceRect, previewBox.origin.x, previewBox.origin.y);
//mirror
CGRect rect = CGRectMake(previewBox.size.width - faceRect.origin.x - faceRect.size.width, faceRect.origin.y, faceRect.size.width, faceRect.size.height);
if (fabs(rect.origin.x - self.faceBounds.origin.x) > 5.0) {
self.faceBounds = rect;
// if (self.faceView) {
// [self.faceView removeFromSuperview];
// self.faceView = nil;
// }
//
// // create a UIView using the bounds of the face
// self.faceView = [[UIView alloc] initWithFrame:self.faceBounds];
//
// // add a border around the newly created UIView
// self.faceView.layer.borderWidth = 1;
// self.faceView.layer.borderColor = [[UIColor redColor] CGColor];
//
// // add the new view to create a box around the face
// [self.view addSubview:self.faceView];
}
}
});
}
#pragma mark -
#pragma mark Getter
// 人脸识别
- (CIDetector *)faceDetector {
if (!_faceDetector) {
NSDictionary *detectorOptions = [[NSDictionary alloc] initWithObjectsAndKeys:CIDetectorAccuracyLow, CIDetectorAccuracy, nil];
_faceDetector = [CIDetector detectorOfType:CIDetectorTypeFace context:nil options:detectorOptions];
}
return _faceDetector;
}
- (UIView *)elementView {
if (!_elementView) {
_elementView = [[UIView alloc] initWithFrame:self.view.frame];
_capImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 160, 160)];
[_capImageView setImage:[UIImage imageNamed:@"cap.jpg"]];
[_elementView addSubview:_capImageView];
}
return _elementView;
}
感谢http://www.jianshu.com/u/131423baa3c6
GPUImage还有更多功能,这些都是略探窥镜,走的越远,见的越多,生命不息,代码不止!码农一枚,请多点赞