skimage treshold_local does not work with pictures loaded using io.imread - scikit-image

I am was trying out one of the sample Python scripts available from the web site of Scikit Image. This script demonstrates Otsu segmentation at a local level. The script works with pictures loaded using
data.page()
but not using
io.imread
. Any suggestions?
https://scikit-image.org/docs/dev/auto_examples/applications/plot_thresholding.html#sphx-glr-auto-examples-applications-plot-thresholding-py
Picture file
Actual output - the Local thresholding window is empty
As you can see, Global thresholding has worked.But Local Thresholding has failed to produce any results.
Strangely, if I use data.page() then everything works fine.
Script
from skimage import io
from skimage.color import rgb2gray
import matplotlib.pyplot as plt
from skimage.filters import threshold_otsu,threshold_local
import matplotlib
from skimage import data
from skimage.util import img_as_ubyte
filename="C:\\Lenna.png"
mypic= img_as_ubyte (io.imread(filename))
#image = data.page() #This works - why not io.imread ?
imagefromfile=io.imread(filename)
image = rgb2gray(imagefromfile)
global_thresh = threshold_otsu(image)
binary_global = image > global_thresh
block_size = 35
local_thresh = threshold_local(image, block_size, offset=10)
binary_local = image > local_thresh
fig, axes = plt.subplots(nrows=3, figsize=(7, 8))
ax = axes.ravel()
plt.gray()
ax[0].imshow(image)
ax[0].set_title('Original')
ax[1].imshow(binary_global)
ax[1].set_title('Global thresholding')
ax[2].imshow(binary_local)
ax[2].set_title('Local thresholding')
for a in ax:
a.axis('off')
plt.show()

If you load the lenna.png and print its shape you will see it is a 4-channel RGBA image rather than a 3-channel RGB image.
print mypic.shape
(512, 512, 4)
I am not sure which parts of your code apply to which image, so I am not sure where to go next, but I guess you want to just get the RGB part and discard the alpha:
RGB = mypic[...,:3]

Related

Image text extraction in skimage

I have an image and I want to filter it to split the text from the background:
and after applying below code:
from skimage import filters
from skimage.filters import threshold_otsu
from skimage import io as skimage_io # So as not to clash with builtin io
dir = r"image_path/a.jpg"
img = skimage_io.imread(dir, as_gray=True, plugin='imageio')
blurred = filters.gaussian(img, sigma=2.0)
sobel = filters.sobel_h(blurred)
blurred += sobel
blurred += sobel
thresh = threshold_otsu(blurred)
# skimage_io.imshow(blurred)
print(thresh)
binary = img < thresh-0.1
skimage_io.imshow(binary)
The image became
Is there a way to make the result better ??
Yes, you can get a better result:
You do account for the noise, but the gaussian blur you apply is much too strong; it starts to dull out the features you are interested in (the letters).
As #Ziri pointed out in a comment, you are not accounting for the uneven exposure that is present in your image. There is many ways to do that; I will use a rolling ball filter to smooth out the background and combine it with a global thresholding method. Note that this is currently (August 2020) a PR in skimage, but will hopefully get merged soon.
import numpy as np
import matplotlib.pyplot as plt
from skimage import util
from skimage import filters
from skimage import io
from skimage import exposure
# PR 4851; will hopefully be in the library soon(TM)
from skimage.morphology import rolling_ellipsoid
img = io.imread("test.jpg", as_gray=True)
img_inv = util.invert(util.img_as_float(img))
# blurr the image slightly to remove noise
blurred = filters.gaussian(img_inv, sigma=1.0)
# remove background
background = rolling_ellipsoid(blurred, kernel_size=(50, 50), intensity_vertex=0.1)
normalized = blurred - background
# re-normalize intensity
normalized = exposure.rescale_intensity(normalized)
# binarize
binary = normalized > 0.38
binary = util.invert(binary)
plt.imshow(binary, cmap="gray")
plt.gca().axis("off")
plt.show()
Sidenote: It may not be wise to upload an uncensored picture of a passport to SO where it is freely accessible to anybody.

Bokeh rotated image blocks underlying image

I'm placeing a rotated image on top of another image of different anchor point in the same figure. However the top image partially covers the bottom image, shown below. Is there a way to remove the black border of the rotated image?
Sample codes here:
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.plotting import figure, ColumnDataSource, show
from bokeh.layouts import column
from bokeh.models.tools import PanTool, BoxZoomTool, WheelZoomTool, \
UndoTool, RedoTool, ResetTool, SaveTool, HoverTool
import numpy as np
from collections import namedtuple
from scipy import ndimage
def make_document(doc):
p = figure(match_aspect=True)
Anchor = namedtuple('Anchor', ['x', 'y'])
img1 = np.random.rand(256, 256)
anchor1 = Anchor(x=0, y=0)
img2= np.random.rand(256, 256)
anchor2 = Anchor(x=100, y=100)
img2 = ndimage.rotate(img2, 45, reshape=True)
p.image(image=[img1], x=anchor1.x, y=anchor1.y,
dw=img1.shape[0], dh=img1.shape[1], palette="Greys256")
p.image(image=[img2], x=anchor2.x, y=anchor2.y,
dw=img2.shape[0], dh=img2.shape[1], palette="Greys256")
doc.add_root(column(p, sizing_mode='stretch_both'))
apps = {'/': make_document}
server = Server(apps)
server.start()
server.io_loop.add_callback(server.show, "/")
try:
server.io_loop.start()
except KeyboardInterrupt:
print('keyboard interruption')
print('Done')
When you rotate an image, the new empty regions (black triangles on your image) are by default initialized with 0 (check out the mode and cval options at https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.rotate.html).
If you have a value that you know for sure will never be used in an image, you can pass it as cval. Then, you should be able to manually create a color mapper that maps that value to a transparent pixel and use the mapper instead of the palette (the arg name would be color_mapper).
If you don't have such a value, then you will have to use image_rgba and just make sure that whatever cval you decide to use will result in a transparent pixel.

How to generate a matplotlib animation using an image array?

So my problem is generating an animation from the list img_array. The code above that is basically used to get an image from the folder, annotate it and then save it into the array. Was wondering if anyone would have any suggestions on how to convert the images in the image array into an animation. Any help is appreciated! TIA.
I tried FFmepg and what not but none of them seem to work. I also tried videowriter in OpenCV but when I tried to open the file I get that this file type is not supported or corrupt.
import cv2
import numpy as np
import glob
import matplotlib.pyplot as plt
from skimage import io
import trackpy as tp
import pims
import pylab as pl
##########
pixel_min=23
min_mass=5000
Selector1=[1,2,3,4,5,6,7,11]
##########
frames = pims.ImageSequence('/Users/User/Desktop/eleventh_trial_2/*.tif', as_grey=True)
f1 = tp.locate(frames[0], pixel_min,minmass=min_mass)
plt.figure(1)
ax3=tp.annotate(f1,frames[0])
ax = plt.subplot()
ax.hist(f1['mass'], bins=20)
ax.set(xlabel='mass', ylabel='count');
f = tp.batch(frames[:], pixel_min, minmass=min_mass);
#f = tp.batch(frames[lower_frame:upper_frame], pixel, minmass=min_mass);
t=tp.link_df(f,10,memory=3)
##############
min_mass=8000#12000 #3000#2000 #6000#3000
pixel_min=23;
count=0
img_array = []
for filename in glob.glob('/Users/User/Desktop/eleventh_trial_2/*.tif'):
img = cv2.imread(filename)
height, width, layers = img.shape
size = (width,height)
img2 = io.imread(filename, as_gray=True)
fig, ax = plt.subplots()
ax.imshow(img)
#ax=pl.text(T1[i,1]+13,T1[i,0],str(int(T1[i,9])),color="red",fontsize=18)
T1=t.loc[t['frame']==count]
T1=np.array(T1.sort_values(by='particle'))
for i in Selector1:
pl.text(T1[i,1]+13,T1[i,0],str(int(T1[i,9])),color="red",fontsize=18)
circle2 = plt.Circle((T1[i,1], T1[i,0]), 5, color='r', fill=False)
ax.add_artist(circle2)
count=count+1
img_array.append(fig)
ani = animation.ArtistAnimation(fig, img_array, interval=50, blit=True,repeat_delay=1000)
When I run this I don't get an an error however I can't save the ani as tried in the past either using OpenCV videoWriter.
I found a work around although not the most efficient one. I saved the figures in a separate directory using os and plt.savefig() and then use ImageJ to automatically convert the sequentially numbered and saved figures into an animation. It ain't efficient but gets the job done. I am still open to more efficient answers. Thanks

pyplot.imsave() saves image correctly but cv2.imwrite() saved the same image as black

from scipy.misc import imread
from matplotlib import pyplot
import cv2
from cv2 import cv
from SRM import SRM ## Module for Statistical Regional Segmentation
im = imread("lena.png")
im2 = cv2.imread("lena.png")
print type(im), type(im2), im.shape, im2.shape
## Prints <type 'numpy.ndarray'> <type 'numpy.ndarray'> (120, 120, 3) (120, 120, 3)
srm = SRM(im, 256)
segmented = srm.run()
srm2 = SRM(im2, 256)
segmented2 = srm2.run()
pic = segmented/256
pic2 = segmented2/256
pyplot.imshow(pic)
pyplot.imsave("onePic.jpg", pic)
pic = pic.astype('uint8')
cv2.imwrite("onePic2.jpg", pic2)
pyplot.show()
onePic.jpg gives the correct segmented image but onePic2.jpg gives a complete black image.
Converting the datatype to uint8 using pic = pic.astype('uint8') did not help. I still gives a black image!
onePic.jpg using pyplot.imsave():
onePic2.jpg using cv2.imwrite():
Please help!
Before converting pic to uint8, you need to multiply it by 255 to get the correct range.
Although I agree with #sansuiso, in my case I found a possible edge case where my images were being shifted either one bit up in the scale or one bit down.
Since we're dealing with unsigned ints, a single shift means a possible underflow/overflow, and this can corrupt the whole image.
I found cv2's convertScaleAbs with an alpha value of 255.0 to yield better results.
def write_image(path, img):
# img = img*(2**16-1)
# img = img.astype(np.uint16)
# img = img.astype(np.uint8)
img = cv.convertScaleAbs(img, alpha=(255.0))
cv.imwrite(path, img)
This answer goes into more detail.
I encountered a similar situation with face detection, I wonder if there is a better way to execute this, here is my solution here as a reference.
from deepface import DeepFace
import cv2
import matplotlib.pyplot as plt
# import image and output
img_path = "image.jpg"
detected_face = DeepFace.detectFace(img_path, target_size = (128, 128))
plt.imshow(detected_face)
# image color scaling and saving
detected_face = cv2.cvtColor( detected_face,cv2.COLOR_BGR2RGB)
detected_face = cv2.convertScaleAbs(detected_face, alpha=(255.0))
cv2.imwrite("image_thumbnail.jpg", detected_face)

Is it possible to have black and white and color image on same window by using opencv?

Is it possible to have black-and-white and color image on same window by using opencv libraray? How can I have both of these images on same window?
fraxel's answer has solved the problem with old cv interface. I would like to show it using cv2 interface, just to understand how this easy in new cv2 module. (May be it would be helpful for future visitors). Below is the code:
import cv2
import numpy as np
im = cv2.imread('kick.jpg')
img = cv2.imread('kick.jpg',0)
# Convert grayscale image to 3-channel image,so that they can be stacked together
imgc = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
both = np.hstack((im,imgc))
cv2.imshow('imgc',both)
cv2.waitKey(0)
cv2.destroyAllWindows()
And below is the output I got:
Yes it is, here is an example, expaination in the comments:
import cv
#open color and b/w images
im = cv.LoadImageM('1_tree_small.jpg')
im2 = cv.LoadImageM('1_tree_small.jpg',cv.CV_LOAD_IMAGE_GRAYSCALE)
#set up our output and b/w in rgb space arrays:
bw = cv.CreateImage((im.width,im.height), cv.IPL_DEPTH_8U, 3)
new = cv.CreateImage((im.width*2,im.height), cv.IPL_DEPTH_8U, 3)
#create a b/w image in rgb space
cv.Merge(im2, im2, im2, None, bw)
#set up and add the color image to the left half of our output image
cv.SetImageROI(new, (0,0,im.width,im.height))
cv.Add(new, im, new)
#set up and add the b/w image to the right half of output image
cv.SetImageROI(new, (im.width,0,im.width,im.height))
cv.Add(new, bw, new)
cv.ResetImageROI(new)
cv.ShowImage('double', new)
cv.SaveImage('double.jpg', new)
cv.WaitKey(0)
Its in python, but easy to convert to whatever..
Small improvement to the code with modern writing
concatenate
instead of
hstack
that is discontinued (stack can also be used)
import cv2
import numpy as np
im = cv2.imread('kick.jpg')
img = cv2.imread('kick.jpg',0)
# Convert grayscale image to 3-channel image,so that they can be stacked together
imgc = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
both = np.concatenate((im,imgc), axis=1) #1 : horz, 0 : Vert.
cv2.imshow('imgc',both)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
img = cv2.imread("image.jpg" , cv2.IMREAD_GRAYSCALE)
cv2.imshow("my image",img)
cv2.waitkey(0)
cv2.destroyAllWindow
#The image file should be in the application folder.
#The output file will be 'my image' name.
#The bottom line is to free up memory.

Resources