Roi Mulia Roi Mulia - 5 months ago 100
Objective-C Question

OpenCV - Create CV_8UC3 (3 channel) Mat from BGRA output

I have an

AVCaptureSession
which output the
CMSampleBuffer
with
BGRA
pixels. I'm trying to create
Mat
object only from
BGR
, in the most efficient way, using the data pointers.

CVPixelBufferLockBaseAddress(imageBuffer,0);
void *baseaddress = CVPixelBufferGetBaseAddress(imageBuffer);
CGRect videoRect = CGRectMake(0.0f, 0.0f, CVPixelBufferGetWidth(imageBuffer), CVPixelBufferGetHeight(imageBuffer));
size_t bytesPerRow = (((CVPixelBufferGetBytesPerRow(imageBuffer)
cv::Mat frame(videoRect.size.height, videoRect.size.width,CV_8UC3 , baseaddress, bytesPerRow);


But it dosen't seems to work. Any suggestions?

Answer

Data in a Mat must be contiguous (with eventually some extra padding at the end of each row).

Your data are like:

row 0: B G R A B G R A ... B G R A <padding>
row 1: B G R A B G R A ... B G R A <padding>
...

And you can't read data like:

row 0: B G R - B G R - ... B G R - <padding>
row 1: B G R - B G R - ... B G R - <padding>
...

What you can do is wrap your BGRA data in a CV_8UC4 matrix (i.e. build a matrix header on your data):

cv::Mat frameBGRA (height, width, CV_8UC4, buffer);

and then copy only the bytes you need:

cv::Mat frameBGR;
cv::cvtColor(frameBGRA, frameBGR, cv::COLOR_BGRA2BGR);

Another solution would be to manually copy only the needed data into your BGR matrix (4 is the number of channels in BGRA):

// Create the BGR (3 channel) matrix
cv::Mat3b frameBGR(height, width);


for(int r = 0; r < height; ++r) {
    for(int c = 0; c < width; ++c) {

        // Copy only BGR
        frameBGR(r,c)[0] = buffer((r * c * 4) + (c * 4) + 0); // Blue
        frameBGR(r,c)[1] = buffer((r * c * 4) + (c * 4) + 1); // Green
        frameBGR(r,c)[2] = buffer((r * c * 4) + (c * 4) + 2); // Red

        // Don't consider Alpha channel
        // buffer((r * c * 4) + (c * 4) + 3); // Alpha
    }
}
Comments