Would like to use edge detection on microscope images to make the background white.
This is the code i have so far, could this be useful?
code:
import cv2
import numpy as np
import matplotlib.pyplot as plt
def simple_edge_detection(image):
edges_detected = cv2.Canny(image , 100, 200)
images = [image , edges_detected]
location = [121, 122]
for loc, edge_image in zip(location, images):
plt.subplot(loc)
plt.imshow(edge_image, cmap='gray')
cv2.imwrite('edge_detected.png', edges_detected)
plt.savefig('edge_plot.png')
plt.show()
img = cv2.imread('gay2.0.jpg', 0)
simple_edge_detection(img)
result of the code:
result wished:
Use a Canny filter and binarize with a threshold sufficient to remove most of the background.
Remove the small blobs and detect those against the image borders (possibly fill their holes). These define a mask around the object of interest (don't worry about the green blob and orange lines in the picture).
You may add some preprocessing or postprocessing to remove the holders.
Related
I am trying to write a code that calculates the area occupied by the tracks on a microscope image, as this:
As the tracks aren't uniform (I mean, they don't have a unique grey level as they are darker in the edge and lighter in the centre), I can't do this just by comparing their grey level with the grey level of the background because in some parts of the tracks it is the same.
Therefore, what I tried is to compare the image with an image of the background:
I do this in order to extract the differences between these two images (which correspond to the tracks themselves). In order to do this, I have used the compare_ssim function from skimage. The code I've used is the following:
from skimage.measure import compare_ssim
import imutils
import cv2
import numpy as np
# load the two input images
imageA = cv2.imread("./b.jpg")
imageB = cv2.imread("./a.jpg")
# convert the images to grayscale
grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY)
grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY)
# compute the Structural Similarity Index (SSIM) between the two
# images, ensuring that the difference image is returned
(score, diff) = compare_ssim(grayA, grayB, full=True)
diff = (diff * 255).astype("uint8")
# show the diff image
cv2.imshow("Diff", diff)
The diff image I obtain is the following:
This is quite good, because now the grey level in the tracks is well-differentiated from the grey level of the background, and I can calculate the area occupied by the tracks (there are some points in the track that are lighter, but they are few and it's okay for my purpose).
Nevertheless, the problem is that in the diff image not only the tracks appear in black, but also a thick border surrounding the tracks:
This edge makes my area estimation incorrect.
So I would like to know how I can eliminate that edge, or at least make it thinner.
If this is not possible, it would be really helpful if you can show me another Python function that can achieve my purpose of calculating the area occupied by the tracks.
There is always a better way to do the same thing, but I used here a simple approach, which you can improve or tune later according to your needs:
Algorithm:
Firstly, proper thresholding will keep only the edges
Secondly, Morphology (dilation, or erosion depending on your thresholding approach) will thin your Edges.
At the end, to get rid of everything, except your tracks, you can use Flood_Fill algorithm, then count the white pixels to get your area(in pixels).
Results:
In the Final result:
The count of the white of pixels is: 52219
Code:
#========================
# Import Libraries
#========================
import numpy as np
import matplotlib.pyplot as plt
import cv2
from skimage.morphology import flood_fill
#========================
# Read images
#========================
img = cv2.imread('1.png',0)
bck = cv2.imread('2.png',0)
#========================
# Gaussian Blur
#========================
gauss = cv2.GaussianBlur(img, (5,5), 1)
bck_gauss = cv2.GaussianBlur(bck, (5,5), 1)
#========================
# Thresholding
#========================
_,thresh1 = cv2.threshold(gauss,127,255,cv2.THRESH_BINARY)
_,bck_th = cv2.threshold(bck_gauss,127,255,cv2.THRESH_BINARY_INV)
# Get rid of black borders
thresh1 = thresh1 + bck_th
#========================
# Morphology
#========================
kernel = np.zeros((7,7),np.uint8)
kernel[2,:] = 1
kernel[:,2] = 1
dilation = cv2.dilate(thresh1,kernel,iterations = 1)
#========================
# Flood Fill
#========================
res = flood_fill(dilation,(1,1), 0)
print("The count of the white of pixels is: ", int(np.sum(res)/np.max(res)))
#========================
# Visualize Results
#========================
plt.figure(num='Blobs')
plt.subplot(221)
plt.imshow(img, cmap='gray')
plt.title('Original')
plt.axis('off')
plt.subplot(222)
plt.imshow(thresh1, cmap='gray')
plt.title('Thresholded')
plt.axis('off')
plt.subplot(223)
plt.imshow(dilation, cmap='gray')
plt.title('Morphology')
plt.axis('off')
plt.subplot(224)
plt.imshow(res, cmap='gray')
plt.title('Final Result')
plt.axis('off')
plt.show()
I want to automate the task of entering set of images into a number generating system & before that i like to remove a dotted watermark which is common across these images.
I tried using google, tesseract & abby reader, but I found that the image part that does not contain the watermark is recognized well, but the part that is watermarked is almost impossible to recognize.
I would like to remove the watermark using image processing. I already tried few sample codes of opencv, python, matlab etc but none matching my requirements...
Here is a sample code in Python that I tried which changes the brightness & darkness:
import cv2
import numpy as np
img = cv2.imread("d:\\Docs\\WFH_Work\\test.png")
alpha = 2.5
beta = -250
new = alpha * img + beta
new = np.clip(new, 0, 255).astype(np.uint8)
cv2.imshow("my window", new)
Unusually, i dont know the watermark of this image consists how many pixels. Is there a way to get rid of this watermark OR make digits dark and lower the darkness of watermark via code?
Here is watermarked image
I am using dilate to remove the figures, then find the edge to detect watermark. Remove it by main gray inside watermark
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('test.png', 0)
kernel = np.ones((10,10),np.uint8)
dilation = cv2.dilate(img,kernel,iterations = 1)
erosion = cv2.erode(dilation,kernel,iterations = 1)
plt.imshow(erosion, cmap='gray')
plt.show()
#contour
gray = cv2.bilateralFilter(erosion, 11, 17, 17)
edged = cv2.Canny(gray, 30, 200)
plt.imshow(edged, cmap='gray')
plt.show()
I'm working with Python and trying to do Otsu thresholding on an image but only inside the mask (yes, I have an image and a mask image). It means less pixel on the image will be included in the histogram for calculating the Otsu threshold.
I'm currently using the cv2.threshold function without the mask image and have no idea how to do this kind of job.
ret, OtsuMat = cv2.threshold(GaborMat, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
Since this function also incorporates the pixels outside the mask, I think it will give a less accurate threshold.
This is the example of the image and its mask:
https://drive.google.com/drive/folders/1p8JMhncJs19oOWO9RdkWuEADVGqE-gzQ?usp=sharing
Hope there is a OpenCV or other lib function to do it easily (and also with fast computing), but any kind of help will be appreciated.
I had a try at this using the threshold_otsu() method from skimage and a Numpy masked array. I don't know if there are faster ways - the skimage is normally pretty well optimised. If anyone else wants to take my sample data and try other ideas on it, please feel free - although there is a service charge of one upvote ;-)
#!/usr/bin/env python3
import cv2
import numpy as np
import numpy.ma as ma
from skimage.filters import threshold_otsu
# Set up some repeatable test data, 4 blocks 100x100 pixels each of random normal np.uint8s centred on 32, 64, 160,192
np.random.seed(42)
a=np.random.normal(size = (100,100), loc = 32,scale=10).astype(np.uint8)
b=np.random.normal(size = (100,100), loc = 64,scale=10).astype(np.uint8)
c=np.random.normal(size = (100,100), loc = 160,scale=10).astype(np.uint8)
d=np.random.normal(size = (100,100), loc = 192,scale=10).astype(np.uint8)
# Stack (concatenate) the 4 squares horizontally across the page
im = np.hstack((a,b,c,d))
# Next line is just for debug
cv2.imwrite('start.png',im)
That gives us this:
# Now make a mask revealing only left half of image, centred on 32 and 64
mask=np.zeros((100,400))
mask[:,200:]=1
masked = ma.masked_array(im,mask)
print(threshold_otsu(masked.compressed())) # Prints 47
# Now do same revealing only right half of image, centred on 160 and 192
masked = ma.masked_array(im,1-mask)
print(threshold_otsu(masked.compressed())) # Prints 175
The histogram of the test data looks like this, x-axis is 0..255
Adapting to your own sample data, I get this:
#!/usr/bin/env python3
import cv2
import numpy as np
import numpy.ma as ma
from skimage.filters import threshold_otsu
# Load images
im = cv2.imread('eye.tif', cv2.IMREAD_UNCHANGED)
mask = cv2.imread('mask.tif', cv2.IMREAD_UNCHANGED)
# Calculate Otsu threshold on entire image
print(threshold_otsu(im)) # prints 130
# Now do same for masked image
masked = ma.masked_array(im,mask>0)
print(threshold_otsu(masked.compressed())). # prints 124
I'm trying to fill holes in the below image.
When I use SciPy's binary_fill_holes(), I am generally successful, with the exception of objects that touch the image's border.
Are there any existing Python functions that can fill holes in objects that touch the border? I tried adding a white border around the image, but that just resulted in the entire image being filled.
This assumes that there is more background than other stuff. It basically does a connected component analysis on the image. Extract the largest component (assumed to be the background), and sets everything else to white.
import numpy as np
import matplotlib.pyplot as plt
import skimage.morphology, skimage.data
img = skimage.data.imread('j1ESv.png', 1)
labels = skimage.morphology.label(img)
labelCount = np.bincount(labels.ravel())
background = np.argmax(labelCount)
img[labels != background] = 255
plt.imshow(img, cmap=plt.cm.gray)
plt.show()
I have a plot of spatial data that I display with imshow().
I need to be able to overlay the crystal lattice that produced the data. I have a png
file of the lattice that loads as a black and white image.The parts of this image I want to
overlay are the black lines that are the lattice and not see the white background between the lines.
I'm thinking that I need to set the alphas for each background ( white ) pixel to transparent (0 ? ).
I'm so new to this that I don't really know how to ask this question.
EDIT:
import matplotlib.pyplot as plt
import numpy as np
lattice = plt.imread('path')
im = plt.imshow(data[0,:,:],vmin=v_min,vmax=v_max,extent=(0,32,0,32),interpolation='nearest',cmap='jet')
im2 = plt.imshow(lattice,extent=(0,32,0,32),cmap='gray')
#thinking of making a mask for the white background
mask = np.ma.masked_where( lattice < 1,lattice ) #confusion here b/c even tho theimage is gray scale in8, 0-255, the numpy array lattice 0-1.0 floats...?
With out your data, I can't test this, but something like
import matplotlib.pyplot as plt
import numpy as np
import copy
my_cmap = copy.copy(plt.cm.get_cmap('gray')) # get a copy of the gray color map
my_cmap.set_bad(alpha=0) # set how the colormap handles 'bad' values
lattice = plt.imread('path')
im = plt.imshow(data[0,:,:],vmin=v_min,vmax=v_max,extent=(0,32,0,32),interpolation='nearest',cmap='jet')
lattice[lattice< thresh] = np.nan # insert 'bad' values into your lattice (the white)
im2 = plt.imshow(lattice,extent=(0,32,0,32),cmap=my_cmap)
Alternately, you can hand imshow a NxMx4 np.array of RBGA values, that way you don't have to muck with the color map
im2 = np.zeros(lattice.shape + (4,))
im2[:, :, 3] = lattice # assuming lattice is already a bool array
imshow(im2)
The easy way is to simply use your image as a background rather than an overlay. Other than that you will need to use PIL or Python Image Magic bindings to convert the selected colour to transparent.
Don't forget you will probably also need to resize either your plot or your image so that they match in size.
Update:
If you follow the tutorial here with your image and then plot your data over it you should get what you need, note that the tutorial uses PIL so you will need that installed as well.