Я знаю, что это довольно старый вопрос, но я недавно решал подобную проблему, и, возможно, кто-то может найти мое решение полезным.
Мне нужно было извлечь необработанные данные изображения из буфера изображения формата YCbCr, доставленного камерой iPhone (полученного из [AVCaptureVideoDataOutput.availableVideoCVPixelFormatTypes firstObject]), отбросить такую информацию, как заголовки, метаданные и т. Д., Чтобы передать их для дальнейшей обработки.
Кроме того, мне нужно было извлечь только небольшую область в центре захваченного видеокадра, поэтому потребовалась некоторая обрезка.
Мои условия позволили снимать видео только в любой горизонтальной ориентации, но когда устройство расположено в горизонтальной левой ориентации, изображение доставляется перевернутым, поэтому мне нужно было перевернуть его по обеим осям.
В случае, если изображение перевернуто, моя идея состояла в том, чтобы скопировать данные из буфера исходного изображения в обратном порядке и обратных байтов в каждой строке считанных данных, чтобы перевернуть изображение на обеих осях. Эта идея действительно работает, и, поскольку мне все равно нужно было копировать данные из исходного буфера, кажется, что при чтении с начала или в конце не наблюдается большого снижения производительности (конечно, большее изображение = более длительная обработка, но я имею дело с действительно небольшими числами) .
Я хотел бы знать, что другие думают об этом решении, и, конечно, некоторые советы о том, как улучшить код:
/// Lock pixel buffer
CVPixelBufferLockBaseAddress(imageBuffer, 0);
/// Address where image buffer starts
uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);
/// Read image parameters
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
/// See whether image is flipped upside down
BOOL isFlipped = (_previewLayer.connection.videoOrientation == AVCaptureVideoOrientationLandscapeLeft);
/// Calculate cropping frame. Crop to scanAreaSize (defined as CGSize constant elsewhere) from the center of an image
CGRect cropFrame = CGRectZero;
cropFrame.size = scanAreaSize;
cropFrame.origin.x = (width / 2.0f) - (scanAreaSize.width / 2.0f);
cropFrame.origin.y = (height / 2.0f) - (scanAreaSize.height / 2.0f);
/// Update proportions to cropped size
width = (size_t)cropFrame.size.width;
height = (size_t)cropFrame.size.height;
/// Allocate memory for output image data. W*H for Y component, W*H/2 for CbCr component
size_t bytes = width * height + (width * height / 2);
uint8_t *outputDataBaseAddress = (uint8_t *)malloc(bytes);
if(outputDataBaseAddress == NULL) {
/// Memory allocation failed, unlock buffer and give up
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
return NULL;
}
/// Get parameters of YCbCr pixel format
CVPlanarPixelBufferInfo_YCbCrBiPlanar *bufferInfo = (CVPlanarPixelBufferInfo_YCbCrBiPlanar *)baseAddress;
NSUInteger bytesPerRowY = EndianU32_BtoN(bufferInfo->componentInfoY.rowBytes);
NSUInteger offsetY = EndianU32_BtoN(bufferInfo->componentInfoY.offset);
NSUInteger bytesPerRowCbCr = EndianU32_BtoN(bufferInfo->componentInfoCbCr.rowBytes);
NSUInteger offsetCbCr = EndianU32_BtoN(bufferInfo->componentInfoCbCr.offset);
/// Copy image data only, skipping headers and metadata. Create single buffer which will contain Y component data
/// followed by CbCr component data.
/// Process Y component
/// Pointer to the source buffer
uint8_t *src;
/// Pointer to the destination buffer
uint8_t *destAddress;
/// Calculate crop rect offset. Crop offset is number of rows (y * bytesPerRow) + x offset.
/// If image is flipped, then read buffer from the end to flip image vertically. End address is height-1!
int flipOffset = (isFlipped) ? (int)((height - 1) * bytesPerRowY) : 0;
int cropOffset = (int)((cropFrame.origin.y * bytesPerRowY) + flipOffset + cropFrame.origin.x);
/// Set source pointer to Y component buffer start address plus crop rect offset
src = baseAddress + offsetY + cropOffset;
for(int y = 0; y < height; y++) {
/// Copy one row of pixel data from source into the output buffer.
destAddress = (outputDataBaseAddress + y * width);
memcpy(destAddress, src, width);
if(isFlipped) {
/// Reverse bytes in row to flip image horizontally
[self reverseBytes:destAddress bytesSize:(int)width];
/// Move one row up
src -= bytesPerRowY;
}
else {
/// Move to the next row
src += bytesPerRowY;
}
}
/// Calculate crop offset for CbCr component
flipOffset = (isFlipped) ? (int)(((height - 1) / 2) * bytesPerRowCbCr) : 0;
cropOffset = (int)((cropFrame.origin.y * bytesPerRowCbCr) + flipOffset + cropFrame.origin.x);
/// Set source pointer to the CbCr component offset + crop offset
src = (baseAddress + offsetCbCr + cropOffset);
for(int y = 0; y < (height / 2); y++) {
/// Copy one row of pixel data from source into the output buffer.
destAddress = (outputDataBaseAddress + (width * height) + y * width);
memcpy(destAddress, src, width);
if(isFlipped) {
/// Reverse bytes in row to flip image horizontally
[self reverseBytes:destAddress bytesSize:(int)width];
/// Move one row up
src -= bytesPerRowCbCr;
}
else {
src += bytesPerRowCbCr;
}
}
/// Unlock pixel buffer
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
/// Continue with image data in outputDataBaseAddress;