Home > OS >  Remove rectangle from image
Remove rectangle from image

Time:05-17

How to remove the outer rectangle from this image? If there should be other rectangles inside the outer rectangle they should stay untoched. Only the outer rectagnel should be removed

Below I have some code which can detect the entire rectangle but how to mask only the outer rectangle and remove it?

Have attached the input and what I expect as output

OpenCV 4.5.1

Input

Output

// g   -Wall -O3 -std=c  17 test.cpp -o test `pkg-config opencv4 --cflags --libs`

#include <iostream>
#include <string>
#include <opencv4/opencv2/opencv.hpp>

const int
    HIERARCHY_NEXT              = 0,
    HIERARCHY_PREV              = 1,
    HIERARCHY_CHILD             = 2,
    HIERARCHY_PARENT            = 3,
    SHAPE_UNKNOWN               = -1,
    SHAPE_RECT                  = 0;

struct Contours {
    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
};

Contours find_contours(const cv::Mat& m, int retrieve_type, bool fetch_hierarchy=true){
    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    
    if(fetch_hierarchy){
        cv::findContours(m, contours, hierarchy, retrieve_type, cv::CHAIN_APPROX_SIMPLE);
    }
    else{
        cv::findContours(m, contours, retrieve_type, cv::CHAIN_APPROX_SIMPLE);
    }
    
    Contours con;
    con.contours    = contours;
    con.hierarchy   = hierarchy;
    
    return con;
}

int detect_contour_shape(const std::vector<cv::Point>& contour, double precision=0.02, bool convex=true){
    std::vector<cv::Point> approx;
    cv::approxPolyDP(contour, approx, cv::arcLength(contour, true) * precision, true);
    
    if(convex && !cv::isContourConvex(approx)){
        return SHAPE_UNKNOWN;
    }
    
    switch(approx.size()){
        case 4:
            return SHAPE_RECT;
        
        default:
            return SHAPE_UNKNOWN;
    }
}

bool is_contour_top_with_children(Contours contours, int i){
    return contours.hierarchy[i][HIERARCHY_PARENT] == -1 && contours.hierarchy[i][HIERARCHY_CHILD] != -1;
}

int main(int argc, char* argv[]){
    cv::Mat
        src     = cv::imread("input.png", cv::IMREAD_GRAYSCALE),
        mask    = cv::Mat::zeros(src.rows, src.cols, CV_8UC1);
    
    Contours contours = find_contours(src, cv::RETR_TREE);
    
    //  Filter contours
    if(!contours.hierarchy.empty()){
        for(int i = 0; i >= 0; i = contours.hierarchy[i][HIERARCHY_NEXT]){
            //  Contour has no parent (top-level contour) and has children
            if(is_contour_top_with_children(contours, i)){
                if(detect_contour_shape(contours.contours[i]) == SHAPE_RECT){
                    cv::Rect rect = cv::boundingRect(contours.contours[i]);
                    
                    std::cout << rect << '\n';
                    cv::drawContours(mask, contours.contours, i, cv::Scalar(255), cv::FILLED);
                }
            }
        }
    }
    
    cv::imwrite("mask.png", mask);
    
    return 0;
}

CodePudding user response:

You can just get the largest contour and draw a thick black rectangle around it. Something like this:

#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>

int main()
{
    // Read the input image:
    std::string imageName = "D://opencvImages//LSmPY.png";
    cv::Mat inputImage =  cv::imread( imageName );

    // Convert BGR to Gray:
    cv::Mat grayImage;
    cv::cvtColor( inputImage, grayImage, cv::COLOR_RGB2GRAY );

    // Extract external contours:
    std::vector< std::vector<cv::Point> > contours;
    cv::findContours( grayImage, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE );

    // Loop through all contours:
    for( int i = 0; i< (int)contours.size(); i   ) {
        
        // Get contour area:
        double contourArea = cv::contourArea( contours[i], false );

        // Only process the contour if it is above a certain area:
        double minArea = 100000;
        if (contourArea > minArea){
            // Get bounding rectangle:
            cv::Rect boundingRectangle = cv::boundingRect( contours[i] );

            int rectX = boundingRectangle.x;
            int rectY = boundingRectangle.y;
            int rectWidth = boundingRectangle.x   boundingRectangle.width;
            int rectHeight = boundingRectangle.y   boundingRectangle.height;

            // Draw a thick black rectangle around contour:
            int rectangleThickness = 10;
            cv::rectangle( inputImage, cv::Point(rectX, rectY), cv::Point(rectWidth, rectHeight), cv::Scalar(0,0,0), rectangleThickness );
            
            cv::imshow( "Out Image", inputImage );
            cv::waitKey(0);

        }

    }

    return 0;
}

This is the output image:

CodePudding user response:

First use Sobel filter in the x-axis to find just the vertical lines. Follow this tutorial for more details on the Sobel filter operator.

 cv::Mat src_gray = cv::imread("input.png", cv::IMREAD_GRAYSCALE),
 Sobel(src_gray, grad_x, ddepth, 1, 0, ksize, scale, delta, BORDER_DEFAULT);
 

Then loop over columns to find the lines with a high count of white pixels.

int threshWhite = 100
int colNum1, colNum2 = 0, 0;
for(int i = 0; i < grad_x.cols; i  )
{
   
    int numWhite = cv::countNonZero(grad_x.col(i))
    if (numWhite  > threshWhite ){
    colNum1 = i;
    colNum2 = grad_x.cols - i;
    break;
    }
}

Note: Given that the distance from margin to outer lines are symmetric.

For the upper line of the rectangle, do the same with two adjustments:

Sobel(src_gray, grad_x, ddepth, 0, 1, ksize, scale, delta, BORDER_DEFAULT); 

Sobel on the y-axis to find horizontal line

int threshWhite = 100
int rowNum = 0;
for(int i = 0; i < grad_x.rows; i  )
{

    int numWhite = cv::countNonZero(grad_x.row(i))
    if (numWhite  > threshWhite ){
    rowNum = i;

    break;
    }
}

  • Related