2013-06-24 14 views
6

Modyfikuję przykładową aplikację do wykrywania twarzy Apple SquareCam, dzięki czemu wykadruje twarz przed zapisaniem na rolce aparatu, zamiast rysować czerwony kwadrat wokół twarzy. Używam tego samego CGRect, aby wykonać kadrowanie, które było używane do rysowania czerwonego kwadratu. Jednak zachowanie jest inne. W trybie portretowym, jeśli twarz znajduje się w środkowej części ekranu, kadruje twarz zgodnie z oczekiwaniami (to samo miejsce, w którym znajdowałby się czerwony kwadrat). Jeśli twarz jest przesunięta w lewo lub w prawo, plon wydaje się być zawsze pobierany ze środka ekranu zamiast miejsca, w którym znajdowałby się czerwony kwadrat.Przycinanie do twarzy z wykrywaniem twarzy

Oto jabłka Kod oryginalny:

- (CGImageRef)newSquareOverlayedImageForFeatures:(NSArray *)features 
              inCGImage:(CGImageRef)backgroundImage 
             withOrientation:(UIDeviceOrientation)orientation 
              frontFacing:(BOOL)isFrontFacing 
{ 
    CGImageRef returnImage = NULL; 
    CGRect backgroundImageRect = CGRectMake(0., 0., CGImageGetWidth(backgroundImage), CGImageGetHeight(backgroundImage)); 
    CGContextRef bitmapContext = CreateCGBitmapContextForSize(backgroundImageRect.size); 
    CGContextClearRect(bitmapContext, backgroundImageRect); 
    CGContextDrawImage(bitmapContext, backgroundImageRect, backgroundImage); 
    CGFloat rotationDegrees = 0.; 

    switch (orientation) { 
     case UIDeviceOrientationPortrait: 
      rotationDegrees = -90.; 
      break; 
     case UIDeviceOrientationPortraitUpsideDown: 
      rotationDegrees = 90.; 
      break; 
     case UIDeviceOrientationLandscapeLeft: 
      if (isFrontFacing) rotationDegrees = 180.; 
      else rotationDegrees = 0.; 
      break; 
     case UIDeviceOrientationLandscapeRight: 
      if (isFrontFacing) rotationDegrees = 0.; 
      else rotationDegrees = 180.; 
      break; 
     case UIDeviceOrientationFaceUp: 
     case UIDeviceOrientationFaceDown: 
     default: 
      break; // leave the layer in its last known orientation 
    } 
    UIImage *rotatedSquareImage = [square imageRotatedByDegrees:rotationDegrees]; 

    // features found by the face detector 
    for (CIFaceFeature *ff in features) { 
     CGRect faceRect = [ff bounds]; 
     NSLog(@"faceRect=%@", NSStringFromCGRect(faceRect)); 
     CGContextDrawImage(bitmapContext, faceRect, [rotatedSquareImage CGImage]); 
    } 
    returnImage = CGBitmapContextCreateImage(bitmapContext); 
    CGContextRelease (bitmapContext); 

    return returnImage; 
} 

i moja wymiana:

- (CGImageRef)newSquareOverlayedImageForFeatures:(NSArray *)features 
              inCGImage:(CGImageRef)backgroundImage 
             withOrientation:(UIDeviceOrientation)orientation 
              frontFacing:(BOOL)isFrontFacing 
{ 
    CGImageRef returnImage = NULL; 

    //I'm only taking pics with one face. This is just for testing 
    for (CIFaceFeature *ff in features) { 
     CGRect faceRect = [ff bounds]; 
     returnImage = CGImageCreateWithImageInRect(backgroundImage, faceRect); 
    } 

    return returnImage; 
} 

Aktualizacja *

oparciu o wkład Wains, starałem się zrobić mój kod bardziej jak oryginał, ale wynik był sama:

- (NSArray*)extractFaceImages:(NSArray *)features 
       fromCGImage:(CGImageRef)sourceImage 
      withOrientation:(UIDeviceOrientation)orientation 
       frontFacing:(BOOL)isFrontFacing 
{ 
NSMutableArray *faceImages = [[[NSMutableArray alloc] initWithCapacity:1] autorelease]; 


CGImageRef returnImage = NULL; 
CGRect backgroundImageRect = CGRectMake(0., 0., CGImageGetWidth(sourceImage), CGImageGetHeight(sourceImage)); 
CGContextRef bitmapContext = CreateCGBitmapContextForSize(backgroundImageRect.size); 
CGContextClearRect(bitmapContext, backgroundImageRect); 
CGContextDrawImage(bitmapContext, backgroundImageRect, sourceImage); 
CGFloat rotationDegrees = 0.; 

switch (orientation) { 
    case UIDeviceOrientationPortrait: 
     rotationDegrees = -90.; 
     break; 
    case UIDeviceOrientationPortraitUpsideDown: 
     rotationDegrees = 90.; 
     break; 
    case UIDeviceOrientationLandscapeLeft: 
     if (isFrontFacing) rotationDegrees = 180.; 
     else rotationDegrees = 0.; 
     break; 
    case UIDeviceOrientationLandscapeRight: 
     if (isFrontFacing) rotationDegrees = 0.; 
     else rotationDegrees = 180.; 
     break; 
    case UIDeviceOrientationFaceUp: 
    case UIDeviceOrientationFaceDown: 
    default: 
     break; // leave the layer in its last known orientation 
} 

// features found by the face detector 
for (CIFaceFeature *ff in features) { 
    CGRect faceRect = [ff bounds]; 

    NSLog(@"faceRect=%@", NSStringFromCGRect(faceRect)); 

    returnImage = CGBitmapContextCreateImage(bitmapContext); 
    returnImage = CGImageCreateWithImageInRect(returnImage, faceRect); 
    UIImage *clippedFace = [UIImage imageWithCGImage:returnImage]; 
    [faceImages addObject:clippedFace]; 
} 

CGContextRelease (bitmapContext); 

return faceImages; 

}

Wziąłem trzy zdjęcia i zalogowany faceRect z tych wyników;

Zdjęcie zrobione z twarzą umieszczoną w pobliżu lewej krawędzi urządzenia. Przechwytywanie obrazu całkowicie strzela twarz w prawo: faceRect = {{972, 43,0312}, {673,312, 673,312}}

Pic zrobione z twarzy umieszczonej w środku urządzenia. Obraz przechwytywania jest dobry: faceRect = {{106,59, 536.625}, {68,25, 668.25}}

Zdjęcie zrobione z twarzą umieszczoną w pobliżu prawej krawędzi urządzenia. Przechwytywanie obrazu całkowicie strzela twarz lewej: faceRect = {{982,125, 999,844}, {804,938, 804,938}}

Wygląda więc na to, że "x" i "y" są odwrócone. Trzymam urządzenie w pozycji pionowej, ale faceRect wydaje się być oparty na pejzażu. Jednak nie mogę się dowiedzieć, jaka część oryginalnego kodu Jabłek wyjaśnia to. Kod orientacji w tej metodzie wydaje się wpływać tylko na sam obraz na czerwonym kwadracie.

+0

Czy spróbować moją odpowiedź, ale wciąż widzę problemów? – Wain

Odpowiedz

3

Należy zachować wszystkie oryginalnego kodu i wystarczy dodać jedną linię przed powrotem (z wariację umieścić generowanie obrazu wewnątrz pętli, jak jesteś przycinanie tylko pierwszą twarz):

returnImage = CGImageCreateWithImageInRect(returnImage, faceRect); 

ten umożliwia renderowanie obrazu z prawidłową orientacją, co oznacza, że ​​twarz rect będzie w odpowiednim miejscu.

+0

Logika orientacji w oryginalnym kodzie po prostu obraca obraz z czerwonego kwadratu (squarePNG.png), ponieważ zawiera słowa "góra" i "dół" zapakowane w obraz. Nie sądzę, żeby to miało znaczenie. W każdym razie próbowałem zrobić to bardziej jak oryginalny kod, ale wynik jest taki sam. Działa tylko wtedy, gdy twarz znajduje się bezpośrednio w (poziomym) środku urządzenia. – ax123man

+0

Obrót należy zastosować do obrazu z kamery. Po zastosowaniu obrotu obraz źródłowy jest "ustalony", a następnie można zastosować twarz prostą. – Wain

0

Masz do czynienia z tym problemem, ponieważ po zapisaniu obrazu jest on zapisywany w pionie. A pozycja faceRect nie pokrywa się dokładnie z twarzą. Możesz rozwiązać ten problem, modyfikując poistion faceRect w taki sposób, że jest on odwrócony w pionie w obrębie returnImage.

for (CIFaceFeature *ff in features) { 
     faceRect = [ff bounds]; 
     CGRect modifiedRect = CGRectFlipVertical(faceRect,CGRectMake(0,0,CGImageGetWidth(returnImage),CGImageGetHeight(returnImage))); 
     returnImage = CGImageCreateWithImageInRect(returnImage, modifiedRect); 
     UIImage *clippedFace = [UIImage imageWithCGImage:returnImage]; 
     [faceImages addObject:clippedFace]; 
    } 

CGRectFlipVertical(CGRect innerRect, CGRect outerRect) można zdefiniować jak tak,

CGRect CGRectFlipVertical(CGRect innerRect, CGRect outerRect) 
    { 
     CGRect rect = innerRect; 
     rect.origin.y = outerRect.origin.y + outerRect.size.height - (rect.origin.y + rect.size.height); 
     return rect; 
    } 
Powiązane problemy