I've got the following image.
Other Samples
I want to detect the six square-shaped green portions and the one circular portion above them. I basically want a binary image with these portions marked 1 (white) and everything else 0 (black).
What have I done so far?
I found a range of H, S, and V within which these colors fall which works fine for a single image, but I've got multiple such images, some under different illumination (brightness) conditions and the ranges do not work in those cases. What should I do to make the thresholding as invariant to brightness as possible? Is there a different approach I should take for thresholding?
What you did was manually analyze the values you need for thresholding for a specific image, and then apply that. What you see is that analysis done on one image doesn't necessarily fit other images.
The solution is to do the analysis automatically for each image. This can be achieved by creating a histogram for each of the channels, and if you're working in HSV, I'm guessing that the H channel would be pretty much useless in this case.
Anyway, once you have the histograms, you should analyze the threshold using something like Lloyd-Max, which is basically a K-Means type clustering of intensities. This should give the centroids for the intensity of the white background, and the other colors. Then you choose the threshold based on the cluster standard deviation.
For example, in the image you gave above, the histogram of the S channel looks like:
You can see the large blob near 0 is the white background that has the lowest saturation.
Related
Here`s the deal. I want to create a mask that visualizes all the changes between two images (GeoTiffs which are converted to 2D numpy arrays).
For that I simply subtract the pixel values and normalize the absolute value of the subtraction:
Since the result will be covered in noise, I use a treshold and remove all pixels with a value below a certain limit.
def treshold(array, thresholdLimit):
print("Treshold...")
result = (array > thresholdLimit) * array
return result
This works without a problem. Now comes the issue. When applying the treshold, outliers remain, which is not intended:
What is a good way to remove those outliers?
Sometimes the outliers are small chunks of pixels, like 5-6 pixels together, how could those be removed?
Additionally, the images I use are about 10000x10000 pixels.
I would appreciate all advice!
EDIT:
Both images are landsat satelite images, covering the exact same area.
The difference here is that one image shows cloud coverage and the other one is free of clouds.
The bright snakey line in the top right is part of a river that has been covered by a cloud. Since water bodies like the ocean or rivers are depicted black in those images, the difference between the bright cloud and the dark river results in the river showing a high degree of change.
I hope the following images make this clear:
Source tiffs :
Subtraction result:
I also tried to smooth the result of the tresholding by using a median filter but the result was still covered in outliers:
from scipy.ndimage import median_filter
def filter(array, limit):
print("Median-Filter...")
filteredImg = np.array(median_filter(array, size=limit)).astype(np.float32)
return filteredImg
I would suggest the following:
Before proceeding please double check if the two images are 100% registered. To check that you should overlay them using e.g. different color channels. Even minimal registration errors can render your task impossible
Smooth both input images slightly (before the subtraction). For that I would suggest you use standard implementations. Play around with the filter parameters to find an acceptable compromise between smoothness (or reduction of graininess of source image 1) and resolution
Then try to match the image statistics by applying histogram normalization, using the histogram of image 2 as a target for the histogram of image 1. For this you can also use e.g. the OpenCV implementation
Subtract the images
If you then still observe obvious noise, look at the histogram of the subtraction result and see if you can relate the noise to intensity outliers. If you can clearly separate signal and noise based on intensity, apply again a thresholding (informed by your histogram). Alternatively (or additionally), if the noise is structurally different from your signal (e.g. clustered), you could look into morphological operations to remove it.
I have a use case where I need to classify some images as grey scale or color. My initial step was based on the feature that grey scale images should have r,g,b values at a pixel, the same values as it is single channel. Were as for color images, r,g,b values at the same pixel may not be the same.
So I am checking by getting the difference between (r,g), (b,g) and (r,b) and if all three has only zero, its grey scale else, its color.
This approach helped me to identify many grey scale images but still there are some images which does not follow this logic. Can anyone specify some good features on which we can classify an image as color or grey scale using opencv?
Do not ask me to check the number of channels and classify, it gives 3 for both the classes as we are loading it in .jpg format.
Thanks in advance
I suspect, some never were grey-scale images after digitizing (e.g. a color scan of gray-scale picture). Due to noise, there are minimal differences in the RGB values. A low threshold greater than perfect zero should do the trick.
Please note that JPEG totally has a gray-scale option. However, when storing the picture, you request that mode. Compressors usually do not pick it up automatically. Also, you explicitly need to set the flag IMREAD_UNCHANGED while reading with OpenCV's imread.
With the method Suggested by #QuangHoang I got a result of 85+% accuracy.
Here is the approach explained.
#test image
img=cv2.imread('test.jpg')
r,g,b=cv2.split(img)
#spliting b,g,r and getting differences between them
r_g=np.count_nonzero(abs(r-g))
r_b=np.count_nonzero(abs(r-b))
g_b=np.count_nonzero(abs(g-b))
diff_sum=float(r_g+r_b+g_b)
#finding ratio of diff_sum with respect to size of image
ratio=diff_sum/img.size
if ratio>0.005:
label='color'
else:
label='grey'
Thanks for all the suggestions.
I've been trying to detect if a printed image has any defects(shape and color) when compared to either a proof of another printed image which has no defects or the digital version of the image, which also has no defects. I'm using opencv(cv2) and python.
I first take a picture of the printed image. Then, I perform perspective transformation to get the picture of the printed image cropped sufficiently. I am then using Zernike moments, SSIM, and color histograms to compare the color and shape of the image. However, the resulting values vary too much and I am not able to create a threshold for a misprinted image.
I have also tried to subdivide the image into smaller sections and compare those. This is also not creating distinguishable values to determine if there is a misprint or not.
The differences in the print can be subtle or very apparent. Are there any other techniques that I can try? Thanks!
This is an example of a correctly printed image:
This is an example of an incorrectly printed image, it has too much blue ink on the right side:
This is another example of a correct print:
This is an example of a misprint when compared to the one above:
I am trying to enhance old hand drawn maps which were digitized by scanning and this process has caused some blacked out areas in the image making the text inside them very hard to read.
I tried adaptive histogram equalization and couple of other histogram based approach using MATLAB but nothing gives me the desired result. I could probably lighten the darker shades of grey and make it look a bit better using adaptive histogram equalization but it doesn't really help with the text.
Specifically, I tried adapthisteq() with different variations which is a function available in MATLAB.
Something like this:
A = adapthisteq(I,'NumTiles',X,'clipLimit',0.01,'Distribution','uniform');
... and also tried to change the pixel values directly by having a look at image, something like this :
I(10 > I & I > 0) = 0;
I(30 > I & I > 10) = 10;
I(255 > I & I > 30) = 255;
Can I enhance the image and get an end result which has only black and white where the lines and text (basically all the information) turns into black (0) and the shades of grey and whiter regions turn into white (255 or 1)?
Is this even possible? If not, how close can I even get to it or what is the best solution to get as close as possible to the desired result. Any help is appreciated.
Here's what the original image looks like:
Here's what the result looks like after I tried out my solution using adaptive histogram equalization:
Sounds like a classic case of using adaptive thresholding. Adaptive thresholding in a general sense works by taking a look at local image pixel neighbourhoods, compute the mean intensity and seeing if a certain percentage of pixels exceed this mean intensity. If it does, we set the output to white and if not, we set this to black.
One classic approach is to use the Bradley-Roth algorithm.
If you'd like to see an explanation of the algorithm, you can take a look at a previous answer that I wrote up about it:
Bradley Adaptive Thresholding -- Confused (questions)
However, if you want the gist of it, an integral image of the grayscale version of the image is taken first. The integral image is important because it allows you to calculate the sum of pixels within a window in O(1) complexity. However, the calculation of the integral image is usually O(n^2), but you only have to do that once. With the integral image, you scan neighbourhoods of pixels of size s x s and you check to see if the average intensity is less than t% of the actual average within this s x s window then this is pixel classified as the background. If it's larger, then it's classified as being part of the foreground. This is adaptive because the thresholding is done using local pixel neighbourhoods rather than using a global threshold.
On this post: Extract a page from a uniform background in an image, there is MATLAB code I wrote that is an implementation of the Bradley-Roth algorithm, so you're more than welcome to use it.
However, for your image, the parameters I used to get some OK results was s = 12 and t = 25.
After running the algorithm, I get this image:
Be advised that it isn't perfect... but you can start to see some text that you didn't see before. Specifically at the bottom, I see Lemont Library - Built 1948.... and we couldn't see that before in the original image.
Play around with the code and the parameters, read up on the algorithm, and just try things out yourself.
Hope this helps!
I've read a number of questions on finding the colour palette of an image, but my problem is slightly different. I'm looking for images made up of pure colours: pictures of the open sky, colourful photo backgrounds, red brick walls etc.
So far I've used the App Engine Image.histogram() function to produce a histogram, filter out values below a certain occurrence threshold, and average the remaining ones down. That still seems to leave in a lot of extraneous photographs where there are blobs of pure colour in a mixed bag of other photos.
Any ideas much appreciated!
How about doing this?
Blur the image using some fast blurring algorithm. (Search for stack blur or box blur)
Compute standard deviation of the pixels in RGB domain, once for each color.
Discard the image if the standard deviation is beyond a certain threshold.
In my opinion a histogram will not be the ideal tool for the this task since it typically looks at separately at each color channel and you will loose information like this. So for example if you get peaks at 255 red, green and blue this can either mean that there is lots of red (0xFF0000), green (0x00FF00) and blue ( 0x0000FF) in the image or that the whole image is simply entirely white ( 0xFFFFFF).
I recommend you to use a color quantization algorithm on your image: http://en.wikipedia.org/wiki/Color_quantization and have it return you the 16 most dominant colors. Then maybe convert them to HSL and check for values with a high saturation.