Доступ к пикселям за пределами CVPixelBuffer, который был расширен с помощью Padding - PullRequest
0 голосов
/ 12 декабря 2018

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

    let paddingLeft = abs(min(cropX, 0))
    let paddingRight = max((cropX + cropWidth) - (srcWidth - 1), 0)
    let paddingBottom = max((cropY + cropHeight) - (srcHeight - 1), 0)
    let paddingTop = abs(min(cropY, 0))

    let attr = [kCVPixelBufferExtendedPixelsLeftKey: paddingLeft*40 + 1 as CFNumber,
                kCVPixelBufferExtendedPixelsTopKey: paddingTop*40 + 1 as CFNumber,
                kCVPixelBufferExtendedPixelsRightKey: paddingRight*40 + 1 as CFNumber,
                kCVPixelBufferExtendedPixelsBottomKey: paddingBottom*40 + 1 as CFNumber]

    guard kCVReturnSuccess == CVPixelBufferCreateWithBytes(kCFAllocatorDefault, srcWidth, srcHeight, pixelFormat, srcData, srcBytesPerRow, nil, nil, attr as CFDictionary, &paddedSrcPixelBuffer) else {
            print("failed to allocate a new padded pixel buffer")
            return nil
        }

С расширенным CVPixelBuffer, доступ к данным вне CVPixelBuffer (например, когда x, y отрицательный или больше, чем ширина / высота)из буфера) должно быть определено поведение на основе моего понимания.Тем не менее следующий фрагмент кода дает сбой в последней строке внутри VImageScale_ARGB8888 с кодом EXC_BAD_ACCESS 1.

Это, вероятно, означает, что данные, к которым осуществляется доступ, не отображаются.

guard let paddedSrcData = CVPixelBufferGetBaseAddress(paddedSrcPixelBuffer) else {
            print("Error: could not get padded pixel buffer base address")
            return nil
        }

srcBuffer = vImage_Buffer(data: paddedSrcData.advanced(by: offset),
                              height: vImagePixelCount(cropHeight),
                              width: vImagePixelCount(cropWidth),
                              rowBytes: srcBytesPerRow)
let destBytesPerRow = scaleWidth*4
let destData = malloc(scaleHeight*destBytesPerRow)

var destBuffer = vImage_Buffer(data: destData,
                                   height: vImagePixelCount(scaleHeight),
                                   width: vImagePixelCount(scaleWidth),
                                   rowBytes: destBytesPerRow)

let vImageFlags: vImage_Flags = vImage_Flags(kvImageEdgeExtend)
let error = vImageScale_ARGB8888(&srcBuffer, &destBuffer, nil, vImageFlags) // crashes here due to EXC_BAD_ACCESS Code: 1

Большое спасибо!

1 Ответ

0 голосов
/ 18 декабря 2018

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

public func resizePixelBuffer(_ srcPixelBuffer: CVPixelBuffer,
                              cropX: Int,
                              cropY: Int,
                              cropWidth: Int,
                              cropHeight: Int,
                              scaleWidth: Int,
                              scaleHeight: Int) -> CVPixelBuffer? {
    let flags = CVPixelBufferLockFlags(rawValue: 0)
    let pixelFormat = CVPixelBufferGetPixelFormatType(srcPixelBuffer)
    guard kCVReturnSuccess == CVPixelBufferLockBaseAddress(srcPixelBuffer, flags) else {
        return nil
    }
    defer { CVPixelBufferUnlockBaseAddress(srcPixelBuffer, flags) }

    guard let srcData = CVPixelBufferGetBaseAddress(srcPixelBuffer) else {
        print("Error: could not get pixel buffer base address")
        return nil
    }

    let srcHeight = CVPixelBufferGetHeight(srcPixelBuffer)
    let srcWidth = CVPixelBufferGetWidth(srcPixelBuffer)
    let srcBytesPerRow = CVPixelBufferGetBytesPerRow(srcPixelBuffer)
    let offset = cropY*srcBytesPerRow + cropX*4

    var srcBuffer: vImage_Buffer!
    var paddedSrcPixelBuffer: CVPixelBuffer!

    if (cropX < 0 || cropY < 0 || cropX + cropWidth > srcWidth || cropY + cropHeight > srcHeight) {
        let paddingLeft = abs(min(cropX, 0))
        let paddingRight = max((cropX + cropWidth) - (srcWidth - 1), 0)
        let paddingBottom = max((cropY + cropHeight) - (srcHeight - 1), 0)
        let paddingTop = abs(min(cropY, 0))

        let paddedHeight = paddingTop + srcHeight + paddingBottom
        let paddedWidth = paddingLeft + srcWidth + paddingRight

        guard kCVReturnSuccess == CVPixelBufferCreate(kCFAllocatorDefault, paddedWidth, paddedHeight, pixelFormat, nil, &paddedSrcPixelBuffer) else {
            print("failed to allocate a new padded pixel buffer")
            return nil
        }

        guard kCVReturnSuccess == CVPixelBufferLockBaseAddress(paddedSrcPixelBuffer, flags) else {
            return nil
        }

        guard let paddedSrcData = CVPixelBufferGetBaseAddress(paddedSrcPixelBuffer) else {
            print("Error: could not get padded pixel buffer base address")
            return nil
        }

        let paddedBytesPerRow = CVPixelBufferGetBytesPerRow(paddedSrcPixelBuffer)
        for yIndex in paddingTop..<srcHeight+paddingTop {
            let dstRowStart = paddedSrcData.advanced(by: yIndex*paddedBytesPerRow).advanced(by: paddingLeft*4)
            let srcRowStart = srcData.advanced(by: (yIndex - paddingTop)*srcBytesPerRow)
            dstRowStart.copyMemory(from: srcRowStart, byteCount: srcBytesPerRow)
        }

        let paddedOffset = (cropY + paddingTop)*paddedBytesPerRow + (cropX + paddingLeft)*4
        srcBuffer = vImage_Buffer(data: paddedSrcData.advanced(by: paddedOffset),
                                  height: vImagePixelCount(cropHeight),
                                  width: vImagePixelCount(cropWidth),
                                  rowBytes: paddedBytesPerRow)

    } else {
        srcBuffer = vImage_Buffer(data: srcData.advanced(by: offset),
                                  height: vImagePixelCount(cropHeight),
                                  width: vImagePixelCount(cropWidth),
                                  rowBytes: srcBytesPerRow)
    }

    let destBytesPerRow = scaleWidth*4
    guard let destData = malloc(scaleHeight*destBytesPerRow) else {
        print("Error: out of memory")
        return nil
    }
    var destBuffer = vImage_Buffer(data: destData,
                                   height: vImagePixelCount(scaleHeight),
                                   width: vImagePixelCount(scaleWidth),
                                   rowBytes: destBytesPerRow)

    let vImageFlags: vImage_Flags = vImage_Flags(kvImageEdgeExtend)
    let error = vImageScale_ARGB8888(&srcBuffer, &destBuffer, nil, vImageFlags)
    if error != kvImageNoError {
        print("Error:", error)
        free(destData)
        return nil
    }

    let releaseCallback: CVPixelBufferReleaseBytesCallback = { _, ptr in
        if let ptr = ptr {
            free(UnsafeMutableRawPointer(mutating: ptr))
        }
    }

    var dstPixelBuffer: CVPixelBuffer?
    let status = CVPixelBufferCreateWithBytes(nil, scaleWidth, scaleHeight,
                                              pixelFormat, destData,
                                              destBytesPerRow, releaseCallback,
                                              nil, nil, &dstPixelBuffer)
    if status != kCVReturnSuccess {
        print("Error: could not create new pixel buffer")
        free(destData)
        return nil
    }

    if paddedSrcPixelBuffer != nil {
        CVPixelBufferUnlockBaseAddress(paddedSrcPixelBuffer, flags)
    }


    return dstPixelBuffer
}
...