How to paint text in an image using python - python

I have a image:
It has some time in it.
But I need to convert it to this using python:
this:
to this
In short, I need to paint the text in that image black using python. I think maybe opencv is useful for this, But not sure. And I need to do that for any time that will be in that same type of image(like the image before editing, just different digits).
I think we can use canny to detect edges of the digits and the dots and then paint the area between them black. I don't know the perfect way of doing this, but I think it might work. Hoping for a solution. Thanks in advance.

This is how I did it, I recommend that you play with morphological operations and also look for ways to remove blobs of certain sizes to fix the "22" problem. You can also adjust the tolerance value.
import cv2
imgray = cv2.imread('j3iIp.png',0)
point = (0,0)
src = imgray.copy()
tolerance = 25
connectivity = 4
flags = connectivity
flags |= cv2.FLOODFILL_FIXED_RANGE
cv2.floodFill(src, None, point, (0, 255, 255), (tolerance,) * 3, (tolerance,) * 3, flags)
src = cv2.subtract(255, src)
cv2.imshow('filled', src)
cv2.waitKey(0)
cv2.imwrite("result.jpg",src)
cv2.destroyAllWindows()
The result is:

As mentioned, due to adjascent 22 it is creating problem, so the procedure will be like this
import numpy as np
import matplotlib.pyplot as plt
from skimage.segmentation import flood_fill
img = io.imread('number.png', as_gray=True)
plt.imshow(img, cmap='gray')
You app ly flood fill
ff = flood_fill(img, (0,0), 125)
ff[ff != 125] = 0
ff[ff == 125] = 255
plt.imshow(ff, cmap='gray')
Finally, save the image
io.imsave('out.png', ff)

The border pixels are turned on to distinguish the characters from background.
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('j3iIp.jpg',0)
ret,thresh2 = cv2.threshold(img,120,255,cv2.THRESH_BINARY_INV)
cv2.imshow("result", thresh2)
cv2.waitKey(0)
cv2.destroyAllWindows()
img1 = cv2.imwrite("thresh4.jpg",thresh2)

I first tried using cv2.findContours() but there is no hierarchical difference between the white space inside each character and the white space locked in between 22. The "22 problem" displayed in Epsi95's solution can't be solved using contour hierarchy information.
The problem can be solved using cv2.floodFill() and horizontal edge detection to determine a seed point per object.
import cv2
import matplotlib.pyplot as plt
import numpy as np
# Fixed colon locations (assumed to be known constants)
Y1, Y2 = (24, 48)
X1, X2 = (45, 93)
# Plot grayscale image
img = np.uint8(cv2.imread("digits.png", cv2.IMREAD_GRAYSCALE) / 255)
fig, axs = plt.subplots(2)
axs[0].imshow(img, cmap="gray")
# Horizontal edge detection at Y1 to determine the seed points (for flood filling)
kernel = np.array([-1, -1, 1, 1], dtype=np.int8) # bbww detector
edge_det = np.correlate(img[Y1, :], kernel, mode="same") # 2 # bbWw
edge_xloc = np.flatnonzero(edge_det == 2)
seeds_yx = np.array(
[
[Y1, edge_xloc[1]],
[Y1, edge_xloc[edge_xloc < X1][-2]],
[Y1, X1],
[Y1, edge_xloc[edge_xloc > X1][1]],
[Y1, edge_xloc[edge_xloc < X2][-2]],
[Y1, X2],
[Y1, edge_xloc[edge_xloc > X2][1]],
[Y1, edge_xloc[-2]],
[Y2, X1],
[Y2, X2],
]
)
axs[0].plot(seeds_yx[:, 1], seeds_yx[:, 0], "ro")
# Flood fill at seed points
for y, x in seeds_yx:
cv2.floodFill(img, mask=None, seedPoint=(x, y), newVal=0)
axs[1].imshow(img, cmap="gray")
fig.show()
Code results in this plot

Related

How to calculate irregularities in a surface in python

I want to calculate how irregular the surface is for various images. I attach one of the images below:
The idea would be to calculate the direction vectors every few samples and see how similar they are to each other (with the scalar product for example), if they are very similar it means that the surface is quite regular and the scalar product will be close to 1. If the irregularity is huge, it will be close to 0. Something like this:
I would be grateful for any help. Thank you!!!
Here's the first part of a solution for you – computing the "roughness" is up to you...
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
# Read, convert, threshold image
im = cv.imread('5FH31.png')
imgray = cv.cvtColor(im, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(imgray, 127, 255, 0)
# Find the outermost contour
contours, hierarchy = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# Decimate the contour to an approximation, squeeze to get an N x 2 array
dec_points = np.squeeze(cv.approxPolyDP(np.squeeze(contours), 3, True))
# Compute vectors from point + follower
vecs = dec_points - np.roll(dec_points, 1, 0)
# TODO: compute "roughness"? :)
# Debug: show our image and data
plt.imshow(im)
xs, ys = zip(*dec_points)
plt.plot(xs, ys, 'y')
plt.plot(xs, ys, 'r+')
plt.tight_layout()
plt.show()
The debug image will look something like this - you can see each point and the contour they form.

Finding matching data point within two images using Python

I am have having two images, namely Fig 1 and Fig 2. Both taken from the same source but not aligned. The task is to find the common data point among these two images and draw lines between the data points that match in both the images., I am looking at this figure should be like Fig 4.
So far, I have used OpenCV and written the following codes:
import cv2
import matplotlib.pyplot as plt
img_file1= "Fig_1.png"
img_file2= "Fig_2.png"
img1= cv2.imread(img_file1)
img2= cv2.imread(img_file2)
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
figure, ax = plt.subplots(1, 2, figsize=(16, 8))
ax[0].imshow(img1, cmap='gray')
ax[1].imshow(img2, cmap='gray')
#sift
sift = cv2.xfeatures2d.SIFT_create()
keypoints_1, descriptors_1 = sift.detectAndCompute(img1,None)
keypoints_2, descriptors_2 = sift.detectAndCompute(img2,None)
#feature matching
bf = cv2.BFMatcher(cv2.NORM_L1, crossCheck=True)
matches = bf.match(descriptors_1,descriptors_2)
matches = sorted(matches, key = lambda x:x.distance)
img3 = cv2.drawMatches(img1, keypoints_1, img2, keypoints_2, matches[:50], img2, flags=2)
plt.imshow(img3),plt.show()
This gives to be not expected result, see figure 4. Plus look quite messy and unclear.
Can anyone help me with how to do this? Thanks in advance.
Fig 1
Fig 2
img3
Fig 3
The transformation seems purely translational. Try template matching by normalized grayscale correlation.
Basically, this seems to me a registration problem (the images need to be registered).
Here is what you can do:
find the location of the points with connected components analysis
calculate the shift needed to register the two images. Here it seems your images are only translated so a simple crosscorrelation-based registration is enough.
from skimage.registration import phase_cross_correlation
from skimage.io import imread
from skimage.measure import label, regionprops
from skimage.filters import threshold_otsu
from matplotlib.pyplot import imshow, plot, figure
import numpy as np
# Load images
img_a = imread("671OL.jpg", as_gray=True)
img_b = imread("zpevD.jpg", as_gray=True)
# apply threshold
th_img_a = img_a > threshold_otsu(img_a)
th_img_b = img_b > threshold_otsu(img_b)
# measure connected component
img_lable = label(th_img_a)
r_props = regionprops(img_lable)
figure(figsize=(15,7))
rows, cols = img_b.shape
# calculate the registration (shift) of the two images
flow = phase_cross_correlation(th_img_a, th_img_b)
# stack the images and trace the segments that connect the points
d=10
# a vertical white bar between the two pictures
vbar=np.ones((rows,d))
xshift = cols+d
dy,dx = flow[0]
dx=dx + xshift
imshow(np.hstack([img_a, vbar, img_b]), cmap='gray')
for rp in r_props:
y0,x0 = rp.centroid
x1 = x0 + dx
y1 = y0 - dy
if y1<rows and x1 < 2*cols + d:
# filter out points that are not in img_b
plot([x0,x1],[y0,y1], '--', alpha=0.5)

Get coordinates of a binary image and sort them clockwise starting from a point

I'm working with binary images representing contours (taken through cv2.Canny), and i want to get the coordinates of each contour clockwise, starting from the first point as intersection of the image and a horizontal line located in the center of the image. Assuming that the image i want to use is a circular contour, i would like to get something like this (assuming Y decreasing vertically, as matplotlib.pyplot.imshow does):
I tried with the following code:
indices = numpy.where(edges == [255]) #edges is the contour image
print(indices)
But this solution sorts the coordinates from the upper side of the image. I tried other solution found on the web too, but none of them seems to be usefull for this task.
I will recycle my idea from that answer incorporating the arctan2 function from numpy.
Given is an input image like this:
The output will be a plot like this:
Here's the code, which is hopefully self-explaining:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Generate artificial image
img = np.zeros((400, 400), np.uint8)
center = (150, 150)
img = cv2.circle(img, center, 100, 255, 1)
cv2.imwrite('images/img.png', img)
# Find contour(s)
cnts, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# Center contour
cnt = np.squeeze(cnts[0]) - center
# Calculate atan2 values, and sort
val = np.sort(np.arctan2(cnt[:, 0], cnt[:, 1]))
idx = np.argsort(np.arctan2(cnt[:, 0], cnt[:, 1]))
# atan2 uses (1, 0) as starting point, so correct by 1/2 * pi
corr = np.where(val <= (-0.5 * np.pi))[0][-1]
# Build final indices
indFinal = np.concatenate((idx[corr - 1:], idx[0:corr]))
x = cnt[indFinal, 0]
y = cnt[indFinal, 1]
# Generate plot
ax = plt.subplot(121)
plt.plot(x)
plt.title('x')
ax = plt.subplot(122)
plt.plot(y)
plt.title('y')
plt.savefig('images/plot.png')
Caveat: Concave contours will likely cause corrupted results.

Image processing - fill in hollow circles

I have a binary black and white images that looks like this
I want to fill in those white circles to be solid white disks. How can I do this in Python, preferrably using skimage?
You can detect circles with skimage's methods hough_circle and hough_circle_peaks and then draw over them to "fill" them.
In the following example most of the code is doing "hierarchy" computation for the best fitting circles to avoid drawing circles which are one inside another:
# skimage version 0.14.0
import math
import numpy as np
import matplotlib.pyplot as plt
from skimage import color
from skimage.io import imread
from skimage.transform import hough_circle, hough_circle_peaks
from skimage.feature import canny
from skimage.draw import circle
from skimage.util import img_as_ubyte
INPUT_IMAGE = 'circles.png' # input image name
BEST_COUNT = 6 # how many circles to draw
MIN_RADIUS = 20 # min radius should be bigger than noise
MAX_RADIUS = 60 # max radius of circles to be detected (in pixels)
LARGER_THRESH = 1.2 # circle is considered significantly larger than another one if its radius is at least so much bigger
OVERLAP_THRESH = 0.1 # circles are considered overlapping if this part of the smaller circle is overlapping
def circle_overlap_percent(centers_distance, radius1, radius2):
'''
Calculating the percentage area overlap between circles
See Gist for comments:
https://gist.github.com/amakukha/5019bfd4694304d85c617df0ca123854
'''
R, r = max(radius1, radius2), min(radius1, radius2)
if centers_distance >= R + r:
return 0.0
elif R >= centers_distance + r:
return 1.0
R2, r2 = R**2, r**2
x1 = (centers_distance**2 - R2 + r2 )/(2*centers_distance)
x2 = abs(centers_distance - x1)
y = math.sqrt(R2 - x1**2)
a1 = R2 * math.atan2(y, x1) - x1*y
if x1 <= centers_distance:
a2 = r2 * math.atan2(y, x2) - x2*y
else:
a2 = math.pi * r2 - a2
overlap_area = a1 + a2
return overlap_area / (math.pi * r2)
def circle_overlap(c1, c2):
d = math.sqrt((c1[0]-c2[0])**2 + (c1[1]-c2[1])**2)
return circle_overlap_percent(d, c1[2], c2[2])
def inner_circle(cs, c, thresh):
'''Is circle `c` is "inside" one of the `cs` circles?'''
for dc in cs:
# if new circle is larger than existing -> it's not inside
if c[2] > dc[2]*LARGER_THRESH: continue
# if new circle is smaller than existing one...
if circle_overlap(dc, c)>thresh:
# ...and there is a significant overlap -> it's inner circle
return True
return False
# Load picture and detect edges
image = imread(INPUT_IMAGE, 1)
image = img_as_ubyte(image)
edges = canny(image, sigma=3, low_threshold=10, high_threshold=50)
# Detect circles of specific radii
hough_radii = np.arange(MIN_RADIUS, MAX_RADIUS, 2)
hough_res = hough_circle(edges, hough_radii)
# Select the most prominent circles (in order from best to worst)
accums, cx, cy, radii = hough_circle_peaks(hough_res, hough_radii)
# Determine BEST_COUNT circles to be drawn
drawn_circles = []
for crcl in zip(cy, cx, radii):
# Do not draw circles if they are mostly inside better fitting ones
if not inner_circle(drawn_circles, crcl, OVERLAP_THRESH):
# A good circle found: exclude smaller circles it covers
i = 0
while i<len(drawn_circles):
if circle_overlap(crcl, drawn_circles[i]) > OVERLAP_THRESH:
t = drawn_circles.pop(i)
else:
i += 1
# Remember the new circle
drawn_circles.append(crcl)
# Stop after have found more circles than needed
if len(drawn_circles)>BEST_COUNT:
break
drawn_circles = drawn_circles[:BEST_COUNT]
# Actually draw circles
colors = [(250, 0, 0), (0, 250, 0), (0, 0, 250)]
colors += [(200, 200, 0), (0, 200, 200), (200, 0, 200)]
fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(10, 4))
image = color.gray2rgb(image)
for center_y, center_x, radius in drawn_circles:
circy, circx = circle(center_y, center_x, radius, image.shape)
color = colors.pop(0)
image[circy, circx] = color
colors.append(color)
ax.imshow(image, cmap=plt.cm.gray)
plt.show()
Result:
Do a morphological closing (explanation) to fill those tiny gaps, to complete the circles. Then fill the resulting binary image.
Code :
from skimage import io
from skimage.morphology import binary_closing, disk
import scipy.ndimage as nd
import matplotlib.pyplot as plt
# Read image, binarize
I = io.imread("FillHoles.png")
bwI =I[:,:,1] > 0
fig=plt.figure(figsize=(24, 8))
# Original image
fig.add_subplot(1,3,1)
plt.imshow(bwI, cmap='gray')
# Dilate -> Erode. You might not want to use a disk in this case,
# more asymmetric structuring elements might work better
strel = disk(4)
I_closed = binary_closing(bwI, strel)
# Closed image
fig.add_subplot(1,3,2)
plt.imshow(I_closed, cmap='gray')
I_closed_filled = nd.morphology.binary_fill_holes(I_closed)
# Filled image
fig.add_subplot(1,3,3)
plt.imshow(I_closed_filled, cmap='gray')
Result :
Note how the segmentation trash has melded to your object on the lower right and the small cape on the lower part of the middle object has been closed. You might want to continue with an morphological erosion or opening after this.
EDIT: Long response to comments below
The disk(4) was just the example I used to produce the results seen in the image. You will need to find a suitable value yourself. Too big of a value will lead to small objects being melded into bigger objects near them, like on the right side cluster in the image. It will also close gaps between objects, whether you want it or not. Too small of a value will lead to the algorithm failing to complete the circles, so the filling operation will then fail.
Morphological erosion will erase a structuring element sized zone from the borders of the objects. Morphological opening is the inverse operation of closing, so instead of dilate->erode it will do erode->dilate. The net effect of opening is that all objects and capes smaller than the structuring element will vanish. If you do it after filling then the large objects will stay relatively the same. Ideally it should remove a lot of the segmentation artifacts caused by the morphological closing I used in the code example, which might or might not be pertinent to you based on your application.
I don't know skimage but if you'd use OpenCv, I would do a Hough transform for circles, and then just draw them over.
Hough Transform is robust, if there are some small holes in the circles that is no problem.
Something like:
circles = cv2.HoughCircles(gray, cv2.cv.CV_HOUGH_GRADIENT, 1.2, 100)
# ensure at least some circles were found
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
# you can check size etc here.
for (x, y, r) in circles:
# draw the circle in the output image
# you can fill here.
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
# show the output image
cv2.imshow("output", np.hstack([image, output]))
cv2.waitKey(0)
See more info here: https://www.pyimagesearch.com/2014/07/21/detecting-circles-images-using-opencv-hough-circles/

Scikit image color filtering and changing parts of image to array

I want to do some Optical Mark Recognition using scikit-image. I am looking for 2 functions
Lets say I have an image that looks like this:
The way I would detect it is using filters to clean the image:
(ie bilateral and Gaussian filtering for noise specks in filter module)
Then I would use a gray-scaling in color module
Third I would use a canny edge detector in filter module
I am trying to find out what do I use to filter the cells by color so I can mark reds and blues apart?
I am guessing there is some function for Hues, saturation, and brightness or RGB that can be used to filter out specific colors or some thing that can be used with k-means from scikit learn for filtering data
Second is how would I transform this image to an numpy array/pandas dataframe which will be the following:
[[1,2,0,2,0]
[0,1,1,0,1]
[0,0,1,2,0]]
Where red is 1, blue is 2, and white is 0. I have seen some people put a line that goes down it but do not know what it is called or if it is available in sk-image.
The following code makes use of scikit-image's peak detector, applied on a distance map computed between the image and the values of pure red and blue:
from skimage import io, color, img_as_float
from skimage.feature import corner_peaks, plot_matches
import matplotlib.pyplot as plt
import numpy as np
image = img_as_float(io.imread('colordots.jpg'))
black_mask = color.rgb2gray(image) < 0.1
distance_red = color.rgb2gray(1 - np.abs(image - (1, 0, 0)))
distance_blue = color.rgb2gray(1 - np.abs(image - (0, 0, 1)))
distance_red[black_mask] = 0
distance_blue[black_mask] = 0
coords_red = corner_peaks(distance_red, threshold_rel=0.9, min_distance=50)
coords_blue = corner_peaks(distance_blue, threshold_rel=0.9, min_distance=50)
f, ((ax0, ax1), (ax2, ax3)) = plt.subplots(2, 2, figsize=(15, 10))
ax0.imshow(image)
ax0.set_title('Input image')
ax1.imshow(image)
ax1.set_title('Marker locations')
ax1.plot(coords_red[:, 1], coords_red[:, 0], 'ro')
ax1.plot(coords_blue[:, 1], coords_blue[:, 0], 'bo')
ax1.axis('image')
ax2.imshow(distance_red, interpolation='nearest', cmap='gray')
ax2.set_title('Distance to pure red')
ax3.imshow(distance_blue, interpolation='nearest', cmap='gray')
ax3.set_title('Distance to pure blue')
plt.show()

Categories

Resources