Detecting line artifacts in noisy images - matlab

I'm doing image processing on computed tomography projection images. There's a specific type of artifact that results from the processing I'm doing which manifests as a vertical line going through the whole image:
I'm currently detecting it by comparing the mean of each column. If the mean is less than half the mean of both the left side and the right side column neighbours, then the column is deemed as a line artifact. It is then interpolated as the maximum of the left and right side neighbour pixels.
The interpolation works well (right side of the image), but the detection is too ad hoc. It also fails pretty often, as many of the columns containing only the black background can fulfill that condition due to the heavy Poisson noise apparent. This causes artifacts in filtering out the noise which is the next phase. I'm using BM3D with great results and do not wish to median filter the whole image.
Can you think of a better way to detect these 'line artifacts'? Note the strong borders of the objects in the images and the heavy noise included also in the artifact.

We want to find vertical lines in the image so first convolve the image with the filter [1 -2 1]. This will give high values for pixels that are lower than their vertical neighbors.
Sum all the columns of the image.
Find the index of column with the maximum value. This column is the problematic one.

Related

How to remove image noise (horizontal lines) while filling the hyperbolas? Idea requested :)

I have an image as showin in '1'.
I applied Hessian filter to get the result '2'.
I applied Standard filter (stdfilt) in MATLAB to get the result as in '3'.
I wish to combine these two results into a single image such that:
the horizontal lines (noise in my case) are removed.
the hyperbolic areas are filled.
How could I obtain such a result?
Original (1):
Hessian (2):
stdfilt (3):
Last two without markups:
You can start by binarizing your image. I recommend opencv otsu threshold. it will be hard to solve this problem if gradients are present. After the image is binarized, you will need to fix bridging between the parabolas and the horizontal lines. I recommend morphological erosion, but I am still experimenting with this in my own projects. After that, you can use watershed or DBSCAN to flood-fill each region with a given marker or color. You can then filter out the horizontal lines by pixel count, since they will be much larger than a single parabola. If erosion+DBSCAN doesn't work reliably, you can look into opencv's connectedComponents function.
See:
https://iq.opengenus.org/connected-component-labeling/
https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html
https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html

Area integral invariant computation

Area integral invariant is a type of signature used in image processing. Does anyone know the algorithm for the computation of AII?
i.e. I want to calculate the area enclosed by a boundary and the intersected circle...
the boundary is not a curve with a equation but from a arbitrary profile. The image below is just a schematic drawing. The real boundary can be much more complex with the enclosed area in various positions of the boundary, i.e. top, bottom, left side...
The red area. I am using MATLAB and the image is mostly binary ones.
If you know the equation of the circle and the line then its quite easy, if you are doing this in an image.
Select the pixels that are inside the circle (easily done with the equation of the circle). If you need to compute the AII as a ratio, then count the pixels you have.
Separate the pixels above and below the line. You can do this easily if you know the equation of the line, or the value of the line in each column. Go column by column and discard the pixels that are above the value of the line. Count the result.
That's it! If you want the AII without a ratio, then the number of pixels in 2 is the result. If you want it as a ratio, divide the number of pixels of 2 by the number of pixels of 1.
If you only have the image and no equations, you can still select all the pixels you want by giving your algorithm one pixel in the area you want to calculate and then recusivly check all neighbors and add them to you area, if they are white. When you are done, just count the pixels you have. The result is in some sense the are of the region you wanted.

Problems in implementing the integral image in MATLAB

I tried to implement the integral image in MATLAB by the following:
im = imread('image.jpg');
ii_im = cumsum(cumsum(double(im)')');
im is the original image and ii_im is the integral image.
The problem here is the value in ii_im flows out of the 0 to 255 range.
When using imshow(ii_im), I always get a very bright image which I am not sure is the correct result. Am I correct here?
You're implementing the integral image calculations right, but I don't understand why you would want to visualize it - especially since the sums will go beyond any normal integer range. This is expected as you are performing a summation of intensities bounded by larger and larger rectangular neighbourhoods as you move to the bottom right of the image. It is inevitable that you will get large numbers towards the bottom right. Also, you will obviously get a white image when trying to show this image because most of the values will go beyond 255, which is visualized as white.
If I can add something, one small optimization I have is to get rid of the transposing and use cumsum to specify the dimension you want to work on. Specifically, you can do this:
ii_im = cumsum(cumsum(double(im), 1), 2);
It doesn't matter what direction you specify first (2 then 1, or 1 then 2). The summation of all pixels within each bounded area, as long as you specify all directions to operate on, should be the same.
Back to your question for display, if you really, really, really really... I mean really want to, you can normalize the contrast by doing:
imshow(ii_im, []);
However, what you should expect is a gradient image which starts to be dark from the top, then becomes brighter when you get to the bottom right of the image. Remember, each point in the integral image calculates the total summation of pixel intensities bounded by the top left corner of the image to this point, thus forming a rectangle of intensities you need to sum over. Therefore, as we move further down and to the right of the integral image, the total summation should increase.
With the cameraman.tif image, this is the original image, as well as it's integral image visualized using the above command:
Either way, there is absolutely no reason why you would want to visualize it. You would use this directly with whatever application requires it (adaptive thresholding, Viola-Jones detector, etc.)
Another option could be applying a log operation for each value in the integral image. Something like:
imshow(log(1 + ii_im), []);
However, this will make most of the pixels have the same contrast and this is probably not useful. This is what I get with cameraman.tif:
The moral of this story is that you need some sort of contrast normalization so that you can fit all of the values in your integral image within the confines of the data type that is used to display the image on the screen using imshow.

matlab: remove small edges and simplify an histology image

I have an image like this:
What I want to do is to find the outer edge of this cell and the inner edge in the cell between the two parts of different colors.
But this image contains to much detail I think, and is there any way to simplify this image, remove those small edges and find the edges I want?
I have tried the edge function provided by matlab. But it can only find the outer edge and disturbed by those detailed edges.
This is a very challenging work due to the ambiguous boundaries and tiny difference between red and green intensities. If you want to implement the segmentation very precisely and meet some medical requirements, Shai's k-means plus graph cuts may be one of the very few options (EM algorithm may be an alternative). If you have a large database that has many similar images, some machine learning methods might help. Otherwise, I just wrote a very simple code to roughly extract the internal red region for you. The boundary is not that accurate since some of the green regions are also included.
I1=I;
I=rgb2hsv(I);
I=I(:,:,1); % the channel with relatively large margin between green and red
I=I.*(I<0.25);
I=imdilate(I, true(5));
% I=imfill(I,'holes'); depends on what is your definition of the inner boundary
bw=bwconncomp(I);
ar=bw.PixelIdxList;
% find the largest labeled area,
n=0;
for i=1:length(ar)
if length(ar{i})>n
n=length(ar{i});
num=i;
end
end
bw1=bwlabel(I);
bwfinal(:,:,1)=(bw1==num).*double(I1(:,:,1));
bwfinal(:,:,2)=(bw1==num).*double(I1(:,:,2));
bwfinal(:,:,3)=(bw1==num).*double(I1(:,:,3));
bwfinal=uint8(bwfinal);
imshow(bwfinal)
It seems to me you have three dominant colors in the image:
1. blue-ish background (but also present inside cell as "noise")
2. grenn-ish one part of cell
3. red-ish - second part of cell
If these three colors are distinct enough, you may try and segment the image using k-means and Graph cuts.
First stage - use k-means to associate each pixels with one of three dominant colors. Apply k-means to the colors of the image (each pixel is a 3-vector in your chosen color space). Run k-means with k=3, keep for each pixel its distance to centroids.
Second stage - separate cell from background. Do a binary segmentation using graph-cut. The data cost for each pixel is either the distance to the background color (if pixel is labeled "background"), or the minimal distance to the other two colors (if pixel is labeled "foreground"). Use image contrast to set the pair-wise weights for the smoothness term.
Third stage - separate the two parts of the cell. Again do a binary segmentation using graph-cut but this time work only on pixels marked as "cell" in the previous stage. The data term for pixels that the k-means assigned to background but are labeled as cell should be zero for all labels (these are the "noise" pixels inside the cell).
You may find my matlab wrapper for graph-cuts useful for this task.

Fill an outline which is incomplete

Consider that I have a colored image like this in which the outline is not complete (There are gaps between lines). I want to be able to fill the area between the lines with one color or another. This actually is a binary image which I got after applying canny edge detector on a corresponding gray scale image.
I tried first dilating the image and then eroding it, but the result is not good enough. I want to be able to preserve the thickness of the root
Any help would be greatly appreciated
Original Image
Image after edge detection and some manual removal of pixels
Using the information in the edge image, I thought I would try to extract pixels from the original image of a certain color. For every white pixel in the edited image, I used a search space in the original image along the same horizontal line. I used different thresholds for R, G and B and I ended up with this
I'm not sure what your original image looks like. It would be helpful to see.
You have gaps between the lines because a line in your original image has two edges, one on each side. The canny algorithm is detecting them both. The Canny edge detection algorithm has at its heart the application of two Sobel kernels to calculate the gradient, one for detecting horizontal edges and one for detection vertical edges.
-1 0 +1
-2 0 +2
-1 0 +1
and
+1 +2 +1
0 0 0
-1 -2 -1
These kernels will present peaks for both sides of the line. One peak positive and one negative. You can exclude one side of the line by excluding the corresponding peak. After taking the gradient of each direction truncate any values below zero (set the values to zero) to remove the second peak. Then continue with the Canny edge detection as usual. This will result in the detection of only a single edge for each line instead of the two that you are seeing now.
I'll add a third approach now that I have seen the image. It looks like most of the information is in the green channel.
Green channel image
This image gives you a decent result if you simply apply a threshold.
Thresholded image with a somewhat arbitrary threshold
You can then either clean this image up by itself or use your edge image. To clean it up with the edge image you produced remove any white pixels that are more than a certain distance from one of your detected edges (create a Euclidean distance map from your edge image and use that to set any white pixels greater than a certain distance from an edge to black).
If you are still collecting images you may want to try to position the camera in a way to avoid the bottom of the jar (or whatever this is).
You could attempt to use a line scanning methodology. Start at the side and scan horizontally. When you hit an edge you assume you are in a root and you start setting the voxels to white. When you hit another edge you assume you are leaving a root and you start. There will be some fringe cases and you may want to add additional checks, such as limiting the allowed thickness of a root.
You could also do a flood fill style algorithm where you take a seed point in a root and travel up the root filling it in.
Not sure how well these would work as it depends on the image and I did not test it.