Разработчик в камере. Видео

    Apple постоянно держит разработчиков в тонусе.
    Фронтальные камеры на iPad и iPhone родили новый виток идей у создателей сиюминутных приложений. Я тоже провел небольшое исследование двухкамерных телефонов и приглашаю нажать на кнопку, кому это интересно.

    image

    Видеозахват в iOS 4.3+ стал простым, как апельсин.


    Четыре вызова – и Вы обладатель пикселей, полученных с любой из двух камер iPhone.

    Чуть-чуть кода, дизайнерам не читать


    Заводим наш HabrahabrView
    класс HabrahabrView
    @class CaptureSessionManager;
    
    @interface HabrahabrView : UIView <AVCaptureVideoDataOutputSampleBufferDelegate> {
        CaptureSessionManager *captureFront;
        CaptureSessionManager *captureBack;
        UIImageView *face;
    }
    


    В теле класса вводим обязательную функцию captureOutput, куда будет приходить 20 раз в секунду картинки с видеокамеры.
    функция captureOutput
    
    -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
    
        CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
        // Lock the base address of the pixel buffer
        CVPixelBufferLockBaseAddress(imageBuffer, 0); 
        //    Get the number of bytes per row for the pixel buffer
        //    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); 
        // Get the number of bytes per row for the pixel buffer
        size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
        // Get the pixel buffer width and height
        size_t width = CVPixelBufferGetWidth(imageBuffer); 
        size_t height = CVPixelBufferGetHeight(imageBuffer); 
        unsigned char* pixel = (unsigned char *)CVPixelBufferGetBaseAddress(imageBuffer);
        CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();  
        CGContextRef context=CGBitmapContextCreate(pixel, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little|kCGImageAlphaPremultipliedFirst);  
        CGImageRef image=CGBitmapContextCreateImage(context);  
        CGContextRelease(context);  
        CGColorSpaceRelease(colorSpace);  
        UIImage *resultUIImage=[UIImage imageWithCGImage:image];  
        CGImageRelease(image); 
        CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
        [resultUIImage retain];
        [self performSelectorOnMainThread:@selector(cameraCaptureGotFrame:)
                               withObject:resultUIImage waitUntilDone:NO];
    
    }
    
    - (void) cameraCaptureGotFrame:(UIImage*)image
    {
        face.image = [self fixOrientation:image];
        // decrement ref count
        [image release];
    }
    


    Теперь, когда вся предварительная работа закончена, заводим картинку на экране, куда будет выводится наше видео
    Небольшой ImageView 58 на 70
        face = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 58, 70)];
        [self addSubview:face];
    


    Вот так подключаем заднюю камеру
    Задняя камера готова
        [self setCaptureBack:[[[CaptureSessionManager alloc] init] autorelease]];
        [[self captureBack] addVideoInput:2  PView:self];
        [[self captureBack] addVideoPreviewLayer];
        [[[self captureBack] captureSession] setSessionPreset:AVCaptureSessionPresetLow];
    


    А вот так переднюю
    Передняя камера готова
        [self setCaptureFront:[[[CaptureSessionManager alloc] init] autorelease]];
        [[self captureFront] addVideoInput:1  PView:self];
        [[self captureFront] addVideoPreviewLayer];
        [[[self captureFront] captureSession] setSessionPreset:AVCaptureSessionPresetLow];
    


    Оформим все функции в отдельный класс.
    Здесь скучный код, не читайте
    #import "CaptureSessionManager.h"
    
    @implementation CaptureSessionManager
    @synthesize captureSession;
    @synthesize previewLayer;
    
    - (id)init {
    	if ((self = [super init])) {
    		[self setCaptureSession:[[AVCaptureSession alloc] init]];
    	}
    	return self;
    }
    
    - (void)addVideoPreviewLayer {
    	[self setPreviewLayer:[[[AVCaptureVideoPreviewLayer alloc] initWithSession:[self captureSession]] autorelease]];
    	[[self previewLayer] setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    }
    
    - (void)addVideoInput:(int)camType PView:(HabrahabrView*) habraview {
            NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];  
            AVCaptureDevice *videoDevice = nil;  
            NSInteger side = (camType==1) ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack;
        for (AVCaptureDevice *device in videoDevices)  
            {  
                if (device.position == side)  
                {  
                    videoDevice = device;  
                    break;  
                }  
            }  
    	if (videoDevice == nil) {
            videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        }
    	if (videoDevice) {
    		NSError *error;
    		AVCaptureDeviceInput *videoIn = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
    		if (!error) {
    			if ([[self captureSession] canAddInput:videoIn])
    				[[self captureSession] addInput:videoIn];
    			else
    				NSLog(@"Couldn't add video input");	
                // Set the output  
                AVCaptureVideoDataOutput* videoOutput = [[AVCaptureVideoDataOutput alloc] init];  
                // create a queue to run the capture on  
                dispatch_queue_t captureQueue=dispatch_queue_create("catpureQueue", NULL);  
                // setup our delegate  
                [videoOutput setSampleBufferDelegate:habraview queue:captureQueue];  
                // configure the pixel format  
                videoOutput.videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey,  
                                             nil];  
                if ([[self captureSession] canAddOutput:videoOutput])
    				[[self captureSession] addOutput:videoOutput];
    			else
    				NSLog(@"Couldn't add video ouput");		
            }
    		else
    			NSLog(@"Couldn't create video input");
    	}
    	else
    		NSLog(@"Couldn't create video capture device");
    }
    
    
    - (void)dealloc {
    	[[self captureSession] stopRunning];
    	[previewLayer release], previewLayer = nil;
    	[captureSession release], captureSession = nil;
    	[super dealloc];
    }
    @end
    
    Готово.

    Работа с камерами


    Включаем переднюю камеру
        [[captureFront captureSession] startRunning];
    


    Все работает. Как видно из кода видео можно дергать в трех разрешениях. Я выбираю для скорости самое маленькое AVCaptureSessionPresetLow (примерно 144 на 192 пикселя ). Для наших нужд этого хватает, заодно и фильтр картинки происходит бесплатный.

    Как теперь включить заднюю камеру? Остановить переднюю и включить заднюю
        [[captureFront captureSession] stopRunning];
        [[captureBack captureSession] startRunning];
    


    Сразу захотелось включить обе. Увы. Невозможно. Я попытался быстро переключать камеры, но существует задержка примерно в треть секунды, которая ничего, кроме раздражения не вызывает.

    Мечту о наложении двух изображении онлайн пришлось убить.



    Фруктожорка


    Но не убить мечту о каком-нибудь дурацком приложении. Я решил быстро сотворить фруктожорку — изображение с фронтальной камеры реальное, апельсины — виртуальные.

    Апельсины по очереди падают на экран. Их надо захватить ртом и съесть. Кто быстрее съест 7 апельсинов — тому приз.

    Осталось написать процедуру распознавания рта.
    Распознать рот — занятие примитивное, даже я, человек впадающий в тоску от слов ООП, ОпенСиВи, сделал эту функцию быстро. Не используя ни ООП, ни ОпенСиВи.
    Подсказка — Вы знаете положение апельсина и пляшете от него.

    Ага, воскликнет внимательный читатель — процедура распознавания отлажена исключительно на авторе при освещении его рабочего места. Вы правы, но судя по фотографиям пользователей, приложение успешно работает от Китая до Бразилии, от офисов до квартир.

    Конечно, был велик соблазн стащить фотографию пользователя в момент поедания им 7-ого апельсина. Я всегда поддаюсь соблазнам, ведь они могут не повториться. Спокойно! Для любителей нравственности я добавил кнопку — Разрешить отправлять свое фото. По умолчанию — выключена.

    Я отправляю небольшую картинку игрока размером 58 на 70 и тайком любуюсь. Попадаются очень забавные фотки. Ржу порой минуты по 3. Есть и симпатичные особы.
    Ради Бога, не болтайте про фото, храните корпоративные тайны.

    Совсем забыл. Мой рекорд — 12 секунд. Готовлюсь к Олимпиаде.

    До встречи в Лондоне, друзья.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 11

      +6
      Идея, как всегда, на отлично! =)
      А фотками бразильцев поделитесь, будет интересно посмотреть )
        +6
        Лучше бразильянок.
        +2
        Идея шикарная, как и реализация, планируете выпустить на Android?
          +1
          Я удовлетворил свое любопытство при помощи iOS, на андроид запала не хватит. Пусть профи Явы резвятся на зеленом поле. Лично я никакой коммерческой выгоды из этой веселой затеи не вижу.
        • UFO just landed and posted this here
            +1
            На такое можно и госзаказ получить!
            0
            а в сторе уже есть?
              0
              Так а как рот-то распознавать, чем картинки анализируются?
                0
                Возникает подозрение, что просто ищется чёрный овал. Едва ли кто-то направит в рот фонарик, чтобы открытый рот превратился на фото во что-то другое.
                  +2
                  Алгоритм таков — от центра апельсина по матрице изображения проводится 8 лучей до точки отстоящей на 2 радиуса апельсина. Профили лучей анализируются на предмет резкого скачка яркости. Если скачок лежит в диапазоне больше радиуса апельсина — фрукт во рту.
                    0
                    Видимо я намного дальше Вас от анализа картинок, решительно не представляю как это реализовать. Штатные средства или сторонние либы?

                Only users with full accounts can post comments. Log in, please.