Determine height of Coffee in the pot using Python imaging - python

We have a web-cam in our office kitchenette focused at our coffee maker. The coffee pot is clearly visible. Both the location of the coffee pot and the camera are static. Is it possible to calculate the height of coffee in the pot using image recognition? I've seen image recognition used for quite complex stuff like face-recognition. As compared to those projects, this seems to be a trivial task of measuring the height.
(That's my best guess and I have no idea of the underlying complexities.)
How would I go about this? Would this be considered a very complex job to partake? FYI, I've never done any kind of imaging-related work.

Since the coffee pot position is stationary, get a sample frame and locate a single column of pixels where the minimum and maximum coffee quantities can easily be seen, in a spot where there are no reflections. Check the green vertical line segment in the following picture:
(source: nullnetwork.net)
The easiest way is to have two frames, one with the pot empty, one with the pot full (obviously under the same lighting conditions, which typically would be the case), convert to grayscale (colorsys.rgb_to_hsv each RGB pixel and keep only the v (3rd) component) and sum the luminosity of all pixels in the chosen line segment. Let's say the pot-empty case reaches a sum of 550 and the pot-full case a sum of 220 (coffee is dark). By comparing an input frame sum to these two sums, you can have a rough estimate of the percentage of coffee in the pot.
I wouldn't bet my life on the accuracy of this method, though, and the fluctuations even from second to second might be wild :)
N.B: in my example, the green column of pixels should extend to the bottom of the pot; I just provided an example of what I meant.

Steps that I'd try:
Convert the image in grayscale.
Binarize the image, and leave only the coffee. You can discover a good threshold manually through experimentation.
Blob extraction. Blob's area (number of pixels) is one way to calculate the height, ie area / width.

First do thresholding, then segmentation. Then you can more easily detect edges.

You're looking for edge detection. But you only need to do it between the brown/black of the coffee and the color of the background behind the pot.

make pictures of the pot with different levels of coffe in it.
downsample the image to maybe 4*10 pixels.
make the same in a loop for each new live picture.
calculate the difference of each pixels value compared to the reference images.
take the reference image with the least difference sum and you get the state of your coffe machine.
you might experiment if a grayscale version or only red or green might give better results.
if it gives problems with different light settings this aproach is useless. just buy a spotlight for the coffe machine, or lighten up, or darken each picture till the sum of all pixels reaches a reference value.

Related

Improving background subtraction when encountering split objects in foreground masks

For a project I implemented a simple background subtraction using a median background estimation. The result is not bad, but often moving objects (people in my test examples) are cut in unconnected blobs.
I tried calling open and close operations (I removed the close operation, because it seemed as if it wouldn't improve the result) on the foreground mask to improve the result, which worked to some degree. However, I am wondering if there are even more ways how I could improve the foreground mask. It is still a fair bit away from the ground truth.
I am aware that playing around with the threshold itself is also always a viable solution and I do play around with that too. That being said, I focus on reducing noise to a minimum. I also tried adaptive thresholding, but that didn't look very promising for this usecase.
Without opening:
With opening:
I am more looking for general approaches than to actual implementations.
Pseudocode of background subtraction
Greyscale all images.
Make a background estimation by calculating the median for every r,g and b value for every pixel in a subset of all images.
Then take every image and calculate the absolute difference between that image and the background estimation.
Apply a threshold to get a binary result called the foreground mask
Use opencvs open operation once.
I like the greyscale simplification.
Simple is good.
We should make everything as simple as
possible, but not simpler.
Let's attack your model for a moment.
An evil clothing designer with an army
of fashion models sends them walking
past your camera, each wearing a red
shirt that is slightly darker than
the preceding one.
At least one of the models will be
"invisible" against some of your background pixels,
having worn a matching shade,
with matching illumination,
compared with the median pixel value.
Repeat with a group of green shirts, then blue.
How to remedy this?
In each channel compute the median red,
median green, median blue pixel intensity.
At inference time, compute three absolute
value differences.
Threshold on max of those deltas.
Computing over sensor R, G, B is straightforward.
Human perception more closely aligns with H, S, V.
Consider computing max delta over those three,
or over all six.
For each background pixel,
compute both expected value and variance,
either for the whole video or for one-minute
slots of time.
Now, at inference time, the variance
informs your thresholding decision,
improving its accuracy.
For example, some pixels may have constant
illumination, others slowly change with the
movement of the sun, and others are quite
noisy when wind disturbs the leaves of
vegetation. The variance lets you capture
this quite naturally.
For a much more powerful modeling approach,
go with Optical Flow.
https://docs.opencv.org/3.4/d4/dee/tutorial_optical_flow.html

Image analysis help needed with identifying hot pixels in a set of images

I conducted an experiment to see radioactivity using a camera sensor. so i captured long exposure images(greyscale) in a dark environment and saw that there is a pixel pattern (bright pixels) repeating in my image data set(I think they are called hot pixels).I need to identify these pixels and ignore them when calculating the number of radiation interactions observed in each image. A radiation interaction would also appear as a bright pixel or a collection of a couple of pixels.
sample image
**I am using python to analyze the image data set. I am a newbie to Python programming and do not have much knowledge about handling these level problems or the libraries/functions to be used in analysis like this. thought it would be a good project to learn a bit of image analysis.
I dont know how to code this problem. So I definitely need help with that. **However **i thought of an algorithm that could possibly help me achieve my goal. ****
-Since hot pixels and radiation interactions appear as white pixels/spots in a black/dark background, I would assign a suitable pixel value threshold to the image to set the background pixel value to 0 (min) and white pixels to 255(max).
then I would check each pixel value in all 100 images and identify the pixel positions that have the same value in all images. (eg: lets say pixel value at position (1,1) in an image is 255 for all 100 images. then i would note that position as a hot pixel).
Next, I would set the pixel value of those positions to 0, so i will be left with bright pixels from radiation events only.
sometimes radiation events can have more than one pixel (but they will be next to each other). So i need to know a method to count them as one event.
I would truly appreciate your help if you can help me resolve this problem in a much more efficient manner.

Identifying positive pixels after color deconvolution ignoring boundaries

I am analyzing histology tissue images stained with a specific protein marker which I would like to identify the positive pixels for that marker. My problem is that thresholding on the image gives too much false positives which I'd like to exclude.
I am using color deconvolution (separate_stains from skimage.color) to get the AEC channel (corresponding to the red marker), separating it from the background (Hematoxylin blue color) and applying cv2 Otsu thresholding to identify the positive pixels using cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU), but it is also picking up the tissue boundaries (see white lines in the example picture, sometimes it even has random colors other than white) and sometimes even non positive cells (blue regions in the example picture). It's also missing some faint positive pixels which I'd like to capture.
Overall: (1) how do I filter the false positive tissue boundaries and blue pixels? and (2) how do I adjust the Otsu thresholding to capture the faint red positives?
Adding a revised example image -
top left the original image after using HistoQC to identify tissue regions and apply the mask it identified on the tissue such that all of the non-tissue regions are black. I should tru to adjust its parameters to exclude the folded tissue regions which appear more dark (towards the bottom left of this image). Suggestions for other tools to identify tissue regions are welcome.
top right hematoxylin after the deconvolution
bottom left AEC after the deconvolution
bottom right Otsu thresholding applied not the original RGB image trying to capture only the AEC positives pixels but showing also false positives and false negatives
Thanks
#cris-luengo thank you for your input on scikit-image! I am one of the core developers, and based on #assafb input, we are trying to rewrite the code on color/colorconv/separate_stains.
#Assafb: The negative log10 transformation is the Beer-Lambert mapping. What I don't understand in that code is the line rgb += 2. I don't know where that comes from or why they use it. I'm 100% sure it is wrong. I guess they're trying to avoid log10(0), but that should be done differently. I bet this is where your negative values come from, though.
Yes, apparently (I am not the original author of this code) we use rgb += 2 to avoid log10(0). I checked Fiji's Colour Deconvolution plugin, and they add 1 to their input. I tested several input numbers to help on that, and ~2 would let us closer to the desirable results.
#Assafb: Compare the implementation in skimage with what is described in the original paper. You'll see several errors in the implementation, most importantly the lack of a division by the max intensity. They should have used -np.log10(rgb/255) (assuming that 255 is the illumination intensity), rater than -np.log10(rgb).
Our input data is float; the max intensity in this case would be 1. I'd say that that's the reason we don't divide by something.
Besides that, I opened an issue on scikit-image to discuss these problems — and to specify a solution. I made some research already — I even checked DIPlib's documentation —, and implemented a different version of that specific function. However, stains are not my main area of expertise, and we would be glad if you could help evaluating that code — and maybe pointing a better solution.
Thank you again for your help!
There are several issues that cause improper quantification. I'll go over the details of how I would recommend you tackle these slides.
I'm using DIPlib, because I'm most familiar with it (I'm an author). It has Python bindings, which I use here, and can be installed with pip install diplib. However, none of this is complicated image processing, and you should be able to do similar processing with other libraries.
Loading image
There is nothing special here, except that the image has strong JPEG compression artifacts, which can interfere with the stain unmixing. We help the process a bit by smoothing the image with a small Gaussian filter.
import diplib as dip
import numpy as np
image = dip.ImageRead('example.png')
image = dip.Gauss(image, [1]) # because of the severe JPEG compression artifacts
Stain unmixing
[Personal note: I find it unfortunate that Ruifrok and Johnston, the authors of the paper presenting the stain unmixing method, called it "deconvolution", since that term already had an established meaning in image processing, especially in combination with microscopy. I always refer to this as "stain unmixing", never "deconvolution".]
This should always be the first step in any attempt at quantifying from a bightfield image. There are three important RGB triplets that you need to determine here: the RGB value of the background (which is the brightness of the light source), and the RGB value of each of the stains. The unmixing process has two components:
First we apply the Beer-Lambert mapping. This mapping is non-linear. It converts the transmitted light (as recorded by the microscope) into absorbance values. Absorbance indicates how strongly each point on the slide absorbs light of the various wavelengths. The stains absorb light, and differ by the relative absorbance in each of the R, G and B channels of the camera.
background_intensity = [209, 208, 215]
image = dip.BeerLambertMapping(image, background_intensity)
I manually determined the background intensity, but you can automate that process quite well if you have whole slide images: in whole slide images, the edges of the image always correspond to background, so you can look there for intensities.
The second step is the actual unmixing. The mixing of absorbances is a linear process, so the unmixing is solving of a set of linear equations at each pixel. For this we need to know the absorbance values for each of the stains in each of the channels. Using standard values (as in skimage.color.hax_from_rgb) might give a good first approximation, but rarely will provide the best quantification.
Stain colors change from assay to assay (for example, hematoxylin has a different color depending on who made it, what tissue is stained, etc.), and change also depending on the camera used to image the slide (each model has different RGB filters). The best way to determine these colors is to prepare a slide for each stain, using all the same protocol but not putting on the other dyes. From these slides you can easily obtain stain colors that are valid for your assay and your slide scanner. This is however rarely if ever done in practice.
A more practical solution involves estimating colors from the slide itself. By finding a spot on the slide where you see each of the stains individually (where stains are not mixed) one can manually determine fairly good values. It is possible to automatically determine appropriate values, but is much more complex and it'll be hard finding an existing implementation. There are a few papers out there that show how to do this with non-negative matrix factorization with a sparsity constraint, which IMO is the best approach we have.
hematoxylin_color = np.array([0.2712, 0.2448, 0.1674])
hematoxylin_color = (hematoxylin_color/np.linalg.norm(hematoxylin_color)).tolist()
aec_color = np.array([0.2129, 0.2806, 0.4348])
aec_color = (aec_color/np.linalg.norm(aec_color)).tolist()
stains = dip.UnmixStains(image, [hematoxylin_color, aec_color])
stains = dip.ClipLow(stains, 0) # set negative values to 0
hematoxylin = stains.TensorElement(0)
aec = stains.TensorElement(1)
Note how the linear unmixing can lead to negative values. This is a result of incorrect color vectors, noise, JPEG artifacts, and things on the slide that absorb light that are not the two stains we defined.
Identifying tissue area
You already have a good method for this, which is applied to the original RGB image. However, don't apply the mask to the original image before doing the unmixing above, keep the mask as a separate image. I wrote the next bit of code that finds tissue area based on the hematoxylin stain. It's not very good, and it's not hard to improve it, but I didn't want to waste too much time here.
tissue = dip.MedianFilter(hematoxylin, dip.Kernel(5))
tissue = dip.Dilation(tissue, [20])
tissue = dip.Closing(tissue, [50])
area = tissue > 0.2
Identifying tissue folds
You were asking about this step too. Tissue folds typically appear as larger darker regions in the image. It is not trivial to find an automatic method to identify them, because a lot of other things can create darker regions in the image too. Manual annotation is a good start, if you collect enough manually annotated examples you could train a Deep Learning model to help you out. I did this just as a place holder, again it's not very good, and identifies some positive regions as folds. Folds are subtracted from the tissue area mask.
folds = dip.Gauss(hematoxylin - aec, [20])
area -= folds > 0.2
Identifying positive pixels
It is important to use a fixed threshold for this. Only a pathologist can tell you what the threshold should be, they are the gold-standard for what constitutes positive and negative.
Note that the slides must all have been prepared following the same protocol. In clinical settings this is relatively easy because the assays used are standardized and validated, and produce a known, limited variation in staining. In an experimental setting, where assays are less strictly controlled, you might see more variation in staining quality. You will even see variation in staining color, unfortunately. You can use automated thresholding methods to at least get some data out, but there will be biases that you cannot control. I don't think there is a way out: inconsistent stain in, inconsistent data out.
Using an image-content-based method such as Otsu causes the threshold to vary from sample to sample. For example, in samples with few positive pixels the threshold will be lower than other samples, yielding a relative overestimation of the percent positive.
positive = aec > 0.1 # pick a threshold according to pathologist's idea what is positive and what is not
pp = 100 * dip.Count(dip.And(positive, area)) / dip.Count(area)
print("Percent positive:", pp)
I get a 1.35% in this sample. Note that the % positive pixels is not necessarily related to the % positive cells, and should not be used as a substitute.
I ended up incorporating some of the feedback given above by Chris into the following possible unconventional solution for which I would appreciate getting feedback (to the specific questions below but also general suggestions for improvement or more effective/accurate tools or strategy):
Define (but not apply yet) tissue mask (HistoQC) after optimizing HistoQC script to remove as much of the tissue folds as possible without removing normal tissue area
Apply deconvolution on the original RGB image using hax_from_rgb
Using the second channel which should correspond to the red stain pixels, and subtract from it the third channel which as far as I see corresponds to the background non-red/blue pixels of the image. This step removes the high values in the second channel that which up because of tissue folds or other artifacts that weren't removed in the first step (what does the third channel correspond to? The Green element of RGB?)
Blur the adjusted image and threshold based on the median of the image plus 20 (Semi-arbitrary but it works. Are there better alternatives? Otsu doesn't work here at all)
Apply the tissue regions mask on the thresholded image yielding only positive red/red-ish pixels without the non-tissue areas
Count the % of positive pixels relative to the tissue mask area
I have been trying to apply, as suggested above, the tissue mask on the deconvolution red channel output and then use Otsu thresholding. But it failed since the black background generated by the applying the tissue regions mask makes the Otsu threshold detect the entire tissue as positive. So I have proceeded instead to apply the threshold on the adjusted red channel and then apply the tissue mask before counting positive pixels. I am interested in learning what am I doing wrong here.
Other than that, the LoG transformation didn't seem to work well because it produced a lot of stretched bright segments rather than just circular blobs where cells are located. I'm not sure why this is happening.
Use ML for this case.
Create manually binary mask for your pictures: each red pixel - white, background pixels - black.
Work in HSV or Lab color space.
Train simple classifier: decision tree or SVM (linear or with RBF)..
Let's test!
See on a good and very simple example with skin color segmentation.
And in the future you can add new examples and new cases without code refactoring: just update dataset and retrain model.

How could we do temporal filtering for video processing?

I am reading the slides for temporal filtering in Computer vision (page 108) class and i am wondering how can we do temporal filtering for videos?
For example they say our data is a vide which is in XYT, whre X,Y are spatial domain and T is time.
"How could we create a filter that keeps sharp objects that move at some velocity (vx, vy)while blurring the rest?"
and they kinda drive the formula for that, but im confused how to apply it?
How can we do filtering in Fourie Domain , how should we apply that in general? can someone please help me how should i code it?
In that example, they're talking about a specific known speed. For example, if you know that a car is moving left at 2 pixels per frame. It's possible to make a video that blurs everything except that car.
Here's the idea: start at frame 0 of the video. At each pixel, look one frame in the future, and 2 pixels left. You will be looking at the same part of the moving car. Now, imagine you take the average color value between your current pixel & the future pixel (the one that is 2 pixels left, and 1 frame in the future). If your pixel is on the moving car, both pixels will be the exact same color, so taking the average has no effect. On the other hand, if it's NOT on the moving car, they'll be different colors, and so take the average will have the effect of blurring between them.
Thus, the pixels of the car will be unchanged, but the rest of the video will get a blur. Repeat for each frame. You can also include more frames in your filter; e.g. you could look 2 frames in the future and 4 pixels left, or 1 frame in the past and 2 pixels right.
Note: this was a teaching example; I don't think there are many real computer vision applications for this (at least, not as a standalone technique), because it's so fragile. If the car speeds up or slows down slightly, it gets blurred.

How to measure color proximity / concentration in an image? (python solution preferred)

Consider the 2 images below:
Image 1:
Image 2:
For the sake of the argument let's say that the 2 images have the same amount of black pixels.
I would like to calculate the "concentration" of black pixels. I.e. image 1 has higher "index" than image 2, since the black pixels are more "grouped" or concentrated than in image 2.
Intuitively I would hope that a metric or function would already exist. I did some searches but couldn't quite find what I am looking for.
Applied to real examples, I would use kmeans to reduce color numbers to around 15 and apply that calculation to each color. I do not think that a histogram would help as I am assuming a same number of black pixels (pls correct me if I'm wrong).
One approach I can think of is:
1. Filter for color
2. Use contours to define blobs
3. Count size and number of blobs for each color
4. Quantify into a comparable metric per color
I'm not an expert in imaging libraries, so if you could provide some guidance on how to implement that, it would be great.
Any suggestions?
Thanks a lot!
I think you are heading towards the field of Granulometry with was developed by a Parisian mining company to determine the sizes of grains of minerals in rock in order to decide whether mining was viable.
Anyway, enough history. If you perform a "morphological closing" on each of your images with a disk as the structuring element, gradually increasing in radius, you will get a measure of the distribution of the sizes of blobs present in your image.
No time to write the Python at the minute, but the graph below shows your images side-by-side with a red vertical bar to separate them. In each successive frame of the video, I increase the radius of the circular disk-shaped structuring element by 1 pixel. The first frame has radius of 1, and the final frame has a radius of 39:
Hopefully you can see that the bigger, more concentrated shapes hang around longer than the smaller ones.

Categories