2

カメラセッションがあり、バッファから画像を取得しています:

   -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
    CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);

    CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];        
    //rotate image 90°
    ciImage = [ciImage imageByApplyingTransform:CGAffineTransformMakeRotation(-M_PI/2.0)];
}

そして、私は画像にフィルターを適用しています。別のキューにフィルターを適用したいのですが、スレッドセーフではないため、画像を高速に取得すると、画像がブレンドされます(左から右に50/50、私は思う)、しかし、私はそれをスレッドセーフにしようとしています.NSLockまたはNSRecursiveLockを使用すると、画像がブレンドされるため、機能しません.

dispatch_async(filterQueue, ^{
            CIImage *scaleImage = [CIFilter filterWithName:@"CILanczosScaleTransform" keysAndValues:kCIInputImageKey, ciImage, @"inputScale", [NSNumber numberWithFloat:0.5], nil].outputImage;

            CGImageRef cgImage = [imageContext createCGImage:scaleImage fromRect:scaleImage.extent];
            [self.pictureArray addObject:[UIImage imageWithCGImage:cgImage]];
            CGImageRelease(cgImage);
        });

誰かが私を助けることができますか?コードをスレッドセーフにする方法についてあまり知識がありません

画像は次のようにブレンドされます: http://i.stack.imgur.com/wjrIl.png

4

2 に答える 2

2

まず、スレッド セーフではない上記のコードの唯一の部分は、 への呼び出しaddObject:です。addObject:呼び出しをメインスレッドに移動するか、pictureArrayアクセスをよりスレッドセーフにすることで、コードをスレッドセーフにすることができます。両方を見てみましょう。

通話を移動する

これは、ほぼ間違いなく、あなたがやりたい方法です。

dispatch_async(filterQueue, ^{
  CIImage *scaleImage = [CIFilter filterWithName:@"CILanczosScaleTransform" keysAndValues:kCIInputImageKey, ciImage, @"inputScale", [NSNumber numberWithFloat:0.5], nil].outputImage;

  CGImageRef cgImage = [imageContext createCGImage:scaleImage fromRect:scaleImage.extent];
  UIImage *image = [UIImage imageWithCGImage:cgImage];
  CGImageRelease(cgImage);
  cgImage = NULL; // Always set to NULL after you release something
  dispatch_async(dispatch_get_main_queue(), ^{
    [self.pictureArray addObject:image];
  });
});

バックグラウンド スレッドでできる限りのことを行っていることに注意してください。ファイナルaddObject:をメインスレッドに移動するだけです。

スレッドセーフな addImage:

バックグラウンド スレッドから頻繁に呼び出すaddObject:場合は、次のようにこれを独自のメソッドに巻き上げるとよいでしょう。

- (void)addImageOnMainThread:(UIImage *)image {
  dispatch_async(dispatch_get_main_queue(), ^{
    [self.pictureArray addObject:image];
  });
}

スレッドセーフのpictureArray:

これは、ミューテーションが非常に一般的で、ロックがパフォーマンスの問題である場合に適しています。バリアを使用すると、アクセスが非常に高速になりますが、コードが複雑になります。

- (UIImage *)imageAtIndex:(NSUInteger)index {
  UIImage *result = nil;
  dispatch_sync(self.pictureArrayQueue, 
    ^{ result = self.pictureArray[index]; });
  return result;
}

- (void)addImage:(UIImage *)image {
  dispatch_barrier_async(self.pictureArrayQueue, 
    ^{ [self.pictureArray addObject:image]; });
}

にアクセスするための新しいディスパッチ キューがここにあることに注意してくださいpictureArray。より多くのリーダーとライター (removeImageAtIndex:など) を作成できます。すべてのリーダーは を使用しますdispatch_sync。すべてのライターが使用しますdispatch_barrier_async。これにはより多くのコードが必要であり、pictureArrayこれらのメソッドを使用する場合を除き、直接アクセスしてはなりません。利点は、突然変異が非常に一般的である場合、はるかに高速であることです。

于 2012-12-10T14:56:36.530 に答える
0

@synchronized(self) を使用して、コード フラグメントをスレッド セーフにします。

例えば:

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)
   //The following code will be thread safe.
   @synchronized(self) {
      CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);

      CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];        
      //rotate image 90°
       ciImage = [ciImage imageByApplyingTransform:CGAffineTransformMakeRotation(-M_PI/2.0)];
    }
}

@synchronized ステートメントは、コードのこのセクションをロックして、一度に 1 つのスレッドに使用できるようにします。コードをシングルスレッドにする必要がある場所ならどこでも適用できます。

それが役立つことを願っています。

于 2012-12-10T14:08:18.603 に答える