Objective c 如何使用Accelerate框架将iOS摄像头图像转换为灰度?

Objective c 如何使用Accelerate框架将iOS摄像头图像转换为灰度?,objective-c,iphone,image-processing,accelerate-framework,vimage,Objective C,Iphone,Image Processing,Accelerate Framework,Vimage,看起来这应该比我发现的要简单 我在标准委托方法中返回了一个AVFoundation框架: - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection 我想使用Accelerate.Framework将帧转换为灰度 框架中有一系列

看起来这应该比我发现的要简单

我在标准委托方法中返回了一个
AVFoundation
框架:

- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
       fromConnection:(AVCaptureConnection *)connection
我想使用
Accelerate.Framework
将帧转换为灰度

框架中有一系列转换方法,包括
vImageConvert\u RGBA8888toPlanar8()
,看起来可能是我想看到的,但是,我找不到任何如何使用它们的示例

到目前为止,我有以下代码:

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

      @autoreleasepool {
            CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
            /*Lock the image buffer*/
            CVPixelBufferLockBaseAddress(imageBuffer,0);
            /*Get information about the image*/
            uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);
            size_t width = CVPixelBufferGetWidth(imageBuffer);
            size_t height = CVPixelBufferGetHeight(imageBuffer);
            size_t stride = CVPixelBufferGetBytesPerRow(imageBuffer);

            // vImage In
            Pixel_8 *bitmap = (Pixel_8 *)malloc(width * height * sizeof(Pixel_8));
            const vImage_Buffer inImage = { bitmap, height, width, stride };

            //How can I take this inImage and convert it to greyscale?????
            //vImageConvert_RGBA8888toPlanar8()??? Is the correct starting format here??
      }    
}
所以我有两个问题: (1) 在上面的代码中,
RBGA8888
是正确的起始格式吗?
(2) 我如何才能真正地使
加速.Framework
调用转换为灰度?

这里有一个更简单的选项。如果将“摄影机获取格式”更改为YUV,则您已经有了一个可以随意使用的灰度帧。设置数据输出时,请使用以下方法:

dataOutput.videoSettings = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) };
然后,可以使用以下方法访问捕获回调中的Y平面:

CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
uint8_t *yPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);

... do stuff with your greyscale camera image ...

CVPixelBufferUnlockBaseAddress(pixelBuffer);

vImage方法是使用
VimageMatrix多重平面8
和1x3矩阵。
vImageConvert_RGBA8888toPlanar8
是用于将一个rgba888缓冲区转换为4个平面缓冲区的函数。这些由
vImageMatrixMultiply_Planar8
使用<代码>vImageMatrixMultiply_argb888也将在一次过程中完成,但结果中灰色通道将与其他三个通道交错<代码>vImageConvert_RGBA8888toPlanar8本身不做任何计算。它所做的只是将交错图像分割成不同的图像平面

如果您还需要调整gamma,那么很可能
vImageConvert\u AnyToAny()
是一个简单的选择。它将完成从RGB格式到灰度颜色空间的完全颜色管理转换。见vImage_实用程序.h


不过我更喜欢柏油。它只会让您不得不手动管理亮度的颜色(如果您愿意)。

如果您需要使用BGRA视频流,您可以使用这种出色的转换

这是您需要执行的功能:

void neon_convert (uint8_t * __restrict dest, uint8_t * __restrict src, int numPixels)
      {
          int i;
          uint8x8_t rfac = vdup_n_u8 (77);
          uint8x8_t gfac = vdup_n_u8 (151);
          uint8x8_t bfac = vdup_n_u8 (28);
          int n = numPixels / 8;

          // Convert per eight pixels
          for (i=0; i < n; ++i)
          {
              uint16x8_t  temp;
              uint8x8x4_t rgb  = vld4_u8 (src);
              uint8x8_t result;

              temp = vmull_u8 (rgb.val[0],      bfac);
              temp = vmlal_u8 (temp,rgb.val[1], gfac);
              temp = vmlal_u8 (temp,rgb.val[2], rfac);

              result = vshrn_n_u16 (temp, 8);
              vst1_u8 (dest, result);
              src  += 8*4;
              dest += 8;
          }
      }
void neon\u convert(uint8\u t*\u restrict dest,uint8\u t*\u restrict src,int numPixels)
{
int i;
uint8x8_t rfac=vdup_n_u8(77);
uint8x8_t gfac=vdup_n_u8(151);
uint8x8\u t bfac=vdup\u u8(28);
int n=numPixels/8;
//每八像素转换
对于(i=0;i
链接中提供了更多优化(使用汇编)

使用Accelerate vImage将BGRA图像转换为灰度 此方法旨在说明如何将BGR图像转换为灰度。你的图像可能是RGBA格式的,你需要相应地调整矩阵,但是相机输出BGRA,所以我在这里使用它。矩阵中的值与OpenCV中使用的值相同,您还可以使用其他值。我假设您为结果分配了适当的内存量。在灰度情况下,它仅为BGRA使用的1通道或1/4内存。如果有人发现此代码有问题,请留下评论

业绩说明 以这种方式转换为灰度可能不是最快的。您应该检查环境中任何方法的性能。Brad Larson的可能更快,甚至OpenCV的
cvtColor
。在任何情况下,您都希望删除对malloc的调用并释放中间缓冲区,并在应用程序生命周期中对其进行管理。否则,函数调用将由malloc和free控制。苹果的文件建议尽可能重复使用整个vImage_缓冲区

您还可以阅读有关使用解决相同问题的内容

最后,最快的方法是根本不转换。如果您从设备摄像机获取图像数据,则设备摄像机本机采用
kCVPixelFormatType\u 420YpCbCr8BiPlanarFullRange
格式。也就是说,获取第一个平面的数据(Y通道,luma)是获取灰度的最快方法

BGRA到灰度 CLPBasicVideoFrame.h-供参考 我通过了灰度转换,但是当我在网上找到这本书时,质量有问题。我亲自拿了一份,里面有很多宝石,虽然代码有点乱。从好的方面来看,这是一本价格非常合理的电子书


我对那个矩阵很好奇。我花了好几个小时玩弄它,想弄清楚该怎么安排。我本以为这些值应该在对角线上,但Instant OpenCV的人把它放在上面。

从某种意义上说,是的。使用YUV的问题是,现在您有两个问题。最初的问题和其他一切都需要RGB数据的问题。虽然在有限的情况下,这可能就足够了。所以op询问转换为灰度,你的回答只是演示如何获得像素缓冲区地址。请帮助我解决这个问题。。
- (void)convertBGRAFrame:(const CLPBasicVideoFrame &)bgraFrame toGrayscale:(CLPBasicVideoFrame &)grayscaleFrame
{
    vImage_Buffer bgraImageBuffer = {
        .width = bgraFrame.width,
        .height = bgraFrame.height,
        .rowBytes = bgraFrame.bytesPerRow,
        .data = bgraFrame.rawPixelData
    };

    void *intermediateBuffer = malloc(bgraFrame.totalBytes);
    vImage_Buffer intermediateImageBuffer = {
        .width = bgraFrame.width,
        .height = bgraFrame.height,
        .rowBytes = bgraFrame.bytesPerRow,
        .data = intermediateBuffer
    };

    int32_t divisor = 256;
//    int16_t a = (int16_t)roundf(1.0f * divisor);
    int16_t r = (int16_t)roundf(0.299f * divisor);
    int16_t g = (int16_t)roundf(0.587f * divisor);
    int16_t b = (int16_t)roundf(0.114f * divisor);
    const int16_t bgrToGray[4 * 4] = { b, 0, 0, 0,
                                       g, 0, 0, 0,
                                       r, 0, 0, 0,
                                       0, 0, 0, 0 };

    vImage_Error error;
    error = vImageMatrixMultiply_ARGB8888(&bgraImageBuffer, &intermediateImageBuffer, bgrToGray, divisor, NULL, NULL, kvImageNoFlags);
    if (error != kvImageNoError) {
        NSLog(@"%s, vImage error %zd", __PRETTY_FUNCTION__, error);
    }

    vImage_Buffer grayscaleImageBuffer = {
        .width = grayscaleFrame.width,
        .height = grayscaleFrame.height,
        .rowBytes = grayscaleFrame.bytesPerRow,
        .data = grayscaleFrame.rawPixelData
    };

    void *scratchBuffer = malloc(grayscaleFrame.totalBytes);
    vImage_Buffer scratchImageBuffer = {
        .width = grayscaleFrame.width,
        .height = grayscaleFrame.height,
        .rowBytes = grayscaleFrame.bytesPerRow,
        .data = scratchBuffer
    };

    error = vImageConvert_ARGB8888toPlanar8(&intermediateImageBuffer, &grayscaleImageBuffer, &scratchImageBuffer, &scratchImageBuffer, &scratchImageBuffer, kvImageNoFlags);
    if (error != kvImageNoError) {
        NSLog(@"%s, vImage error %zd", __PRETTY_FUNCTION__, error);
    }
    free(intermediateBuffer);
    free(scratchBuffer);
}
typedef struct
{
    size_t width;
    size_t height;
    size_t bytesPerRow;
    size_t totalBytes;
    unsigned long pixelFormat;
    void *rawPixelData;
} CLPBasicVideoFrame;