Search code examples
c++opencvcomputer-visioncrop

Cropping an triangle from captured frame - OpenCV and C++


I have a video file from which I'm capturing a frames. I want to crop a triangle from captured frame and display it, but my program shows just a source frame.

Here is my code:

cv::Mat Detector::cropRegionOfInterest(cv::Mat& frame)
{
    cv::Point corners[1][3];
    corners[0][0] = cv::Point(0, frameHeight);
    corners[0][1] = cv::Point(frameWidth, frameHeight);
    corners[0][2] = cv::Point(frameWidth / 2, frameHeight / 2);

    const cv::Point* cornerList[1] = { corners[0] };

    int numPoints = 3;
    int numPolygons = 1;

    cv::Mat mask(frame.size(), CV_8UC1, cv::Scalar(0, 0, 0));
    cv::fillPoly(mask, cornerList, &numPoints, numPolygons, cv::Scalar(255, 255, 255), 8);

    cv::Mat result(frame.size(), CV_8UC3);
    cv::bitwise_and(frame, mask, result);

    return result;
}

Instead of displaying source frame I want it to display cropped triangle.


Solution

  • Since you're using CV_8UC3 as the type of result, I'm assuming (see the Edit at the end of the answer if that's not the case) that the input image frame also has 3 channels. In that case, I'm a bit surprised that you can even see the non-cropped image, as running your code simply throws an exception on my machine at the call to bitwise_and:

    OpenCV(3.4.1) Error: Sizes of input arguments do not match
    

    From the documentation, it seems to me that you can't mix different input and mask types. A quick and dirty solution is to split the input image into a vector of three channels, call bitwise_and for each of them, and then merge them back. The code below works for me:

    #include <stdio.h>
    #include <opencv2/opencv.hpp>
    
    using namespace cv;
    
    
    cv::Mat cropRegionOfInterest(cv::Mat& frame)
    {
    
        const int frameWidth=frame.cols-1;
        const int frameHeight=frame.rows-1;
        cv::Point corners[1][3];
        corners[0][0] = cv::Point(0, frameHeight);
        corners[0][1] = cv::Point(frameWidth, frameHeight);
        corners[0][2] = cv::Point(frameWidth / 2, frameHeight / 2);
    
        const cv::Point* cornerList[1] = { corners[0] }; 
    
        int numPoints = 3; 
        int numPolygons = 1; 
    
        cv::Mat mask(frame.rows,frame.cols, CV_8UC1, cv::Scalar(0, 0, 0));
        cv::fillPoly(mask, cornerList, &numPoints, numPolygons, cv::Scalar(255, 255, 255), 8);
    
        std::vector<cv::Mat> src_channels;
        std::vector<cv::Mat> result_channels;
        cv::split(frame,src_channels);
    
        for(int idx=0;idx<3;++idx)
        {  
          result_channels.emplace_back(frame.rows,frame.cols,CV_8UC1);
          cv::bitwise_and(src_channels[idx], mask,result_channels[idx]);
    
        }  
        cv::Mat result;
        cv::merge(result_channels,result);
        return result;
    }
    
    
    int main(int argc, char** argv )
    {
        if ( argc != 2 )
        {  
            printf("usage: DisplayImage.out <Image_Path>\n");
            return -1;
        }  
    
        Mat image;
        image = imread( argv[1], 1 ); 
    
        if ( !image.data )
        {  
            printf("No image data \n");
            return -1;
        }  
    
        cv::Mat cropped=cropRegionOfInterest(image);
    
        namedWindow("cropped Image", WINDOW_AUTOSIZE ); 
        imshow("cropped Image", cropped);
    
        waitKey(0);
    
        return 0; 
    }
    

    Edit: From your comments it seems that frame is actually grayscale. In that case, nevermind all the code above, and just change cv::Mat result(frame.size(), CV_8UC3); to

    cv::Mat result(frame.rows,frame.cols,CV_8UC1);
    

    in your original code.