I just started learning openCV and I want to write a program that can detect the organs in a radiograph. The result I want to have look like this.
I tried cv2.findContours but it can't detect the correct one, then I use convex hull and it return like this which is not the one I want neither.
Is there a other way that you can find the contours in openCV that can help me with this one? I can only find two ways above.
you must use the validContours so you can use this code after use findContours and you change the boundRect value to find the area that you want
vector<vector<Point> > contours_poly(contourss.size());
vector<Rect> boundRect(contourss.size());
vector<Point2f>center(contourss.size());
vector<float>radius(contourss.size());
//Get poly contours
for (int i = 0; i < contourss.size(); i++)
{
approxPolyDP(Mat(contourss[i]), contours_poly[i], 3, true);
}
//Get only important contours, merge contours that are within another
vector<vector<Point> > validContours;
for (int i = 0; i < contours_poly.size(); i++){
Rect r = boundingRect(Mat(contours_poly[i]));
if (r.area() < 200)continue;
bool inside = false;
for (int j = 0; j < contours_poly.size(); j++){
if (j == i)continue;
Rect r2 = boundingRect(Mat(contours_poly[j]));
if (r2.area() < 200 || r2.area()<r.area())continue;
if (r.x>r2.x&&r.x + r.width<r2.x + r2.width&&
r.y>r2.y&&r.y + r.height < r2.y + r2.height){
inside = true;
}
}
if (inside)continue;
validContours.push_back(contours_poly[i]);
}
//Get bounding rects
for (int i = 0; i < validContours.size(); i++){
boundRect[i] = boundingRect(Mat(validContours[i]));
}
Related
I have a picture with a table in it and I need to extract the boxes with the handwriting in it then crop them,because I need to recognise the handwriting in those boxes one by one but the problem is the findSquare function gives me an output of too many squares with the same coordinates(+-5 pixels) beacuse of the several threshold levels.I already reduced the number of squares from 300 to 90 by searching in only one channel.The teacher told me to filter the extra squares by their coordinates,if the difference between the square coordinates is less then 10 pixels it means it's the same box.
The picture with the table is here https://ibb.co/Ms8YP8n
And instead of 20 boxes it crops me around 90 and most of them are the same.
So I was trying to go through the squares vector in a for loop and eliminate the squares that are seen twice.
Here is the code that I was trying with
vector<vector<Point> > same;
same.clear();
same = squares;
int sq = squares.size();
for (int i = 0; i < sq; i++) {
for (int j = 1; j < sq-1; j++) {
if (-5 < (same[i][0].x - same[j][0].x) < 5 && -5 < (same[i][0].y - same[j][0].y) < 5 &&
-5 < (same[i][1].x - same[j][1].x) < 5 && -5 < (same[i][1].y - same[j][1].y) < 5 &&
-5 < (same[i][2].x - same[j][2].x) < 5 && -5 < (same[i][2].y - same[j][2].y) < 5 &&
-5 < (same[i][3].x - same[j][3].x) < 5 && -5 < (same[i][3].y - same[j][3].y) < 5) {
squares.erase(same.begin() + j);
}
}
}
The "same" vector is just a copy of "squares".
The error that gives me is "Vector erase iterator outside range".
You can find this code in the drawSquare function bellow where you can see the full code too.
I need the pictures for future proccessing, I need to recognise the handwritten numbers.
Can anybody please help me?With another method or ideeas or some fixes in this code...
Thank you !
static void findSquares(const Mat& image, vector<vector<Point> >& squares)
{
squares.clear();
Mat pyr, timg, timb, gray0(image.size(), CV_8UC1), gray;
pyrDown(image, pyr, Size(image.cols / 2, image.rows / 2));
pyrUp(pyr, timg, image.size());
vector<vector<Point> > contours;
// find squares in every color plane of the image
for (int c = 0; c < 1; c++)
{
int ch[] = { c, 0 };
mixChannels(&timg, 1, &gray0, 1, ch, 1);
// try several threshold levels
for (int l = 0; l < N; l++)
{
// hack: use Canny instead of zero threshold level.
// Canny helps to catch squares with gradient shading
if (l == 0)
{
// apply Canny. Take the upper threshold from slider
// and set the lower to 0 (which forces edges merging)
Canny(gray0, gray, 0, thresh, 5);
// dilate canny output to remove potential
// holes between edge segments
dilate(gray, gray, Mat(), Point(-1, -1));
}
else
{
// apply threshold if l!=0:
// tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
gray = gray0 >= (l + 1) * 255 / N;
// imshow("graaay0", gray);
}
// find contours and store them all as a list
findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
vector<Point> approx;
// test each contour
for (int i = 0; i < contours.size(); i++)
{
// approximate contour with accuracy proportional
// to the contour perimeter
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);
// square contours should have 4 vertices after approximation
// relatively large area (to filter out noisy contours)
//// and be convex.
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (approx.size() >= 4 && approx.size() <= 6 &&
fabs(contourArea(Mat(approx))) > 5000 && fabs(contourArea(Mat(approx))) < 8000 &&
isContourConvex(Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
// find the maximum cosine of the angle between joint edges
double cosine = fabs(angle(approx[j % 4], approx[j - 2], approx[j - 1]));
maxCosine = MAX(maxCosine, cosine);
}
// if cosines of all angles are small
// (all angles are ~90 degree) then write quandrange
// vertices to resultant sequence
if (maxCosine >= 0 && maxCosine < 0.2)
squares.push_back(approx);
}
}
}
}
}
// the function draws all the squares in the image
static void drawSquares(Mat& image, vector<vector<Point> > &squares)
{
vector<vector<Point> > same;
same.clear();
same = squares;
int sq = squares.size();
for (int i = 0; i < sq; i++) {
for (int j = 1; j < sq; j++) {
if (-5 < (same[i][0].x - same[j][0].x) < 5 && -5 < (same[i][0].y - same[j][0].y) < 5 &&
-5 < (same[i][1].x - same[j][1].x) < 5 && -5 < (same[i][1].y - same[j][1].y) < 5 &&
-5 < (same[i][2].x - same[j][2].x) < 5 && -5 < (same[i][2].y - same[j][2].y) < 5 &&
-5 < (same[i][3].x - same[j][3].x) < 5 && -5 < (same[i][3].y - same[j][3].y) < 5) {
squares.erase(same.begin() + j);
}
}
}
for (int i = 0; i < squares.size(); i++)
{
const Point* p = &squares[i][0];
int n = (int)squares[i].size();
polylines(image, &p, &n, 1, true, Scalar(0, 255, 0), 3, LINE_AA);
}
imshow("Plate with green", image);
}
Mat ImageExtract(Mat& image, vector<vector<Point> >& squares)
{
char file_name[100];
int x = squares.size();
printf("squaree %d", x);
Mat roi;
for (int i = 0; i < squares.size(); i++)
{
sprintf(file_name, "cropped%d.jpg", i + 1);
Rect r = boundingRect(squares[i]);
roi = Mat(image, r);
imwrite(file_name, roi);
}
return roi;
}
void Preprocessing() {
char fname[MAX_PATH];
openFileDlg(fname);
Mat img = imread(fname, 1);
std::vector<std::vector<Point> > squares;
findSquares(img, squares);
ImageExtract(img, squares);
drawSquares(img, squares);
waitKey(0);
}
int main() {
Preprocessing();
return 0;
}
[1]: https://i.stack.imgur.com/mwvxX.png
I have removed non maxima regions from the vector "bbox" . But when I plot it shows the regions which contain all i.e unsupressed & supressed.
If I measure the bbox.size() befor & after non max supression it says 4677 & 3582 respectively.
Ptr<MSER> ms = MSER::create();
vector<Rect> bbox,filtered;
vector<vector<Point> > regions;
vector <int> vec;
ms->detectRegions(gray,regions,bbox);
cout<<bbox.size()<<"Befor Delete"<<endl;
for (int i = 0; i<bbox.size(); i++){
for(int j = 0; j<bbox.size(); j++){
if( (bbox[i].x > bbox[j].x) && (bbox[i].y > bbox[j].y) && ((bbox[i].x+bbox[i].width) < (bbox[j].x+bbox[j].width)) && ((bbox[i].y+bbox[i].height) < (bbox[j].y+bbox[j].height)) && (bbox[i].area() < bbox[j].area())){
bbox.erase(bbox.begin() + i );
}
}
}
cout<<bbox.size()<<" After"<<endl;
for (int k = 0; k < bbox.size(); k++)
{
rectangle(src,bbox[k], CV_RGB(0, 255, 0));
//cout<<k<<endl;
}
imshow("mser", src);
imwrite("NoN.jpg",src);
I have utilised the OpenCV GrabCut functionality to perform an image segmentation. When viewing the segmented image as per the code below, the segmentation is reasonable/correct. However, when looking at(at attempting to use) the segmrntation mask values, I am getting some very large numbers, and not the enumerated values one would expect from the cv::GrabCutClasses enum.
void doGrabCut(){
Vector2i imgDims = getImageDims();
//Wite image to OpenCV Mat.
const Vector4u *rgb = getRGB();
cv::Mat rgbMat(imgDims.height, imgDims.width, CV_8UC3);
for (int i = 0; i < imgDims.height; i++) {
for (int j = 0; j < imgDims.width; j++) {
int idx = i * imgDims.width + j;
rgbMat.ptr<cv::Vec3b>(i)[j][2] = rgb[idx].x;
rgbMat.ptr<cv::Vec3b>(i)[j][1] = rgb[idx].y;
rgbMat.ptr<cv::Vec3b>(i)[j][0] = rgb[idx].z;
}
}
//Do graph cut.
cv::Mat res, fgModel, bgModel;
cv::Rect bb(bb_begin.x, bb_begin.y, bb_end.x - bb_begin.x, bb_end.y - bb_begin.y);
cv::grabCut(rgbMat, res, bb, bgModel, fgModel, 10, cv::GC_INIT_WITH_RECT);
cv::compare(res, cv::GC_PR_FGD, res, cv::CMP_EQ);
//Write mask.
Vector4u *maskPtr = getMask();//uchar
for (int i = 0; i < imgDims.height; i++) {
for (int j = 0; j < imgDims.width; j++) {
cv::GrabCutClasses classification = res.at<cv::GrabCutClasses>(i, j);
int idx = i * imgDims.width + j;
std::cout << classification << std::endl;//Strange numbers here.
maskPtr[idx].x = (classification == cv::GC_PR_FGD) ? 255 : 0;//This always evaluates to 0.
}
}
cv::Mat foreground(rgbMat.size(), CV_8UC3, cv::Scalar(255, 255, 255));
rgbMat.copyTo(foreground, res);
cv::imshow("GC Output", foreground);
}
Why would one get numbers outside the enumeration when the segmentation is qualitatively correct?
I doubt on your //Write mask. step, why do you re-iterate the res and modify maskPtr as maskPtr[idx].x = (classification == cv::GC_PR_FGD) ? 255 : 0;, Basically you already have a single channel Binary image stored in the res variable, the cv::compare() returns a binary image
However if you still want to debug the values by iteration then you should use the standard technique for iterating a single channel image as:
for (int i = 0; i < m.rows; i++) {
for (int j = 0; j < m.cols; j++) {
uchar classification = res.at<uchar>(i, j);
std::cout << int(classification) << ", ";
}
}
As you are iterating a single channel mat you must use res.at<uchar>(i, j) and not res.at<cv::GrabCutClasses>.
I have copied a grayscale image into a cv::Mat1b, and I want to loop through each pixel and read and change its value. How can I do that?
My code looks like this :
cv::Mat1b newImg;
grayImg.copyTo(newImg);
for (int i = 0; i < grayImg.rows; i++) {
for (int j = 0; i < grayImg.cols; j++) {
int pixelValue = static_cast<int>(newImg.at<uchar>(i, j));
if(pixelValue > thresh)
newImg.at<int>(i,j) = 0;
else
newImg.at<int>(i, j) = 255;
}
}
But in the assignments (inside of if and else), I get the error Access violation writing location.
How do I read and write specific pixels correctly?
Thanks !
Edit
Thanks to #Miki and #Micka, this is how I solved it :
for (int i = 0; i < newImg.rows; i++) {
for (int j = 0; j < newImg.cols; j++) {
// read :
cv::Scalar intensity1 = newImg.at<uchar>(i,j);
int intensity = intensity1.val[0];
// write :
newImg(i, j) = 255;
}
}
newImg.at<int>(i,j)
should be
newImg.at<uchar>(i,j)
Because cv::Mat1b is of uchar type
i suggest :
cv::Mat1b newImg;
newImg = grayImg > thresh ;
or
cv::Mat1b newImg;
newImg = grayImg < thresh ;
also look at the OpenCV Tutorials to know how to go through each and every pixel of an image
How can I found the circles on image using the method minEnclosingCircle but avoid the internal circles?
I'm using the opencv with c++ to detect circles on a image. I was using the method HoughCircles but sometimes it lost some circles or it detected false circles.
Therefore I'm replacing it for the minEnclosingCircle. Now the algorithm is finding all circles, but in some cases it found circles inside other circles and I want avoid this.
The image 1 is a example of input and the image 2 is it output.
The code used to process those images is this:
Start of code:
vector < Circle > houghCircles(Mat thresholdImage, float minRadius, float maxRadiuss) {
vector < vector < Point > > contours;
vector < Vec4i > hierarchy;
findContours(thresholdImage, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
vector < vector < Point > > contours_poly(contours.size());
vector < Circle > circlesTMP(contours.size());
for (int i = 0; i < contours.size(); i++) {
Point2f detectedCenter;
float detectedRadius;
approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);
minEnclosingCircle((Mat) contours_poly[i], detectedCenter, detectedRadius);
if (minRadius != 0 && detectedRadius < minRadius) {
continue;
}
if (maxRadiuss != 0 && detectedRadius > maxRadiuss) {
continue;
}
sf::Circle _circle(detectedCenter, detectedRadius);
circlesTMP.push_back(_circle);
}
vector < Circle > circles;
for (int i = 0; i < circlesTMP.size(); i++) {
sf::Circle _circle = circlesTMP[i];
if (_circle.getRadius() > 0) {
circles.push_back(circlesTMP[i]);
}
}
cout << "Circles found: " << circles.size() << endl;
Mat drawing = Mat::zeros(thresholdImage.size(), CV_8UC3);
for (int i = 0; i < circles.size(); i++) {
sf::Circle _circle = circles[i];
Scalar color = Scalar(200, 187, 255);
circle(drawing, _circle.getCenter(), (int) _circle.getRadius(), color, 2, 8, 0);
}
imshow(drawing, "OpenCVUtil");
waitkey();
return circles;
}
you can check this link out and change your mode parameter and test your code:
http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=findcontours#findcontours
check out the results you obtain for CV_RETR_EXTERNAL instead of CV_RETR_TREE as the mode.