OpenCV get rid of isolated pixels - image-processing

I am trying to remove isolated pixels from an image.
I thought of using:
cvErode(img, img, 0, 1);
The problem is that I want a kernel of:
0 0 0
0 1 0
0 0 0
I'm unsure how to do that. Can anyone help?

You can filter the image with custom kernels in opencv using filter2D function.
Look at the documentation
Documents are always a good source to start from :)

Now you are setting default kernel by passing NULL as 3rd argument.
You should use http://docs.opencv.org/modules/imgproc/doc/filtering.html?highlight=erode#getstructuringelement and pass output as argument for erode function.
If you can not generate your kernel with this function. Simply create IplConvKernel element by hand.

After re-reading the question's title, now I see what you want. You are after the hit-or-miss morphological operator, the kernel you described is actually a 3x3 square perfectly fine for a function that performs a hit-or-miss. It doesn't seem OpenCV support it, but you can perform the equivalent of you what you want by doing a simple analysis of each point neighborhood: if a point isn't connected to any other point, remove it.

Here is my 5 cent event though i dont know openCV at all.
But you should consider looking for a function called "Opening". This in an erosion followed by a dilation. This will remove small isolated pixel. The size of the removed elements will ofcourse depend on the kernel you use.
Another option is finding a function for doing a lowpass filtering of the image.
nomather what you do, it comes down to two steps.
call a function to create a kernel.
apply the kernel to the image using another function.
Whatever you do! Dont just use an "erode" function. It will also change the elements on the remaining image. In that case you should definitly use the "opening" function.

If you're using the new OpenCV 2.x API, you can do it like this:
cv::Mat kernel = (cv::Mat_<uchar>(3,3) << 0, 0, 0,
0, 1, 0,
0, 0, 0);
cv::erode(img, img, kernel);

Related

how to replace values in a tensor?

I have reading an image as a tensor object, which aims to be a mask.
Now, I want to replace values which are close to white (almost 1.0) with 0
and values which are gray to 1.
Then the mask would be correct for my machine learning task.
I have tried it with:
tf.where(imag >= 1.0)
or the next function also returns me the indices
greater = tf.greater_equal(mask, 0.95)
but how to update/assign 0? scatter_nd_add does not work for me.
mask = tf.scatter_nd_add(mask, greater, 0)
Edit:
I tried it differently:
v_mask = tf.Variable(tf.shape(mask))
ind = tf.to_float(mask >= 0.0)
v_mask.assign(ind)
but if I run the session. It stops there and does not go on.
What I really wanna do:
I have a gray image with the dimensions (mxnx1, tensor, float32) and the values are rescaled to from [0,255] to [0,1].
I want to replace all values which are white (1) with 0 and gray (0.45 - 0.55) with 1 and the rest should be undefined.
To threshold your image, you can use:
thim = tf.tofloat(im >= 0.95) # or to whichever type you use
To reassign the result to im, assuming it is a variable:
im_update = im.assign(thim)
This gives you an update op that you need to call for the update to happen.
If im is not a variable, then you cannot reassign values to it. Generally though, cases where you really need to reassign values to a node are scarce.
One workaround I found is to use the numpy() bridge. Do the numpy operations on the numpy array and the same is reflected in the tensor values. This is because, the numpy array and the pytorch tensor use the same underlying memory locations.
Memory sharing is mentioned on the pytorch introductory tutorial here

Why does the Sobel function for edge detection fail to find the contour of a white square in a black background?

I tryed to apply to the image the following code in octave:
sq = imread("Square BW.jpg");
figure(1), imshow(Square);
cont1 = edge(sq,"Sobel");
figure(2), imshow(cont1);
The image I get is:
And a similar image appears if I use the Prewitt function. Can anyone explain to me what is happening? The problem is that I can't visualize the process only the result, so I can't understand why the code isn't working.
The problem seems to be how threshold is computed in Octave. You can see how Octave does it by looking at its source by entering type edge at the Octave prompt, or online (I'm not copying the exact code since the code is GPL -- although quite simple)
To get the border, you will need to set the threshold yourself (hopefully, in future versions of Octave's image package this will be fixed but at the moment it's Matlab incompatible since Matlab documentation on their default is unclear).
There's definitely a problem with the way the threshold is computed, however I wasn't able to find the correct value to use in this picture. After many attempts I found this code that seems to work perfectly:
sq = imread("Square BW.jpg");
maskSobel = fspecial("sobel");
mSobel = uint8(zeros(size(BW)));
for i = 0:3
mSobel += imfilter(sq, rot90(maskSobel, i));
end
figure(1), imshow(mSobel);
First we create the Sobel matrix/operator and a zero matrix the same size of the image Square BW. Then we rotate the Sobel matrix four times (by 90 degrees), in order filter the image in all directions (left-right, up-down, right-left and down-up), always adding the result to the mSobel matrix that was created.
Here's the final result:

Perform Canny Edge Detection twice --> better line-detection?

I'm working on a project with EmguCV (.NET-version of OpenCV) and I'm using the probabilistic Hough Transformation to find lines.
So at first I was performing the canny-operator. Afterwards doing the Hough-transformation.
Gray cannyThreshold = new Gray(50);
Gray cannyThresholdLinking = new Gray(300);
Image<Gray, Byte> cannyEdges = gray.Canny(cannyThreshold, cannyThresholdLinking);
LineSegment2D[] linesFound_temporary = cannyEdges.HoughLines
(
cannyThreshold, // 1. Parameter
cannyThresholdLinking, // 2. Parameter
1, // 3. Parameter
Math.PI / 360.0, // 4. Parameter
gray.Width * 0.2, // 5. Parameter
gray.Width * 0.4, // 6. Parameter
gray.Width * 0.1 // 7. Parameter
)[0];
Later I realised that the HoughLines-Method already integrated the canny edge detection.
Nevertheless, my results in line-detection are better and more steady when I use the additional canny detection instead of leaving it out.
Can anyone explain to me, why this happens? Or has anyone experienced the same?
I experienced the same while doing one of my project. I think it dépends on the parameter given to both function. If the first canny remove too much information and no lines, the second function will suck. If you do a "first pass", removing much of the information but leaving very apparent lines, then the Hough Line has little to do. But I discovered that by tweaking the parameter of the Hough Line in the first time could achieve almost the same result.
Hope it helps!

Automatic approach for removing colord object shadow on white background?

I am working on some leaf images using OpenCV (Java). The leaves are captured on a white paper and some has shadows like this one:
Of course, it's somehow the extreme case (there are milder shadows).
Now, I want to threshold the leaf and also remove the shadow (while reserving the leaf's details).
My current flow is this:
1) Converting to HSV and extracting the Saturation channel:
Imgproc.cvtColor(colorMat, colorMat, Imgproc.COLOR_RGB2HSV);
ArrayList<Mat> channels = new ArrayList<Mat>();
Core.split(colorMat, channels);
satImg = channels.get(1);
2) De-noising (median) and applying adaptiveThreshold:
Imgproc.medianBlur(satImg , satImg , 11);
Imgproc.adaptiveThreshold(satImg , satImg , 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 401, -10);
And the result is this:
It looks OK, but the shadow is causing some anomalies along the left boundary. Also, I have this feeling that I am not using the white background to my benefit.
Now, I have 2 questions:
1) How can I improve the result and get rid of the shadow?
2) Can I get good results without working on saturation channel?. The reason I ask is that on most of my images, working on L channel (from HLS) gives way better results (apart from the shadow, of course).
Update: Using the Hue channel makes threshdolding better, but makes the shadow situation worse:
Update2: In some cases, the assumption that the shadow is darker than the leaf doesn't always hold. So, working on intensities won't help. I'm looking more toward a color channels approach.
I don't use opencv, instead I was trying to use matlab image processing toolbox to extract the leaf. Hopefully opencv has all the processing functions for you. Please see my result below. I did all the operations in your original image channel 3 and channel 1.
First I used your channel 3, threshold it with 100 (left top). Then I remove the regions on the border and regions with the pixel size smaller than 100, filling in the hole in the leaf, the result is shown in right top.
Next I used your channel 1, did the same thing as I did in channel 3, the result is shown in left bottom. Then I found out the connected regions (there are only two as you can see in the left bottom figure), remove the one with smaller area (shown in right bottom).
Suppose the right top image is I1, and the right bottom image is I, the leaf is extracted by implement ~I && I1. The leaf is:
Hope it helps. Thanks
I tried two different things:
1. other thresholding on the saturation channel
2. try to find two contours: shadow and leaf
I use c++ so your code snippets will look a little different.
trying otsu-thresholding instead of adaptive thresholding:
cv::threshold(hsv_imgs,mask,0,255,CV_THRESH_BINARY|CV_THRESH_OTSU);
leading to following images (just OTSU thresholding on saturation channel):
the other thing is computing gradient information (i used sobel, see oppenCV documentation), thresholding that and after an opening-operator I used findContours giving something like this, not useable yet (gradient contour approach):
I'm trying to do the same thing with photos of butterflies, but with more uneven and unpredictable backgrounds such as this. Once you've identified a good portion of the background (e.g. via thresholding, or as we do, flood filling from random points), what works well is to use the GrabCut algorithm to get all those bits you might miss on the initial pass. In python, assuming you still want to identify an initial area of background by thresholding on the saturation channel, try something like
import cv2
import numpy as np
img = cv2.imread("leaf.jpg")
sat = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)[:,:,1]
sat = cv2.medianBlur(sat, 11)
thresh = cv2.adaptiveThreshold(sat , 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 401, 10);
cv2.imwrite("thresh.jpg", thresh)
h, w = img.shape[:2]
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
grabcut_mask = thresh/255*3 #background should be 0, probable foreground = 3
cv2.grabCut(img, grabcut_mask,(0,0,w,h),bgdModel,fgdModel,5,cv2.GC_INIT_WITH_MASK)
grabcut_mask = np.where((grabcut_mask ==2)|(grabcut_mask ==0),0,1).astype('uint8')
cv2.imwrite("GrabCut1.jpg", img*grabcut_mask[...,None])
This actually gets rid of the shadows for you in this case, because the edge of the shadow actually has high saturation levels, so is included in the grab cut deletion. (I would post images, but don't have enough reputation)
Usually, however, you can't trust shadows to be included in the background detection. In this case you probably want to compare areas in the image with colour of the now-known background using the chromacity distortion measure proposed by Horprasert et. al. (1999) in "A Statistical Approach for Real-time Robust Background Subtraction and Shadow Detection". This measure takes account of the fact that for desaturated colours, hue is not a relevant measure.
Note that the pdf of the preprint you find online has a mistake (no + signs) in equation 6. You can use the version re-quoted in Rodriguez-Gomez et al (2012), equations 1 & 2. Or you can use my python code below:
def brightness_distortion(I, mu, sigma):
return np.sum(I*mu/sigma**2, axis=-1) / np.sum((mu/sigma)**2, axis=-1)
def chromacity_distortion(I, mu, sigma):
alpha = brightness_distortion(I, mu, sigma)[...,None]
return np.sqrt(np.sum(((I - alpha * mu)/sigma)**2, axis=-1))
You can feed the known background mean & stdev as the last two parameters of the chromacity_distortion function, and the RGB pixel image as the first parameter, which should show you that the shadow is basically the same chromacity as the background, and very different from the leaf. In the code below, I've then thresholded on chromacity, and done another grabcut pass. This works to remove the shadow even if the first grabcut pass doesn't (e.g. if you originally thresholded on hue)
mean, stdev = cv2.meanStdDev(img, mask = 255-thresh)
mean = mean.ravel() #bizarrely, meanStdDev returns an array of size [3,1], not [3], so flatten it
stdev = stdev.ravel()
chrom = chromacity_distortion(img, mean, stdev)
chrom255 = cv2.normalize(chrom, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX).astype(np.uint8)[:,:,None]
cv2.imwrite("ChromacityDistortionFromBackground.jpg", chrom255)
thresh2 = cv2.adaptiveThreshold(chrom255 , 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 401, 10);
cv2.imwrite("thresh2.jpg", thresh2)
grabcut_mask[...] = 3
grabcut_mask[thresh==0] = 0 #where thresh == 0, definitely background, set to 0
grabcut_mask[np.logical_and(thresh == 255, thresh2 == 0)] = 2 #could try setting this to 2 or 0
cv2.grabCut(img, grabcut_mask,(0,0,w,h),bgdModel,fgdModel,5,cv2.GC_INIT_WITH_MASK)
grabcut_mask = np.where((grabcut_mask ==2)|(grabcut_mask ==0),0,1).astype('uint8')
cv2.imwrite("final_leaf.jpg", grabcut_mask[...,None]*img)
I'm afraid with the parameters I tried, this still removes the stalk, though. I think that's because GrabCut thinks that it looks a similar colour to the shadows. Let me know if you find a way to keep it.

Opencv cvHoughCircles-Parameters doubts

I have some doubts about cvHoughCircles parameters. I have an image that has some circles and I want to count them, the count gives me a wrong number of circles.
So I don't know how to choose some function's parameters like:
dp,min_dist,param1,param2,min_radius, max_radius.
I don't know what numbers I use in this parameters. How Can I calculate this?
Choosing the parameters depends on the images you are using. An explanation of the parameters themselves can be found in the reference here
http://opencv.willowgarage.com/documentation/cpp/imgproc_feature_detection.html#cv-houghcircles
Using the function with the following parameters
HoughCircles(gray, circles, CV_HOUGH_GRADIENT,2, gray->rows/4, 200, 100, 10, 50);
Will make it search for circles with a dp of 2, a minimum distance between the circles of 1/4 of the image and accumulator values of max 200,100 that determine what to accept as a circle. The 10 and 50 are minimum and maximum radius for the circles to accept.
If you have trouble finding these parameters try to make a test program that attaches these values to sliders so you can see the result on a test image.
Especially param2 is something that is difficult to determine by measuring. Because you know how many circles are in your image you can do a parameter crawl in the following way:
for(int i=0;i<200;i++) {
cv::HoughCircles(gray, circles, CV_HOUGH_GRADIENT,2, gray->rows/4, 200, i, 10, 50);
std::cout<<"HoughCircles with param2="<<i<<" gives "<<circles.size()<<" circles"<<endl;
}
I don't know how param1 and 2 are exactly related but you could do the same with a double for loop to find the optimum. The other values need to be measured from the picture. In stead of making a screenshot you can also save this image with the function:
cvSaveImage("image.jpg",gray);
To make sure you are really measuring the exact picture.

Resources