0

I have recently started working on opencv. I have an image having several dots in a line. I want to make a rectangular border around these dots. I have applied some basic C algorithm but it didn't work for me. This is my code.

int main()

{

    cv::Mat image = imread("C:/Users/Ravi Sharma/Desktop/img.bmp");

     for(int i =0; i < image.rows; i++){
        for(int k = 0; k <image.cols; k ++){
            if((image.at<cv::Vec3b>(k,i)[0] == 0)&&(image.at<cv::Vec3b>(k,i)[1] == 135)&&(image.at<cv::Vec3b>(k,i)[2] == 255))
            {
            (image.at<cv::Vec3b>(k,i)[0] = 0)&&(image.at<cv::Vec3b>(k,i)[1] = 0)&&(image.at<cv::Vec3b>(k,i)[2] = 255);
            }
        }

    }
    imwrite("C:/Users/Ravi Sharma/Desktop/img1.bmp",image);
    cv::namedWindow("Window1");
    cv::imshow("Window1",image);
    cv::waitKey(50000);
    return 1;
} 

This code is not updating the pixel value. Please help me correcting the code to get the desired result. How I can use cvminmaxloc function to do the same. Thanx in advance.

guneykayim
  • 5,210
  • 2
  • 29
  • 61
Ravi Sharma
  • 63
  • 2
  • 12
  • The function `cvminmaxloc` finds minimum and maximum element values and their positions. So, if I understand your question correctly then this function won't help you. – nils Mar 31 '15 at 15:59
  • You can use cvminmaxloc to find the location of the min/max elements in a single channel array. If you repeatedly call it and compare/update/store the highest/lowest points (top left, bottom right) of the min element you can draw a rectangle using them. You will need to use a grayscale image and must update your image after each call to prevent the same location being returned (fill min location with white). – user3510227 Mar 31 '15 at 16:07
  • can you explain it by some example? @user3510227 – Ravi Sharma Mar 31 '15 at 16:10
  • if axis aligned use http://docs.opencv.org/doc/tutorials/imgproc/shapedescriptors/bounding_rects_circles/bounding_rects_circles.html if rotated rect there are similar functions – Micka Mar 31 '15 at 18:15

2 Answers2

1

I will assume that your image can be treated as a binary mask and that we are only interested in pixels with non-zero values.

The assumed input Bounding box around points in the input image

To find the bounding box around the points in the left image above, iterate over all pixels in the image. For every non-zero pixel, check if its xy-location lies outside the current bounding box. If it does, then update your bounding box. The resulting bounding box will enclose all points, as in the right image above.

Below is a minimally working example, which generates an image containing randomly sampling points for which a bounding box is determined.

// This example uses OpenCV 3.0.0-beta. To use with OpenCV 2.4.* a
// few changes have to be made.

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

cv::Mat randomPoints( cv::Size size = cv::Size( 320, 240 ), int pointCount = 25 )
{
    cv::RNG rng( cv::getCPUTickCount() );
    cv::Mat image = cv::Mat3b::zeros( size );
    int radius = 3;
    cv::Scalar color( 0, 153, 255 );
    int thickness = -1;
    int margin = std::min( size.height, size.width ) / 4;

    for ( int i = 0; i < pointCount; ++i )
    {
        cv::Point p;
        p.x = rng.uniform( margin, size.width  - margin - 1 );
        p.y = rng.uniform( margin, size.height - margin - 1 );
        cv::circle( image, p, radius, color, thickness );
    }

    return image;
}

int main( int argc, char ** argv )
{
#if 0
    cv::Mat image = imread( "C:/Users/Ravi Sharma/Desktop/img.bmp" );
#else
    cv::Mat image = randomPoints();
#endif

    cv::Mat imageGray;
    cv::cvtColor( image, imageGray, cv::COLOR_BGR2GRAY );
    cv::Size size = imageGray.size();

    // The bounding box is defined by its top-left (TL) and bottom-right (BR)
    // coordinates.
    cv::Point tl( size.width, size.height );
    cv::Point br(          0,           0 );
    bool hasPoints = false;

    for ( int y = 0; y < size.height; ++y )
    {
        for ( int x = 0; x < size.width; ++x )
        {
            if ( imageGray.at<unsigned char>( y, x ) > 0 )
            {
                hasPoints = true;

                // Update the top-left corner.
                if ( x < tl.x ) tl.x = x;
                if ( y < tl.y ) tl.y = y;

                // Update the bottom-right corner.
                if ( x > br.x ) br.x = x;
                if ( y > br.y ) br.y = y;
            }
        }
    }

    // If the image contains any non-zero pixels, then draw the bounding box.
    if ( hasPoints )
        cv::rectangle( image, tl, br, cv::Scalar( 255, 255, 255, 255 ) );

    cv::namedWindow( "bounding-box" );
    cv::imshow( "bounding-box", image );
    cv::waitKey( 0 );

    cv::imwrite( "bounding-box.png", image );
}

EDIT 1:

I also like the idea suggested by @Micka above, i.e. using cv::boundingRect(). So, inside the loop in the above code example you would push all xy-locations of non-zeros pixels into a std::vector< cv::Point > and then call cv::boundingRect. In that context it is also interesting to look at the convex hull of a 2D point cloud.

nils
  • 2,424
  • 1
  • 17
  • 31
0

Here's a quick example specifically using cvminmaxloc, my Python isn't great but I don't have OpenCV setup for C++ here, hopefully it should be understandable.

Starting image:

Start image

Result image:

Result image

import cv2
import copy
import numpy

#Read in image
img = cv2.imread("Line.png")

#Top Left Point
y1,x1 = img.shape[:2]

#Bottom right point
y2,x2 = 0, 0

#Values returned from minmaxloc
min_val, max_val, min_loc, max_loc = 0,0,(0,0),(0,0)

#create grayscale copy of our img to work with
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#Get values
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(gray_img)

#White = 255 #Black = 0
while(min_val != 255):

        #Check points and update if needed
        if(min_loc[0] < x1):
                x1 = min_loc[0]
        if(min_loc[1] < y1):
                y1 = min_loc[1]
        if(min_loc[0] > x2):
                x2 = min_loc[0]
        if(min_loc[1] > y2):
                y2 = min_loc[1]

        #Update image - remove current result
        gray_img[min_loc[1], min_loc[0]] = 255

        #Get next values
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(gray_img)

#Done checking - draw rectangle over reference + display
cv2.rectangle(img, (x1,y1), (x2, y2), (0,255,0), 1)

#Display
cv2.namedWindow('image')
cv2.imshow('image', img)
cv2.waitKey(0)

#Clear up windows
cv2.destroyAllWindows()

#Write result
cv2.imwrite('result.png',img)
user3510227
  • 576
  • 2
  • 6
  • Thanx all for you help! I have written a code. It is making a rectangle but it is not covering the dot. it starts from (0,0) and goes till it finds the first bright pixel. I want it to make boundary around the dot. My code is: – Ravi Sharma Apr 01 '15 at 11:31
  • Check your initial point values, the top left point should be set to the image width and height so any found pixel coordinates overwrite it. – user3510227 Apr 01 '15 at 11:44