Blurring an image tile-by-tile - c++

I'm trying to segment an input image and blur it tile-by-tile, but after all cv::blur invocations on adjacent tiles I've got border pixels, which differ from what I've got when collectively applying cv::blur to the whole image at once.
Mat upper(im, Rect( 0,0, 10,10 ));
Mat lower(im, Rect( 0,11, 10,20 ))
blur( upper, upper, Size( 5, 5 ) );
blur( lower, lower, Size( 5, 5 ) );
It looks like the library version I use (2.4.8) doesn't do what I expect reading through the following:
Unlike the earlier versions of OpenCV, now the filtering operations fully support the notion of image ROI, that is, pixels outside of the ROI but inside the image can be used in the filtering operations.
(Taken from: See FilterEngine::apply description here)
P.S.1: I've tried to extract the cv::boxFilter implementation and change the srcRoi parameter value, but have wrong results either.
Mat src = im.clone();
Mat dst = src; // Trying to perform the operation in-place
Size ksize( 5, 5 );
Point anchor(-1,-1);
Ptr<FilterEngine> f = createBoxFilter(
src.type(), dst.type(),
ksize, anchor, true, BORDER_DEFAULT
);
f->apply(
src, dst,
Rect(0,0,10,10),
Point(0,0), false
);
f->apply(
src, dst,
Rect(0,0,10,10),
Point(0,11), false
);
P.S.2: Help on coloring the source code would be helpful.

The problem you are seeing is because you are trying to do this in-place. Once you've blurred part of the image then you have invalidated source pixels that would be needed for blurring any adjacent part of the image. The solution is to not do this in-place, so that the original source pixels are available for whatever part of the image you want to blur.

Related

opencv connect thin lines

I have a black and white image with lines. some of these lines, however, are not perfectly connected where they should be (though they are close) I have attached an example.
I want to make it so that the lines are close to 1px thick. I have been playing with a few ideas, but not having much sucess. I have tried dilate erote, and dilate like such:
int dsize = 5;
cv::Mat element = getStructuringElement(cv::MORPH_CROSS,
cv::Size(2*dsize + 1, 2*dsize + 1),
cv::Point( dsize, dsize ) );
cv::dilate( src, src, element );
Is there a better way, as op[p[osed to just dilating and eroding to do specifically what I am after?
There is at least a couple of solutions we can try out, but I'm gonna need more info about your problem. For example, are you trying to close the (in)complete contour of a detected object? How much "contour degradation" are you willing to take to approximate a fully closed contour?
Here's a first and very basic solution, assuming you need a 1 pixel width contour. It involves dilating the image N times and then applying a thinning/skeletonize transformation. (The function is part of the Extended Image Processing module of OpenCV ).
Let's see the code:
#include <opencv2/ximgproc.hpp>
//Read input image:
std::string imagePath = "C://opencvImages//lineImg.png";
cv::Mat imageInput= cv::imread( imagePath );
//Convert it to grayscale:
cv::Mat grayImg;
cv::cvtColor( imageInput, grayImg, cv::COLOR_BGR2GRAY );
//Get binary image via Otsu:
cv::threshold( grayImg, grayImg, 0, 255 , cv::THRESH_OTSU );
//Dilate the binary image with 5 iterations:
cv::Mat morphKernel = cv::getStructuringElement( cv::MORPH_RECT, cv::Size(3, 3) );
int morphIterations = 5;
cv::morphologyEx( grayImg, grayImg, cv::MORPH_DILATE, morphKernel, cv::Point(-1,-1), morphIterations );
This is the Dilated image:
//Get the skeleton:
cv::Mat skel;
int algorithmType = 1;
cv::ximgproc::thinning( grayImg, skel, algorithmType );
This is the Skeleton Image. The line has been "thinned" back to a width of 1 pixel:
I don't know if this is good enough for your application, but, as I said, depending on what you are doing we can try a couple of alternative solutions.
Is it you who draw the lines to the mat, It seems like the problem should be take in hands before.
You should draw line in a bigger cv::mat then resize to make your line thicker.
if you want to have complete line, don't draw each points on the map but line between points to get line from bresenham.

resize a Matrix after created it in OpenCV

I'm new to OpenCV and I was looking at the Canny tutorial for Edge Detection.
I was looking on how to resize a mat just created. The code is this:
src = imread( impath );
...
dst.create( src.size(), src.type() );
now I tried to resize the mat with this:
resize(dst, dst, dst.size(), 50, 50, INTER_CUBIC);
But it does not seems to change anything.
My doubts are two :
1 : Am I doing well calling resize() after create() ?
2 : How can I specify the dimensions of the mat ?
My goal is to resize the image, if it was not clear
You create dst mat with the same size as src. Also when you call resize you pass both destination size and fx/fy scale factors, you should pass something one:
Mat src = imread(...);
Mat dst;
resize(src, dst, Size(), 2, 2, INTER_CUBIC); // upscale 2x
// or
resize(src, dst, Size(1024, 768), 0, 0, INTER_CUBIC); // resize to 1024x768 resolution
UPDATE: from the OpenCV documentation:
Scaling is just resizing of the image. OpenCV comes with a function
cv2.resize() for this purpose. The size of the image can be specified
manually, or you can specify the scaling factor. Different
interpolation methods are used. Preferable interpolation methods are
cv2.INTER_AREA for shrinking and cv2.INTER_CUBIC (slow) &
cv2.INTER_LINEAR for zooming. By default, interpolation method used is
cv2.INTER_LINEAR for all resizing purposes. You can resize an input
image either of following methods:
import cv2
import numpy as np
img = cv2.imread('messi5.jpg')
res = cv2.resize(img,None,fx=2, fy=2, interpolation = cv2.INTER_CUBIC)
#OR
height, width = img.shape[:2]
res = cv2.resize(img,(2*width, 2*height), interpolation = cv2.INTER_CUBIC)
Also, in Visual C++, I tried both methods for shrinking and cv::INTER_AREA works significantly faster than cv::INTER_CUBIC (as mentioned by OpenCV documentation):
cv::Mat img_dst;
cv::resize(img, img_dst, cv::Size(640, 480), 0, 0, cv::INTER_AREA);
cv::namedWindow("Contours", CV_WINDOW_AUTOSIZE);
cv::imshow("Contours", img_dst);

OpenCV rotate, distort and translate ROI in new image

I have an image from which I want to get a vertical ROI, apply some transformations and add to another image.
I read a lot of questions and answer on StackOverflow and other forums, but I'm still stuck with this problem. For the moment I'm using the C interface of OpenCV, but I could use the C++ one if needed (I would have to write a conversion function, since I'm working with CGImageRef in Cocoa).
To get from the top image (see below) to the bottom image, I guess I have to :
Get the ROI on the first image ;
Scale it down ;
Get the intersection points on the lines between the center and the 2 circles for my "width" angle (the angle is fixed) ;
Distort the image so the corners stick to my intersection points ;
Rotate around the center point and put it in the output image.
For the moment, I manage well to do this :
Getting the ROI ;
Scaling it with cvResize ;
Getting the intersection points shouldn't be too complicated, as it is pure geometry and I implemented it yet for another purpose.
But, I have no idea at all of how to distort the resulting image of my ROI, and I don't know if it is even possible in OpenCV. Would I have to use a kind of perspective correction ?
And, I've been trying the few good posts solutions I found by here to rotate with the rotated bounding box, but with no good results for the moment.
EDIT :
Well, I managed to do the first part of the work :
Getting a ROI in a basis image ;
Rotating and placing it at a fixed distance from the center.
I used the method explained and coded in this post : https://stackoverflow.com/a/16285286/1060921
I only added a variable to set the rotation point and get my inner circle.
NB : I set the ROI BEFORE to call the method, so the ROI in the post method is... the image size. Then I place it at the center of my final image with a cvAdd.
Here I get one pixel slices of my camera input. What I want to do now is to distort bigger slices, for example from 2 pixels on the inner circle to 5 pixels on the outer one.
See this tutorial which uses warpPerspective to correct perspective distortion.
EDIT: In your case warpAffine should be better and simpler solution.
So, you could do something like this, just use four points instead of three:
Point2f srcTri[3];
Point2f dstTri[3];
Mat rot_mat( 2, 3, CV_32FC1 );
Mat warp_mat( 2, 3, CV_32FC1 );
Mat src, warp_dst, warp_rotate_dst;
/// Load the image
src = imread( ... );
/// Set the dst image the same type and size as src
warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
/// Set your 3 points to calculate the Affine Transform
srcTri[0] = Point2f( 0,0 );
srcTri[1] = Point2f( src.cols - 1, 0 );
srcTri[2] = Point2f( 0, src.rows - 1 );
dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 );
dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 );
dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 );
/// Get the Affine Transform
warp_mat = getAffineTransform( srcTri, dstTri );
/// Apply the Affine Transform just found to the src image
warpAffine( src, warp_dst, warp_mat, warp_dst.size() );

Filter out only one contour in OpenCV C/C++

I'm trying to make a program to detect an object in any shape using a video camera/webcam based on Canny filter and contour finding function. Here is my program:
int main( int argc, char** argv )
{
CvCapture *cam;
CvMoments moments;
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* contours = NULL;
CvSeq* contours2 = NULL;
CvPoint2D32f center;
int i;
cam=cvCaptureFromCAM(0);
if(cam==NULL){
fprintf(stderr,"Cannot find any camera. \n");
return -1;
}
while(1){
IplImage *img=cvQueryFrame(cam);
if(img==NULL){return -1;}
IplImage *src_gray= cvCreateImage( cvSize(img->width,img->height), 8, 1);
cvCvtColor( img, src_gray, CV_BGR2GRAY );
cvSmooth( src_gray, src_gray, CV_GAUSSIAN, 5, 11);
cvCanny(src_gray, src_gray, 70, 200, 3);
cvFindContours( src_gray, storage, &contours, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, cvPoint(0,0));
if(contours==NULL){ contours=contours2;}
contours2=contours;
cvMoments(contours, &moments, 1);
double m_00 = cvGetSpatialMoment( &moments, 0, 0 );
double m_10 = cvGetSpatialMoment( &moments, 1, 0 );
double m_01 = cvGetSpatialMoment( &moments, 0, 1 );
float gravityX = (m_10 / m_00)-150;
float gravityY = (m_01 / m_00)-150;
if(gravityY>=0&&gravityX>=0){
printf("center point=(%.f, %.f) \n",gravityX,gravityY); }
for (; contours != 0; contours = contours->h_next){
CvScalar color = CV_RGB(250,0,0);
cvDrawContours(img,contours,color,color,-1,-1, 8, cvPoint(0,0));
}
cvShowImage( "Input", img );
cvShowImage( "Contours", src_gray );
cvClearMemStorage(storage);
if(cvWaitKey(33)>=0) break;
}
cvDestroyWindow("Contours");
cvDestroyWindow("Source");
cvReleaseCapture(&cam);
}
This program will detect all contours captured by the camera and the average coordinate of the contours will be printed. My question is how to filter out only one object/contour so I can get more precise (x,y) position of the object? If possible, can anyone show me how to mark the center of the object by using (x,y) coordinates?
Thanks in advance. Cheers
p/s:Sorry I couldn't upload a screenshot yet but if anything helps, here's the link.
Edit: To make my question more clear:
For example, if I only want to filter out only the square from my screenshot above, what should I do?
The object I want to filter out has the biggest contour area and most importantly has a shape(any shape), not a straight or a curve line
I'm still experimenting with the smooth and canny values so if anybody have the problem to detect the contours using my program please alter the values.
I think it can be solved fairly easy. I would suggest some morphological operations before contour detection. Also, I would suggest filtering "out" smaller elements, and getting the biggest element as the only one still in the image.
I suggest:
for filtering out lines (straight or curved): you have to decide what do you yourself consider a border between a "line" and a "shape". Let's say you consider all the objects of a thickness 5 pixel or more to be objects, while the ones that are less than 5 pixels across to be lines. An morphological opening that uses a 5x5 square or a 3-pixel sized diamond shape as a structuring element would take care of this.
for filtering out small objects in general: if objects are of arbitrary shapes, purely morphological opening won't do: you have to do an algebraic opening. A special type of algebraic openings is an area opening: an operation that removes all the connected components in the image that have (pixel) area smaller than a given threshold. If you have an upper bound on the size of uninteresting objects, or a lower bound on the size of interesting ones, that value should be used as a threshold. You can probably get a similar effect with a larger morphological opening, but it will not be so flexible.
for filtering out all the objects except the largest: it sounds like removing connected components from the smallest one to the largest one should work. Try labeling the connected components. On a binary (black & white image), this image transformation works by creating a greyscale image, labeling the background as 0 (black), and each component with a different, increasing grey value. In the end, pixels of each object are marked by a different value. You can now simply look at the gray level histogram, and find the grey value with the most pixels. Set all the other grey levels to 0 (black), and the only object left in the image is the biggest one.
The suggestions are written from the simplest to the most complex ones. Still, I think OpenCV can be of help with any of these. Morphological erosion, dilation, opening and closing are implemented in OpenCV. I think you might need to construct an algebraic opening operator on your own (or play with combining OpenCV basic morphology), but I'm sure OpenCV can help you with both labeling the connected components and examining the histogram of the resulting greyscale image.
In the end, when only pixels from one object are left, you do the Canny contour detection.
This is a blob processing problem that can not be solved (easily) by OpenCV itself. Have a look at cvBlobsLib. This library is extends OpenCV with functions/classes for connected component labeling.
http://opencv.willowgarage.com/wiki/cvBlobsLib

Different result with OPENCV C and C++ API (Border Interpolation difference)

I have performed Closing morphological operation and I am getting different result with the C and C++ API (OpenCV 2.4.2)
Input:
With OpenCV 'C':
//Set ROI
//Perform Gaussian smoothing
//Perform Canny edge analysis
cvMorphologyEx( src, dst, temp, Mat(), MORPH_CLOSE, 5 );
RESULT:
http://i47.tinypic.com/33e0yfb.png
With Opencv C++
//Set ROI
//Perform Gaussian smoothing
//Perform Canny edge analysis
cv::morphologyEx( src, dst, cv::MORPH_CLOSE, cv::Mat(), cv::Point(-1,-1), 5 );
RESULT:
http://i50.tinypic.com/i5vxjo.png
As you can see, the C++ API yields an output with White/Gray border color. Hence, the results are different for both of these APIs.
I have tried different borderType with the C++ API but it always yields the same result.
How can I get the same output as C API in C++? I need it because it affects the detected contours
Thanks in advance
Thank you everybody for answering this question. I have found my error. I am going to describe it in brief below. Hope it helps others facing this problem.
1) I had executed the C and C++ commands on a ROI image. Apparently, the way OpenCV 'C' and 'C++' API treat ROI is different.
2) In 'C', a ROI is treated as a completely different image. Hence, when you execute functions such as cvSmooth, cvDilate, etc, where one needs to mentions border Pixel extrapolation methods, the 'C' API does not refer back to the original image for pixels beyond left/right/top/bottom most pixel. It actually interpolates the pixel values according to the method you mentioned.
3) But in 'C++', I have found that it always refers back to the original image for pixels beyond left/right/top/bottom most pixel. Hence, the border pixel extrapolation method mentioned doesn't affect your output if there are pixels in the original image around your ROI.
I think it applies the order pixel extrapolation method to the original image instead of the ROI unlike the 'C' API. I don't know if this a bug; I haven't completely read the OpenCV 2.4.2 C++ API documentation. (Please correct me if I am wrong)
To claim my support, I have posted input/output images below:
Output for 'C' and C++ API:
INPUT:
<--- input
OpenCV 'C' API:
IplImage *src = cvLoadImage("input.png", 0);
cvSetImageROI( src, cvRect(33,19,250,110));
cvSaveImage( "before_gauss.png", src );
cvSmooth( src, src, CV_GAUSSIAN );
cvSaveImage("after_gauss.png", src);
IplConvKernel *element = cvCreateStructuringElementEx(3,3,1,1,CV_SHAPE_RECT);
cvCanny( src, src, 140, 40 );
cvSaveImage("after_canny.png", src);
cvDilate( src, src, element, 5);
cvSaveImage("dilate.png", src);
OUTPUT:
<-- before_gauss
<--- after_gauss
<--- after_canny
<--- dilate
OpenCV 'C++' API:
cv::Mat src = cv::imread("input.png", 0);
cv::Mat src_ROI = src( cv::Rect(33,19,250,110));
cv::imwrite( "before_gauss.png", src_ROI );
cv::GaussianBlur( src_ROI, src_ROI, cv::Size(3,3),0 );
cv::imwrite( "after_gauss.png", src_ROI );
cv::Mat element = cv::getStructuringElement( cv::MORPH_RECT, cv::Size(3, 3), cv::Point(1,1));
cv::Canny( src_ROI, src_ROI, 140, 40);
cv::imwrite( "after_canny.png", src_ROI );
cv::dilate( src_ROI, src_ROI, element, cv::Point(1,1), 5);
cv::imwrite( "dilate.png", src_ROI );
OUTPUT:
<-- before_gauss
^^^^^ after_gauss (NOTE: the borders are no more completely black, they are grayish)
^^^^^ after_canny
^^^^^ dilate
SOLUTION:
Create a separate ROI copy and use it for further analysis;
src_ROI.copyTo( new_src_ROI );
Use new_src_ROI for further analysis.
If anyone has better solution, please post below
The defaults are not the same between C and C++ - especially the structuring element.
In C: the default structuring element is:
cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT)
whereas in C++, the default structuring element is:
getStructuringElement(MORPH_RECT, Size(1+iterations*2,1+iterations*2));
You should specify all fields(including the anchor) if you want the same results.
Check out this sample code from the OpenCV v2.4.2 documentation. You might also want to check this code for using the Canny edge detector. These will hopefully help you track down the error :)
Also note that morphological closing is an idempotent operator, so it can be applied multiple times without changing the result beyond the initial application.