I need to know how to clean noise from an image with Matlab.
lets look at this example:
as you see the numbers is not look clearly.
so how can I clean the noise and the pixels that are not the numbers so the identification will be easier.
thanks.
Let's do it step by step in Mathematica:
(*first separate the image in HSB channels*)
i1 = ColorSeparate[ColorNegate#yourColorImage, "HSB"]
(*Let's keep the B Channel*)
i2 = i1[[3]]
(*And Binarize it *)
i3 = Binarize[i2, 0.92]
(*Perform a Thinning to get the skeleton*)
i4 = Thinning[i3]
(*Now we cut those hairs*)
i5 = Pruning[i4, 10]
(*Remove the small lines*)
i6 = DeleteSmallComponents[i5, 30]
(*And finally dilate*)
i7 = Dilation[i6, 3]
(*Now we can perform an OCR*)
TextRecognize#i7
-->"93 269 23"
Done!
Since this question is tagged MATLAB, I translated #belisarius's solution as such (which I think is superior to the currently accepted answer):
%# read image
I = imread('http://i.stack.imgur.com/nGNGf.png');
%# complement it, and convert to HSV colorspace
hsv = rgb2hsv(imcomplement(I));
I1 = hsv(:,:,3); %# work with V channel
%# Binarize/threshold image
I2 = im2bw(I1, 0.92);
%# Perform morphological thinning to get the skeleton
I3 = bwmorph(I2, 'thin',Inf);
%# prune the skeleton (remove small branches at the endpoints)
I4 = bwmorph(I3, 'spur', 7);
%# Remove small components
I5 = bwareaopen(I4, 30);
%# dilate image
I6 = imdilate(I5, strel('square',2*3+1));
%# show step-by-step results
figure('Position',[200 150 700 700])
subplot(711), imshow(I)
subplot(712), imshow(I1)
subplot(713), imshow(I2)
subplot(714), imshow(I3)
subplot(715), imshow(I4)
subplot(716), imshow(I5)
subplot(717), imshow(I6)
Finally you can apply some form of OCR to recognize the numbers. Unfortunately, MATLAB has no built-in function equivalent to TextRecognize[] in Mathematica... In the meanwhile, look in the File Exchange, I'm sure you will find dozens of submissions filling the gap :)
Did you start with a bilevel (two color, black and white)? Or did you threshold it yourself?
If it's the latter, you may find it easier to perform noise reduction before you threshold. In this case, please upload the image you have before thresholding.
If it's the former, then you'll have a tough time as traditional noise reduction is concerned. The reason is that a lot of noise reduction approaches take advantage of the distinction in statistical properties between the noise and the actual natural image. By thresholding, that distinction is essentially destroyed.
EDIT
OK, technically, your image isn't really noisy -- it's blurry (letters are running into each other) and has background interference.
But anyway, here is how I would deal with it:
Pick a color channel to work with (RGB is three channels, typically one is enough). I chose green because it looked the easiest to manipulate.
Blur the image (I used a 5x5 Gaussian kernel in GIMP)
Threshold using an empirically determined threshold (basically, try each threshold until you get a decent result). It's OK if some of the numbers have gaps -- we can close them in the next step
Morphological image processing (erosion and dilation)
Green channel:
Blur (5x5 Gaussian):
Thresholded image (I used a threshold of ~93 in GIMP):
Final result:
You can see that the gaps in the middle 6 and 9 have dissapeared. Unfortunately, I couldn't get the gap in the left 3 to go away -- it's simply too large. Here's what the problems causing this are:
The line along the top of the image is much darker than some parts of the 3. If you use a threshold to remove the line, then a gap will be created. If you were to somehow remove that line (e.g. by more zealous cropping), the thresholding result would be much better as far as the 3 is concerned.
Also, the middle 2 and 6 are running together. Heavy thresholding is required to prevent them from both forming the same blob after thresholding.
I think there are two things you could aim to do to make them more detectable:
Remove patches smaller than a certain number of pixels (this would remove the spots between the sets of digits)
Numbers should be "closed" forms, so you need an algorithm to detect the pixels (at the top of each number) that should be changed to black in order to "close" the number "shapes".
You also have linear features that are a part of the noise signal which could be detected through edge / line detection.
Detecting contiguous "zones" and calculating characteristics such as compactness or length / height might also help in identifying which structures to keep...
Related
hope you might have a suggestion for my struggle (matlab). This is the result of the Imfill holes function, but it left a lot of segments unfilled. Is there anything I can attempt to fix this?
imfill(BW1,'holes')
You may use bwmorph with 'bridge' argument for bridging small gaps before imfill.
'bridge'
Bridges unconnected pixels, that is, sets 0-valued pixels to 1 if they have two nonzero neighbors that are not connected.
Here is a code sample:
I = imread('holes.jpg');
% Remove top and bottom "white frame".
I = I(7:end-6, :);
% Convert to binary image with manual threshold (could be that binarize is required only due to JPEG artifacts).
I = imbinarize(I, 0.3);
% Bridges unconnected pixels (morphological operation).
J = bwmorph(I, 'bridge');
% Fill holes
K = imfill(J, 'holes');
Remarks:
Please consider posting the original image (before fill).
Consider posting images in PNG format instead of JPEG - JPEG compression looses information and creates compression artifacts.
The image you have posted has a "white frame", that is probably not part of the original image. I decided to keep the left and right parts for filling the shapes next to the borders.
Result:
I am trying to write a program that uses computer vision techniques to detect (and track) tiny blobs in a stream of very noisy images. The image stream comes from an dual X ray imaging setup, which outputs left and right views (different sizes because of collimating differently). My data is of two types: one set of images are not so noisy, which I am just using to try different techniques with, and the other set are noisier, and this is where the detection needs to work at the end. The image stream is at 60 Hz. This is an example of a raw image from the X ray imager:
Here are some cropped out samples of the regions of interest. The blobs that need to be detected are the small black spots near the center of the image.
Initially I started off with a simple contour/blob detection techniques in OpenCV, which were not very helpful. Eventually I moved on to techniques such as "opening" the image using morphological operators, and subsequently performing a Laplacian of Gaussian blob detection to detect areas of interest. This gave me better results for the low-noise versions of the images, but fails when it comes to the high-noise ones: gives me too many false positives. Here is a result from a low-noise image (please note input image was inverted).
The code for my current LoG based approach in MATLAB goes as below:
while ~isDone(videoReader)
frame = step(videoReader);
roi_frame = imcrop(frame, [660 410 120 110]);
I_roi = rgb2gray(roi_frame);
I_roi = imcomplement(I_roi);
I_roi = wiener2(I_roi, [5 5]);
background = imopen(I_roi,strel('disk',3));
I2 = imadjust(I_roi - background);
K = imgaussfilt(I2, 5);
level = graythresh(K);
bw = im2bw(I2);
sigma = 3;
% Filter image with LoG
I = double(bw);
h = fspecial('log',sigma*30,sigma);
Ifilt = -imfilter(I,h);
% Threshold for points of interest
Ifilt(Ifilt < 0.001) = 0;
% Dilate to obtain local maxima
Idil = imdilate(Ifilt,strel('disk',50));
% This is the final image
P = (Ifilt == Idil) .* Ifilt;
Is there any way I can improve my current detection technique to make it work for images with a lot of background noise? Or are there techniques better suited for images like this?
The approach I would take:
-Average background subtraction
-Aggressive Gaussian smoothing (this filter should be shaped based on your target object, off the top of my head I think you want the sigma about half the smallest cross section of your object, but you may want to fiddle with this) Basically the goal is blurring the noise as much as possible without completely losing your target objects (based on shape and size)
-Edge detection. Try to be specific to the object if possible (basically, look at what the object's edge looks like after Gaussian smoothing and set your edge detection to look for that width and contrast shift)
-May consider running a closing operation here.
-Search the whole image for islands (fully enclosed regions) filter based on size and then on shape.
I am taking a hunch that despite the incredibly low signal to noise ratio, your granularity of noise is hopefully significantly smaller than your object size. (if your noise is both equivalent contrast and same ballpark size as your object... you are sunk and need to re-evaluate your acquisition imo)
Another note based on your speed needs. Extreme amounts of processing savings can be made through knowing last known positions and searching locally and also knowing where new targets can enter the image from.
My input Image is
output image is
Expected output is something like this
it is seen that some of the ellipse like structures are merged with rectangle.Also I'm unable to separate each labels to get the ellipses
Algorithm used is watershed
clear; close all;
I = imread('Sub.png');
I = rgb2gray(I);
figure; imshow(I)
I2 = imtophat(I, strel('square', 45));
figure; imshow(I2)
% Alpha=.047;
% h = fspecial('motion', 10, 5);
% w=gausswin(I2,Alpha) % you'll have to play with N and alpha
% I2 = imfilter(I2,h,'same','symmetric'); % something like these options
level = .047;
BW = im2bw(I2,level);
D = -bwdist(~BW,'chessboard');
D(~BW) = -Inf;
L = watershed(D);
imshow(label2rgb(L,'jet','w'))
ultimate opening code :
ImageSource=imread('cameraman.tif');
ImTmp=ImageSource
ImResidue = zeros(size(ImageSource));
ImIndicator= zeros(size(ImageSource));
ImValues= zeros(size(ImageSource));
For size= 1 : N
se = strel('square',N);
ImOp = imopen(ImageSource,se);
ImDiff=imabsdiff(ImOp,ImTmp)
if ImResidue < ImDiff then
ImResidue = ImDiff
ImIndicator = size
ImValues = ImOp
end
ImTmp=ImOp
end
You have to use a watershed with markers if you want something accurate, but it's going to be more tricky. By default the basic watershed over segment because it uses each local minima as a marker.
Therefore, you have to preprocess a little bit your image in order to increase the separation between the objects you want to segment, and then use markers in order to guide your watershed.
[EDIT according to you EDIT] If you want just the little structures between the vertebras, then I would recommend to perform a small erosion in order to increase the gap between them, followed by an ultimate opening. The structures you want will disappear for small radii, when the vertebras will need bigger ones.
Don't forget to use the markers on the gradient image.
[EDIT 2, preliminary results] I was curious about your problem, so I gave it a try. Instead of going to the small regions between the vertebras (the one you want to segment), I tried to first segment the vertebras (what you want being between them).
Here is what I did:
Small opening (square order 1, square is faster and fine because you patient is well oriented in the image, else disk or hexagon) in order to increase the gap between the vertebras and their neighborhood.
Area Opening (surface 23, but it does not really matter) in order to flatten the different zones, so erase the peaks.
Area Closing (surface 23, but it does not really matter) in order to flatten again the different zones, so fill the holes. See the image result at this point. See, everything looks smoother, but the different boundaries/rims are still intact.
Ultimate opening (UO).
ThresholdingS according to the ultimate opening results (residues, values and indicators). See the vertebras approximation. I haven't the values anymore (I've deleted my code), but you can take a look to the UO results and find them back. However, I still have the UO results if you want.
Opening (disk order 7) in order to erase all the artifacts and false positive (the vertebras being big)
Same operations as 5 in order to approximate the small patterns you want to segment. See the results
Small erosion of result on step 7 in order to have the outer markers (between the vertebras). I complete the outer markers with the boundary of a big dilation (disk order 11) of results of step 6.
Here are the markers I get.
Watershed with the computed markers, here is the preliminary result
The patterns you want to segment are between the vertebras, so I guess this result narrows down a lot the region of interest.
Does it work for you?
I cannot share the code, but I guess that you should find everything in MatLab.
You can improve this result by detecting the rectangular shapes.
Here with i have attached two consecutive frames captured by a cmos camera with IR Filter.The object checker board was stationary at the time of capturing images.But the difference between two images are nearly 31000 pixels.This could be affect my result.can u tell me What kind of noise is this?How can i remove it.please suggest me any algorithms or any function possible to remove those noises.
Thank you.Sorry for my poor English.
Image1 : [1]: http://i45.tinypic.com/2wptqxl.jpg
Image2: [2]: http://i45.tinypic.com/v8knjn.jpg
That noise appears to result from camera sensor (Bayer to RGB conversion). There's the checkerboard pattern still left.
Also lossy jpg contributes a lot to the process. You should first have an access to raw images.
From those particular images I'd first try to use edge detection filters (Sobel Horizontal and Vertical) to make a mask that selects between some median/local histogram equalization for the flat areas and to apply some checker board reducing filter to the edges. The point is that probably no single filter is able to do good for both jpeg ringing artifacts and to the jagged edges. Then the real question is: what other kind of images should be processed?
From the comments: if corner points are to be made exact, then the solution more likely is to search for features (corner points with subpixel resolution) and make a mapping from one set of points to the other images set of corners, and search for the best affine transformation matrix that converts these sets to each other. With this matrix one can then perform resampling of the other image.
One can fortunately estimate motion vectors with subpixel resolution without brute force searching all possible subpixel locations: when calculating a matched filter, one gets local maximums for potential candidates of exact matches. But this is not all there is. One can try to calculate a more precise approximation of the peak location by studying the matched filter outputs in the nearby pixels. For exact match the output should be symmetric. Otherwise the 'energies' of the matched filter are biased towards the second best location. (A 2nd degree polynomial fit + finding maximum can work.)
Looking closely at these images, I must agree with #Aki Suihkonen.
In my view, the main noise comes from the jpeg compression, that causes sharp edges to "ring". I'd try a "de-speckle" type of filter on the images, and see if this makes a difference. Some info that can help you implement this can be found in this link.
In a more quick and dirty fashion, you apply one of the many standard tools, for example, given the images are a and b:
(i) just smooth the image with a Gaussian filter, this can reduce noise differences between the images by an order of magnitude. For example:
h=fspecial('gaussian',15,2);
a=conv2(a,h,'same');
b=conv2(b,h,'same');
(ii) Reduce Noise By Adaptive Filtering
a = wiener2(a,[5 5]);
b = wiener2(b,[5 5]);
(iii) Adjust ntensity Values Using Histogram Equalization
a = histeq(a);
b = histeq(b);
(iv) Adjust Intensity Values to a Specified Range
a = imadjust(a,[0 0.2],[0.5 1]);
b = imadjust(b,[0 0.2],[0.5 1]);
If your images are supposed to be black and white but you have captured them in gray scale there could be difference due to noise.
You can convert the images to black and white by defining a threshold, any pixel with a value less than that threshold should be assigned 0 and anything larger than that threshold should be assigned 1, or whatever your gray scale range is (maybe 255).
Assume your image is I, to make it black and white assuming your gray scale image level is from 0 to 255, assume you choose a threshold of 100:
ind = find(I < 100);
I(ind) = 0;
ind = find(I >= 100);
I(ind) = 255;
Now you have a black and white image, do the same thing for the other image and you should get very small difference if the camera and the subject have note moved.
Anyone knows why the pseudomedian filter is faster than the median filter?
I used medfilt2.m for median filtering and I implemented my own pseudomedian filter which is:
b = strel('square',3);
psmedIm = (0.5*imclose(noisedIm,b)) + (0.5*imopen(noisedIm,b));
where b is a square flat structuring element and noisedIm is an image noised by a salt and pepper noise.
Also I don't understand why the image generated using the pseudomedian filter isn't denoised.
Thank you!
In terms of your speed query, I'd propose that your pseudomedian filter is faster because it doesn't involve sorting. The true median filter requires that you sort elements and find the central value, which takes a fair bit of time.
The reason why your salt and pepper noise isn't removed is that you're always maintaining their effects because you're always using both the min and max values inside the structuring element when you use imclose and imopen. Because you're just weighting each by half, if there's a white pixel, the 0.5 factor contribution from the max function will bump the pixel value up, and vice versa for black pixels.
EDIT: Here's a quick demo I did that helps your pseudomedian behave a little more nicely with salt and pepper noise. The big difference is that it tries to use the 'best parts' of the opened and closed images rather than making them fight it out. I think it works quite well for eliminating the salt and pepper noise you used as an example.
img = imread('cameraman.tif');
img = imnoise(img, 'salt & pepper', 0.01);
subplot(2,2,1); imshow(img);
b = strel('square', 3);
closed = double(imclose(img, b));
opened = double(imopen(img, b));
subplot(2,2,2); imshow(closed,[]);
subplot(2,2,3); imshow(opened,[]);
img = double(img);
img = img + (closed - img) + (opened - img);
subplot(2,2,4); imshow(img,[]);
EDIT: Here's the result of running the code:
EDIT 2: Here's the underlying theory (it's not overly mathematical and based entirely on intuition!)
Salt and pepper noise exists as pure white and pure black pixels scattered randomly. The idea is that the 'closed' and 'opened' images will each eliminate one of the halves -- either the white salt noise or the black pepper noise -- and the pixel value in that location should be corrected by one of the operations. We just don't know which one. So we know that one of the images out of both 'closed' and 'open' is 'correct' for that pixel because the operation should have effectively 'median-ed' that pixel correctly. Since the one that is 'incorrect' should have exactly the same value at that pixel (white or black) as the original image, subtracting its value doesn't affect the original image. Only the 'correct' one (which differs by the exact amount required to return the image to its supposedly correct value) is right, so we adjust the image at that pixel by the corresponding amount. Thus, taking the noisy original image and adding to it both the differences gives us something with much of the noise reduced.