currently I have segmented the object (rectangular) and now I want to create a Line profile. I dont know how to get along this line.
detected object
Aim is to get this:
object with lines
Update 14:25:
I know already the angle from the bounding rect and used this to calculate the shift in y-direction in order to rearrange the values to a new mat so that I only need go through the matrix to get a line profile.
Here my Code, but the rearrangement did not work.
Mat imgIn(SizeY, SizeX, CV_16U, &Wire[0]),
imgOut(SizeY, SizeX, CV_16U, Scalar(0)),
temp, drawing, mask, lineProfile(SizeY, SizeX, CV_16U, Scalar(0));
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
Point center;
char buffer[100];
bool found = false;
int rect_no (0);
double angle(0.0);
// Detecting outer contours
temp = ::adaptiveThreshold(imgIn, SizeY, SizeX, kernelSize, thresh, 0);
// Find contours
findContours(temp, contours, hierarchy, CV_RETR_TREE, CHAIN_APPROX_TC89_KCOS, Point(0, 0) );
/// Find the rotated rectangles and ellipses for each contour
vector<RotatedRect> minRect( contours.size() );
for( int i = 0; i < contours.size(); i++ ) minRect[i] = minAreaRect( Mat(contours[i]) );
// Draw contours + rotated rects
drawing = Mat::zeros(temp.size(), CV_8U );
Point2f rect_points[4];
for( int i = 0; i< minRect.size(); i++ ){
if((float)minRect[i].boundingRect().height/(float)minRect[i].boundingRect().width > 3.0 && (float)minRect[i].boundingRect().height/(float)minRect[i].boundingRect().width < 4.9){
// rotated rectangle
minRect[i].points(rect_points);
for(int j = 0; j < 4; j++) line(drawing, rect_points[j], rect_points[(j+1)%4], Scalar(255), 1, 8);
//found = minRect[i].boundingRect().contains(Point(459, 512));
if(minRect[i].boundingRect().area() > 1000)
rect_no = i;
}
}
center = computeCentroid(drawing);
cv::floodFill(drawing, center, cv::Scalar(255));
drawing.convertTo(imgOut, CV_16U, 257.0);
imgIn.copyTo(imgOut, drawing);
// Calculate Wire SR_min
// Get angle of Wire
angle = (90 - (-1 * minRect[rect_no].angle))*(CV_PI/180);
for(int i = 0;i < SizeY;i++){
for(int j = 0;j < SizeX;j++){
if(imgOut.at<ushort>(i, j) != 0)
lineProfile.at<ushort>(i, j) = imgOut.at<ushort>((unsigned short)(i/cos(angle)), j);
}
}
for(int i = 0;i < SizeY;i++){
for(int j = 0;j < SizeX;j++){
*Wire++ = lineProfile.at<ushort>(i, j);//imgOut.at<ushort>(i, j);
}
}
If you know the coordinates of the beginning and the end of your line, getting the values at each point on the line should be easy with OpenCV's LineIterator. Feed it your image and your two points and let it work its magic.
If you are able to binarize the detected object image , then you could possibly able to apply Houghlines function of OpenCv . You can find it in the below link
http://docs.opencv.org/2.4/doc/tutorials/imgproc/imgtrans/hough_lines/hough_lines.html
Related
I am trying to run the followin code (based on this page) on an image, but it doesn't work:
Mat src=imread("img.jpg",1);
Mat tmp,thr;
cvtColor(src,tmp,CV_BGR2GRAY);
threshold(tmp,thr,200,255,THRESH_BINARY_INV);
vector< vector <Point> > contours;
vector< Vec4i > hierarchy;
Mat dst(src.rows,src.cols,CV_8UC1,Scalar::all(0));//Ceate Mat to draw contour
int box_w=10; // Define box width here
int box_h=10; // Define box height here
int threshold_perc=25; //perceantage value for eliminating the box according to pixel count inside the box
int threshold=(box_w*box_h*threshold_perc)/100;
findContours( thr, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); //Find contour
for( int i = 0; i< contours.size(); i++ ){
drawContours( dst,contours, i, Scalar(255,255,255),CV_FILLED, 8, hierarchy ); // Draw contour with thickness = filled
Rect r= boundingRect(contours[i]); // Find bounding rect
// Scan the image with in bounding box
for(int j=r.x;j<r.x+r.width;j=j+box_w){
for(int k=r.y;k<r.y+r.height;k=k+box_h){
Rect roi_rect(j,k,box_w,box_h);
Mat roi = dst(roi_rect);
int count = countNonZero(roi);
if(count > threshold)
rectangle(src, roi_rect, Scalar(255,0,0),1,8,0 );
}
}
}
imshow("src",src);
waitKey();
It works fine for any normal image, but for the images below, it either breaks or doesn't find the contour and draws boxes all over the image.
It says:
Unhandled exception at 0x00007FF9A72DA388 in test2.exe: Microsoft C++ exception: cv::Exception at memory location 0x000000FECC9DEAC0.
It breaks and points to here:
inline
Mat Mat::operator()( const Rect& roi ) const
{
return Mat(*this, roi);
}
in mat.inl.hpp.
What is wrong with my image? I have changed it from Gray-scale to RGB, but didn't help.
On the following image, it works fine:
As I commented, you're trying to access a region of the image that doesn't exist by using a rectangle of fixed size.
By intersecting the roi with the rectangle, you can avoid this problem:
Mat roi = dst(roi_rect & r);
The problem was that in the first images, the contour gets close to the boundaries of the image and in the bottom for loop of the program, it exceeds the coordinates. It was fixed with this:
// Scan the image with in bounding box
for (int j = r.x;j<r.x + r.width;j = j + box_w) {
for (int k = r.y;k<r.y + r.height;k = k + box_h) {
Rect roi_rect(j, k, box_w, box_h);
if (j + box_w < dst.cols && k + box_h < dst.rows)
{
Mat roi = dst(roi_rect);
int count = countNonZero(roi);
if (count > threshold)
rectangle(src, roi_rect, Scalar(0,0,255), 1, 8, 0);
}
}
}
Task:
I try to write a method, that reduces the objects in a binary image to their centroids (meaning center of mass).
Parameters:
Mat *pMA_Out, Mat *pMA_In, int mode, int method, unsigned int thickness
Body:
//Allocate contours
vector<vector<Point>> vvp_countours;
//Find countours
findContours(
*pMA_In,
vvp_countours,
mode,
method,
Point(0, 0));
//Get the moments
vector<Moments> v_moments(vvp_countours.size());
for(int c = 0; c < vvp_countours.size(); c++)
v_moments[c] = moments(vvp_countours[c], false);
//Get the mass centers
vector<Point2f> v_centroid(vvp_countours.size());
for(int c = 0; c < vvp_countours.size(); c++)
v_centroid[c] = Point2f(
v_moments[c].m10 / v_moments[c].m00,
v_moments[c].m01 / v_moments[c].m00);
//Create output image
Mat MA_tmp = Mat(pMA_In->size(), CV_8UC1, Scalar(0));
//Draw centroids
for(int c = 0; c < v_centroid.size(); c++)
line(
MA_tmp,
v_centroid[c],
v_centroid[c],
Scalar(255),
thickness,
8);
*pMA_Out = MA_tmp.clone();
I'm new in opencv (c++) and I want to remove horizontal line from this x-ray image. But I can not.
These is my image:
What ideas on how to solve this task would you suggest? Or on what resource on the internet can I find help?
This is my c++ code
src = imread("C:/Users/Alireza/Desktop/New folder (3)/11.bmp");
cvtColor(src, gray, CV_RGB2GRAY);
imshow("Original Image", gray);
imwrite("Original Image.png", gray);
normalize(gray, gray, 0, 250, NORM_MINMAX, -1, Mat());
threshold(gray, thresh, 170, 255, THRESH_BINARY_INV);
vector< vector <Point> > contours;
vector< Vec4i > hierarchy;
int largest_contour_index = 0;
int largest_area = 0;
Mat alpha(src.size(), CV_8UC1, Scalar(0));
findContours(thresh, contours, hierarchy, CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
for (int i = 0; i< contours.size(); i++)
{
double a = contourArea(contours[i], false);
if (a>largest_area)
{
largest_area = a;
largest_contour_index = i;
}
}
drawContours(alpha, contours, largest_contour_index,Scalar(255),CV_FILLED, 8, hierarchy);
vector<Mat> rgb;
split(src, rgb);
Mat rgba[4] = { rgb[0], rgb[1], rgb[2], alpha };
merge(rgba, 4, Tafrigh);
imshow("Tafrigh", Tafrigh);
imwrite("Tafrigh.png", Tafrigh);
Take a 2D FFT, take a look at the spectrum. You will see plenty of dots along the center y-axis. Suppress those dots , back transform, your vertical lines will be gone.
Below the result (since I don't have C++ and opencv installed) in Python, with sliders to vary the region to be suppressed. Consider it Pseudocode. This is still pretty rough as I'm not making a smooth transition between suppressed pixels and their neighbors here for simplicity.
%matplotlib inline
from __future__ import division
import numpy as np
import matplotlib.pyplot as p
from ipywidgets import *
from scipy import misc
f = misc.imread('xray_image_with_horizontal_lines.png')
a=np.fft.fftshift(np.fft.fft2(f))
def process(kx,ky):
p.figure(figsize=(12,8))
p.subplot(221)
p.imshow(f, cmap=p.cm.gray)
p.subplot(222)
p.imshow(np.abs(np.log(a)), cmap=p.cm.gray)
print np.shape(a)
b=np.zeros_like(a)
for i in range(639):
for j in range(406):
if not ( 320-kx<i<320+kx and (j<203-ky or j>203+ky)):
b[j,i]=a[j,i]
c=np.fft.ifft2(b)
p.subplot(223)
p.imshow(np.abs(np.log(b)), cmap=p.cm.gray)
p.subplot(224)
p.imshow(np.abs(c), cmap=p.cm.gray)
interact(process, kx=[1,20,1],ky=[1,20,1])
That's just an idea : keep mean line constant.
cv::Mat image = cv::imread("Tf6HO.png",CV_LOAD_IMAGE_GRAYSCALE);
vector<double> moyenne;
double minval,maxval;
minMaxLoc(image,&minval,&maxval);
imshow("original",image);
for (int i = 0; i < image.rows; i++)
{
double s=0;
// Caluclate mean for row i
for (int j=0;j<image.cols;j++)
s += image.at<uchar>(i,j);
// Store result in vector moyenne
moyenne.push_back(s/image.cols);
}
// Energy for row i equal to a weighted mean of row in [i-nbInf,i+nbSup]
int nbInf=32,nbSup=0;
for (int i = 0; i < image.rows; i++)
{
double s=0,p=0;
// weighted mean (border effect process with max and min method
for (int j = max(0, i - nbInf); j <= min(image.rows - 1, i + nbSup); j++)
{
s+=moyenne[j]*1./(1+abs(i-j));
p+=1./(1+abs(i-j));
}
// Weighted mean
s/=p;
// process pixel in row i : mean of row i equal to s
for (int j=0;j<image.cols;j++)
image.at<uchar>(i,j) =saturate_cast<uchar>((image.at<uchar>(i,j)-moyenne[i])+s);
}
imshow("process",image);
waitKey();
or with reduce resolution
if you want to improve you can read this paper and bibliography
I try to run squares.cpp that is in opencv direction in C++ sample , everything fine but when program reach to this point : approxPolyDP(Mat(contours[i]),approx,arcLength(Mat(contours[i]), true)*0.02, true);
I get the exception that say :
Unhandled exception at 0x61163C77 (opencv_imgproc244d.dll) in FindRectangle.exe: 0xC0000005: Access violation reading location 0x030F9000.
I do any thing to solve this problem but I can't.
I run it in visual studio 2012 with 32 bit processing.please help!!!!!!!!!!
static double angle( Point pt1, Point pt2, Point pt0 )
{
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}
// returns sequence of squares detected on the image.
// the sequence is stored in the specified memory storage
static void findSquares( const Mat& image, vector<vector<Point> >& squares )
{
squares.clear();
Mat pyr, timg, gray0(image.size(), CV_8U), gray;
// down-scale and upscale the image to filter out the noise
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 < 3; 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;
}
// find contours and store them all as a list
findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
vector<Point> approx ;
// test each contour
for( size_t 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 &&
fabs(contourArea(Mat(approx))) > 1000 &&
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.3 )
squares.push_back(approx);
}
else{
approx.clear();
}
}
}
}
// the function draws all the squares in the image
static void drawSquares( Mat& image, const vector<vector<Point> >& squares )
{
for( size_t 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, CV_AA);
}
imshow(wndname, image);
}
The usage need to update like below:
//Extract the contours so that
vector<vector<Point> > contours0;
findContours( img, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
contours.resize(contours0.size());
for( size_t k = 0; k < contours0.size(); k++ )
approxPolyDP(Mat(contours0[k]), contours[k], 3, true);
Link for documentation
I am trying to find the edges of the centered box in this image:
I have tried using a HoughLines using dRho=img_width/1000, dTheta=pi/180, and threshold=250
It works great on this image, scaled to 1/3 of the size, but on the full size image it just gets lines everywhere in every direction...
What can I do to tune this to be more accurate?
The code to achieve the result below is a slight modification of the one presented in this answer: how to detect a square:
The original program can be found inside OpenCV, it's called squares.cpp. The code below was modified to search squares only in the first color plane, but as it still detects many squares, at the end of the program I discard all of them except the first, and then call draw_squares() to show what was detected. You can change this easilly to draw all of them and see everything that was detected.
You can do all sorts of thing from now own, including setting a (ROI) region of interest to extract the area that's inside the square (ignore everything else around it).
You can see that the detected rectangle is not perfectly aligned with the lines in the image. You should perform some pre-processing (erode?) operations in the image to decrease the thickness of lines and improve the detection. But from here on it's all on you:
#include <cv.h>
#include <highgui.h>
using namespace cv;
double angle( cv::Point pt1, cv::Point pt2, cv::Point pt0 ) {
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}
void find_squares(Mat& image, vector<vector<Point> >& squares)
{
// TODO: pre-processing
// blur will enhance edge detection
Mat blurred(image);
medianBlur(image, blurred, 9);
Mat gray0(blurred.size(), CV_8U), gray;
vector<vector<Point> > contours;
// find squares in the first color plane.
for (int c = 0; c < 1; c++)
{
int ch[] = {c, 0};
mixChannels(&blurred, 1, &gray0, 1, ch, 1);
// try several threshold levels
const int threshold_level = 2;
for (int l = 0; l < threshold_level; l++)
{
// Use Canny instead of zero threshold level!
// Canny helps to catch squares with gradient shading
if (l == 0)
{
Canny(gray0, gray, 10, 20, 3); //
// Dilate helps to remove potential holes between edge segments
dilate(gray, gray, Mat(), Point(-1,-1));
}
else
{
gray = gray0 >= (l+1) * 255 / threshold_level;
}
// Find contours and store them in a list
findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
// Test contours
vector<Point> approx;
for (size_t 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);
// 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 &&
fabs(contourArea(Mat(approx))) > 1000 &&
isContourConvex(Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = MAX(maxCosine, cosine);
}
if (maxCosine < 0.3)
squares.push_back(approx);
}
}
}
}
}
void draw_squares(Mat& img, vector<vector<Point> > squares)
{
for (int i = 0; i < squares.size(); i++)
{
for (int j = 0; j < squares[i].size(); j++)
{
cv::line(img, squares[i][j], squares[i][(j+1) % 4], cv::Scalar(0, 255, 0), 1, CV_AA);
}
}
}
int main(int argc, char* argv[])
{
Mat img = imread(argv[1]);
vector<vector<Point> > squares;
find_squares(img, squares);
std::cout << "* " << squares.size() << " squares were found." << std::endl;
// Ignore all the detected squares and draw just the first found
vector<vector<Point> > tmp;
if (squares.size() > 0)
{
tmp.push_back(squares[0]);
draw_squares(img, tmp);
}
//imshow("squares", img);
//cvWaitKey(0);
imwrite("out.png", img);
return 0;
}
when resizing the image, the image is normally first blurred with a filter, e.g. Gaussian, in order to get rid of high frequencies. The fact that resized one works better is likely because your original image is somehow noisy.
Try blur the image first, e.g. with cv::GaussianBlur(src, target, Size(0,0), 1.5), then it should be equivalent to resizing. (It forgot the theory, if it does not work, try 3 and 6 as well)
Try using a preprocessing pass with the erosion filter. It will give you the same effect as the downscaling - the lines will become thinner and will not disappear at the same time.
The "Blur" filter is also a good idea, as chaiy says.
This way (with blur) it will become something like http://www.ic.uff.br/~laffernandes/projects/kht/index.html (Kernel Based Hough Transform)