Локальный видеофайл CVPixelBufferRef для iOS Tensorflow Lite - PullRequest
0 голосов
/ 05 декабря 2018

Я использую пример камеры Tensorflow Lite на iOS.Он использует классификационную модель для классификации объектов, таких как мышь, клавиатура, кувшин с водой и т. Д. Он принимает входные данные камеры из

-(void)captureOutput:(AVCaptureOutput*)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection*)connection

CMSampleBufferRef, преобразованного в CVPixelBufferRef и затем переданного методу ProcessInputWithQuantizedModel ИЛИ ProcessInputWithFloatModel в соответствии с типом модели.,

В этом методе ввод обрабатывается с помощью следующего кода

// Preprocess the input image and feed the TFLite interpreter buffer for a float model.
void ProcessInputWithFloatModel(
uint8_t* input, float* buffer, int image_width, int image_height, int image_channels) {
for (int y = 0; y < wanted_input_height; ++y) {// y, loop on height
float* out_row = buffer + (y * wanted_input_width * wanted_input_channels);
  for (int x = 0; x < wanted_input_width; ++x) {
    const int in_x = (y * image_width) / wanted_input_width;
    const int in_y = (x * image_height) / wanted_input_height;
    uint8_t* input_pixel =
      input + (in_y * image_width * image_channels) + (in_x * image_channels);
    float* out_pixel = out_row + (x * wanted_input_channels);
      for (int c = 0; c < wanted_input_channels; ++c) {
        out_pixel[c] = (input_pixel[c] - input_mean) / input_std;
      }
    }
  }
}

И затем модель соответствующим образом классифицирует ввод.Он работает отлично.

Но

Теперь мне нужно воспроизвести локальный видеофайл и передать его в качестве входных данных для модели Tensorflow Lite.Я нашел следующий код для воспроизведения локального видеофайла и получения от него пиксельных буферов.

-(void)readFromLocalVideoFileUrl:(NSURL *)url{

//AVPlayeritem from resource url
AVPlayerItem *itemmm = [AVPlayerItem playerItemWithURL:url];

//AVPlayer from player item with url
AVPlayer *player = [AVPlayer playerWithPlayerItem:itemmm];

//Observer for player status
[[player currentItem] addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:NULL];

//Create Queue for processing video
dispatch_queue_attr_t qos = dispatch_queue_attr_make_with_qos_class(NULL, QOS_CLASS_BACKGROUND, -1);
dispatch_queue_t processingQueue = dispatch_queue_create("videoProcessingQueue", qos);

self.player = player;

__weak CameraExampleViewController *weakSelf = self;
self.timeobserverToken = [player addPeriodicTimeObserverForInterval:CMTimeMake(1, NSEC_PER_MSEC) queue:processingQueue usingBlock:^(CMTime time) {
    [weakSelf processbuffer];
}];

//avplayerlayer
AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:self.player];

CALayer* rootLayer = [previewView layer];
layer.frame = rootLayer.bounds;

[rootLayer setMasksToBounds:YES];
[previewLayer setFrame:[rootLayer bounds]];
[rootLayer addSublayer:layer];

[player play];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([object isKindOfClass:[AVPlayerItem class]]){
    AVPlayerItem *item = (AVPlayerItem *)object;

    if (item.status == AVPlayerItemStatusReadyToPlay){
        [self setOutput];
    }
}
}

- (void)setOutput{

if (self.output != NULL){
    return;
}

AVPlayerItem *videoitem = self.player.currentItem;
if (videoitem.status != AVPlayerItemStatusReadyToPlay){
    return;
}

NSDictionary* attributes = @{ (id)kCVPixelBufferPixelFormatTypeKey : [NSNumber numberWithInt:kCMPixelFormat_32BGRA] };
/*
 kCVPixelFormatType_32ARGB
kCVPixelFormatType_32BGRA
kCVPixelFormatType_32ABGR
kCVPixelFormatType_32RGBA
 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
 */

self.output = [[AVPlayerItemVideoOutput alloc]initWithPixelBufferAttributes:attributes];

[videoitem addOutput:self.output];

}

- (nullable CVPixelBufferRef)getPixelBuffer{

if (self.output != nil, self.player.currentItem != nil){

    CMTime time = self.player.currentItem.currentTime;

    if ([self.output hasNewPixelBufferForItemTime:time]){
        return [self.output copyPixelBufferForItemTime:time itemTimeForDisplay:nil];

    }else{
        return nil;
    }

}
return nil;
}

Я успешно получаю пиксельные буферы из этого кода.Но код вывода не классифицирует эти пиксельные буферы.В коде нет сбоев.Код вывода просто не распознает какой-либо объект из локальных видеобуферов.ИЛИ иногда дает неправильную классификацию.

AFAIK, это может быть код, который фактически обрабатывает ввод для Tensorflow.который на самом деле манипулирует пиксельными буферами, чтобы сделать его понятным для теневой модели потока.Поскольку вход камеры в альбомной ориентации оставлен по умолчанию.Я думаю, что следующий код манипулирует этими пиксельными буферами для выполнения некоторых вещей, связанных с ориентацией.

// Preprocess the input image and feed the TFLite interpreter buffer for a float model.
void ProcessInputWithFloatModel(
uint8_t* input, float* buffer, int image_width, int image_height, int image_channels) {
for (int y = 0; y < wanted_input_height; ++y) {// y, loop on height
float* out_row = buffer + (y * wanted_input_width * wanted_input_channels);
  for (int x = 0; x < wanted_input_width; ++x) {
    const int in_x = (y * image_width) / wanted_input_width;
    const int in_y = (x * image_height) / wanted_input_height;
    uint8_t* input_pixel =
      input + (in_y * image_width * image_channels) + (in_x * image_channels);
    float* out_pixel = out_row + (x * wanted_input_channels);
      for (int c = 0; c < wanted_input_channels; ++c) {
        out_pixel[c] = (input_pixel[c] - input_mean) / input_std;
      }
    }
  }
}

Где, как и локальные пиксельные буферы видеофайла, находятся в портретном режиме.Я проверил их, преобразовав их в UIImage.

Мне нужна некоторая помощь относительно моего понимания этой вещи ориентации.ИЛИ есть что-то еще, что я пропускаю.

...