How to black out everything outside a circle in Open CV - opencv

I am currently trying to black out everything outside a circle.
I am drawing the circle using the following lines of code:
cv::Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); // CVRound converts floating numbers to integer
int radius = cvRound(circles[i][2]); // Radius is the third parameter [i][0] = x [i][1]= y [i][2] = radius
circle( image, center, 3, cv::Scalar(0,255,0), -1, 8, 0 ); // Drawing little circle to Image Center , next Line of Code draws the real circle
circle( image, center, radius, cv::Scalar(0,0,255), 3, 8, 0 ); // Circle(img, center, radius, color, thickness=1, lineType=8, shift=0)
What is the best approach of painting everything of the circly black, if I have a radius and the center of my circle?
Does OpenCV provide an easy mechanism of doing this or should I iterate through all the pixels of my image and depending on the position color them black or not?

Thanks to Abid for the hint, i ended up with this approach. Everything works fine:
cv::Mat src = someMethodThatReturnsSrcImage(); // src Image
cv::Mat maskedImage; // stores masked Image
std::vector<cv::Vec3f> circles = someMethodThatReturnsCircles(src);
cv::Mat mask(srcImageForDimensions.size(),srcImageForDimensions.type()); // create an Mat that has same Dimensons as src
mask.setTo(cv::Scalar(0,0,0)); // creates black-Image
// Add all found circles to mask
for( size_t i = 0; i < circles.size(); i++ ) // iterate through all detected Circles
{
cv::Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); // CVRound converts floating numbers to integer
int radius = cvRound(circles[i][2]); // Radius is the third parameter [i][0] = x [i][1]= y [i][2] = radius
cv::circle( mask, center, radius, cv::Scalar(255,255,255),-1, 8, 0 ); // Circle(img, center, radius, color, thickness=1, lineType=8, shift=0)
}
src.copyTo(maskedImage,mask); // creates masked Image and copies it to maskedImage

you can make the background the color you want
image=cv::Scalar(red_value, green_value, blue_value);
then draw your circles

I think that comment just under your question is the best solution.
I made a modified version of your code for 5M image from fisheye camera. This image also needs to make black all points outside circle.
#include <Windows.h>
#include <Vfw.h>
#include <string>
#include <iostream>
#include "opencv2\core\core.hpp"
#include "opencv2\imgproc\imgproc.hpp"
#include "opencv2\imgcodecs\imgcodecs.hpp"
#include "opencv2\highgui\highgui.hpp"
using namespace std;
using namespace cv;
int _tmain(int argc, _TCHAR* argv[])
{
cv::Mat im_source_non_square = cv::imread("D:/FishLib/sample_02.bmp", CV_LOAD_IMAGE_COLOR);
cv::namedWindow("Image",CV_WINDOW_FREERATIO);
cv::imshow("Image", im_source_non_square);
Mat im_source_square;
int m_nCenterX=1280;
int m_nCenterY=960;
int m_nRadius=916;
Mat im_mask=im_source_non_square.clone();
im_mask.setTo(cv::Scalar(0,0,0));
circle( im_mask, cv::Point(m_nCenterX,m_nCenterY), m_nRadius, cv::Scalar(255,255,255), -3, 8, 0 );
cv::namedWindow("Mask image",CV_WINDOW_FREERATIO);
cv::imshow("Mask image", im_mask);
Mat im_source_circle;
cv::bitwise_and(im_source_non_square,im_mask,im_source_circle);
cv::namedWindow("Combined image",CV_WINDOW_FREERATIO);
cv::imshow("Combined image", im_source_circle);
cv::waitKey(0);
return 0;
}

Just tried your code snippet and it works.
Also if you want to change the background color instead of black, according to opencv docs here, before copyTo the destination mat will be initialized if needed, so just add code below:
cv::Mat maskedImage(srcImageForDimensions.size(), srcImageForDimensions.type()); // stores masked Image
maskedImage.setTo(cv::Scalar(0,0,255)); // set background color to red

Related

Circle detection in a Image

I am trying to use HoughCircles method to detect a circle from an Image, but it looks like that this method is not useful to detect all circle with almost the same centres. For example, if I have 3 circles with almost the same centre it detects as a single circle. Please suggest if there is any way around to find all circles. Here is the source image:
I might be wrong with HoughCircle Methos assumption.
Thanks in advance.
The reason is that when you call HoughCircles you should decide the minimum distance between the detected circle centerces. The same center you mentioned means that zero distance between them. So in this case you should set the minimum distance parameter almost 0.
void cv::HoughCircles ( InputArray image,
OutputArray circles,
int method,
double dp,
double minDist, // You should set this parameter almost zero cos 0 not accepted.
double param1 = 100,
double param2 = 100,
int minRadius = 0,
int maxRadius = 0
)
When I tried with these parameters:
HoughCircles( input, output, CV_HOUGH_GRADIENT, 1, 0.5, 60, 30, 1, 200 );
I get this:
Edit: When I tried some more playing on this, I got 12 circles but the reality is 18 circles(edge circles not included). The reason could be about the image quality. Here is my code and result:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace cv;
using namespace std;
int main()
{
/// Load source image and convert it to gray
Mat src_gray,dst,src = imread("/ur/source/image/image.jpg", 1 );
imshow("Source",src);
int i = 50;
bilateralFilter(src,dst,i,i*2,i/2);
imshow("Output",dst);
cvtColor( dst, src_gray, CV_BGR2GRAY );
vector<Vec3f> circles;
/// Apply the Hough Transform to find the circles
HoughCircles( src_gray, circles, CV_HOUGH_GRADIENT, 1, 0.01, 80, 55, 0, 100 );
Mat zero_mask = Mat::zeros(src.rows,src.cols,CV_8UC3);
/// Draw the circles detected
for( size_t i = 0; i < circles.size(); i++ )
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
// circle center
circle( zero_mask, center, 3, Scalar(0,255,0), -1, 8, 0 );
// circle outline
circle( zero_mask, center, radius, Scalar(0,0,255), 1, 8, 0 );
}
cout<<circles.size()<<endl;
imshow("Output2",src_gray);
imshow("outt",zero_mask);
waitKey(0);
return(0);
}
Output:

How to properly apply Hough Circle Transform in iOS environment?

First of, I have no experience working in Objective C++.
Like the subjects states, I am trying to implement OpenCV (v3.4.2) Hough Circle Transform into images capture via iOS device. So far, I am able to make the captured photo greyscale. However, when I try to apply circle transform, app crashes. Also, there are syntax errors when I try to draw the recognized circles.
OpenCV Guide I am following.
Thank you for your time.
#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>
#import "GoCalc2-Bridging-Header.h"
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#implementation ImageConverter : NSObject
+(UIImage *)ConvertImage:(UIImage *)image {
cv::Mat mat;
UIImageToMat(image, mat);
cv::Mat gray;
cv::cvtColor(mat, gray, CV_RGB2GRAY);
cv::Mat bin;
cv::threshold(gray, bin, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
// Apply a Median blur to reduce noise and avoid false circle detection
cv::medianBlur(mat, gray, 5);
// Proceed to apply Hough Circle Transform
std::vector<cv::Vec3f> circles;
HoughCircles(gray, circles, cv::HOUGH_GRADIENT, 1,
gray.rows/16, // change this value to detect circles with different distances to each other
100, 30, 1, 30 // change the last two parameters
// (min_radius & max_radius) to detect larger circles
);
// Draw the detected circles
for( size_t i = 0; i < circles.size(); i++ )
{
Vec3i c = circles[i];
Point center = Point(c[0], c[1]); // ERROR HERE: No matching constructor for initialization of 'Point'
// circle center
circle( src, center, 1, Scalar(0,100,100), 3, LINE_AA); // ERROR HERE: Use of undeclared identifier 'LINE_AA' and Use of undeclared identifier 'src'
// circle outline
int radius = c[2];
circle( src, center, radius, Scalar(255,0,255), 3, LINE_AA); // ERROR HERE: Use of undeclared identifier 'LINE_AA' and Use of undeclared identifier 'src'
}
// Display the detected circle(s) and wait for the user to exit the program
imshow("detected circles", mat);
cv::waitKey();
UIImage *binImg = MatToUIImage(bin);
return binImg;
}
#end
Have you tried removing LINE_AA?
This is what I have done in my app circle( img, center, 1, cvScalar(255,255,255), 3);.
Instead of Vec3i c = circles[i]; you may want to try cv::Vec3i c = circles[i];
hope it will help

Estimate white background

I have image with white uneven background (due to lighting). I'm trying to estimate background color and transform image into image with true white background. For this I estimated white color for each 15x15 pixels block based on its luminosity. So I've got the following map (on the right):
Now I want to interpolate color so it will be more smooth transition from 15x15 block to neighboring block, plus I want it to eliminate outliers (pink dots on left hand side). Could anyone suggest good technique/algorithm for this? (Ideally within OpenCV library, but not necessary)
Starting from this image:
You could find the text on the whiteboard as the parts of your images that have a high gradient, and apply a little dilation to deal with thick parts of the text. You'll get a mask that separates background from foreground pretty well:
Background:
Foreground:
You can then apply inpainting using the computed mask on the original image (you need OpenCV contrib module photo):
Just to show that this works independently of the text color, I tried on a different image:
Resulting in:
Code:
#include <opencv2/opencv.hpp>
#include <opencv2/photo.hpp>
using namespace cv;
void findText(const Mat3b& src, Mat1b& mask)
{
// Convert to grayscale
Mat1b gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
// Compute gradient magnitude
Mat1f dx, dy, mag;
Sobel(gray, dx, CV_32F, 1, 0);
Sobel(gray, dy, CV_32F, 0, 1);
magnitude(dx, dy, mag);
// Remove low magnitude, keep only text
mask = mag > 10;
// Apply a dilation to deal with thick text
Mat1b K = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
dilate(mask, mask, K);
}
int main(int argc, const char * argv[])
{
Mat3b img = imread("path_to_image");
// Segment white
Mat1b mask;
findText(img, mask);
// Show intermediate images
Mat3b background = img.clone();
background.setTo(0, mask);
Mat3b foreground = img.clone();
foreground.setTo(0, ~mask);
// Apply inpainting
Mat3b inpainted;
inpaint(img, mask, inpainted, 21, CV_INPAINT_TELEA);
imshow("Original", img);
imshow("Foreground", foreground);
imshow("Background", background);
imshow("Inpainted", inpainted);
waitKey();
return 0;
}

how to reduce light intensity of an image

Took an example image from opencv (cat.jpg).To reduce brightness at particular area. here is the link for the image
http://tinypic.com/view.php?pic=2lnfx46&s=5
Here is one possible solution. The bright spots are detected using a simple threshold operation. Then the bright spots are darkened using a gamma transformation. The result looks slightly better, but unfortunately, if the pixels in the image are exactly white, all the pixel information is lost and you will not be able to recover this information.
#include <opencv2/opencv.hpp>
#include <iostream>
#include <cfloat>
int threshold = 200;
double gammav = 3;
int main(int argc, char** argv )
{
cv::Mat image,gray_image,bin_image;
// read image
cv::imread(argv[1]).convertTo(image,CV_32FC3);
// find bright spots with thresholding
cv::cvtColor(image, gray_image, CV_RGB2GRAY);
cv::threshold( gray_image, bin_image, threshold, 255,0 );
// blur mask to smooth transitions
cv::GaussianBlur(bin_image, bin_image, cv::Size(21,21), 5 );
// create 3 channel mask
std::vector<cv::Mat> channels;
channels.push_back(bin_image);
channels.push_back(bin_image);
channels.push_back(bin_image);
cv::Mat bin_image3;
cv::merge(channels,bin_image3);
// create darker version of the image using gamma correction
cv::Mat dark_image = image.clone();
for(int y=0; y<dark_image.rows; y++)
for(int x=0; x<dark_image.cols; x++)
for(int c=0;c<3;c++)
dark_image.at<cv::Vec3f>(y,x)[c] = 255.0 * pow(dark_image.at<cv::Vec3f>(y,x)[c]/255.0,gammav);
// create final image
cv::Mat res_image = image.mul((255-bin_image3)/255.0) + dark_image.mul((bin_image3)/255.0);
cv::imshow("orig",image/255);
cv::imshow("dark",dark_image/255);
cv::imshow("bin",bin_image/255);
cv::imshow("res",res_image/255);
cv::waitKey(0);
}

How to find the coordinates of a point w.r.t another point on an image using OpenCV

Today I wrote a program for detecting circles using Hough Transform using OpenCV in C.
The program inputs 3 images, each image contains a fixed small circle and a big circle with variable position. The program then recognizes both the circles and marks the centres of both the circles. Now what I want to do is that in the output image the (x,y) coordinates of the centre of the bigger circle should be displayed with respect to the centre of the fixed smaller circle . Here's the code for 'circle.cpp'
#include <cv.h>
#include <highgui.h>
#include <math.h>
int main(int argc, char** argv)
{
IplImage* img;
int n=3;
char input[21],output[21];
for(int l=1;l<=n;l++)
{
sprintf(input,"Frame%d.jpg",l); // Inputs Images
if( (img=cvLoadImage(input))!= 0)
{
IplImage* gray = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 1 );
IplImage* canny=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
IplImage* rgbcanny=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,3);
CvMemStorage* storage = cvCreateMemStorage(0);
cvCvtColor( img, gray, CV_BGR2GRAY );
cvSmooth( gray, gray, CV_GAUSSIAN, 9, 9 ); // smooth it, otherwise a lot of false circles may be detected
cvCanny(gray,canny,50,100,3);
CvSeq* circles = cvHoughCircles( canny, storage, CV_HOUGH_GRADIENT, 2, gray->height/4, 200, 100 );
int i;
cvCvtColor(canny,rgbcanny,CV_GRAY2BGR);
for( i = 0; i < circles->total; i++ )
{
float* p = (float*)cvGetSeqElem( circles, i );
cvCircle( rgbcanny, cvPoint(cvRound(p[0]),cvRound(p[1])), 3, CV_RGB(0,255,0), -1, 8, 0 );
cvCircle( rgbcanny, cvPoint(cvRound(p[0]),cvRound(p[1])), cvRound(p[2]), CV_RGB(255,0,0), 3, 8, 0 );
}
cvNamedWindow( "circles", 1 );
cvShowImage( "circles", rgbcanny );
//Displays Output images
sprintf(output,"circle%d.jpg",l);
cvSaveImage(output,rgbcanny);
cvWaitKey(0);
}
}
return 0;
}
And here are the input and output images:
Please suggest what changes should I make in the code to display the desired (x,y)coordinates. Thanx a lot :)
Before you show the image, use cvPutText to add the desired text. The parameters of this function are self-explaining. The font should be initialized using cvInitFont.
When you calculate the relative coordinates, keep in mind that in OpenCV, the coordinate system is like this
-----> x
|
|
v
y
just in case you are interested in showing the relative coordinates in a system in which the axes point in another direction.
You should check that the Hough transform has detected exactly two circles. If so, all the data you need is in the circles variable. If (xa,ya) are the coordinates of the bigger circle and (xb,yb) the coordinates of the smaller one, the relative coordinates are (xa-xb,ya-yb).

Resources