I have a set of coordinates/points I found under the original image before warpPerspective, how do I get the corresponding points in the now cropped & corrected image which is perspective corrected ?
For example:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread('sudoku.png')
rows,cols,ch = img.shape
pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])
point = np.array([[10,10]])
M = cv.getPerspectiveTransform(pts1,pts2)
dst = cv.warpPerspective(img,M,(300,300))
plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
How do I get the new coordinate [10,10] in img map to the dst image ?
You have to perform the same transformations (mathematically) as you have done on the image. In this case it means using cv2.perspectiveTransform (note that the input needs to have 1 row per point, 1 column, and 2 channels -- first being X, second Y cordinate).
This function will transform all the input points, it doesn't perform and cropping. You will need to post-process the transformed coordinates, and discard ones that fall outside the crop area. In your case you want to retain points where (0 <= x < 300) and (0 <= y < 300).
Sample code:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread('sudoku.png')
rows,cols,ch = img.shape
pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])
points = np.float32([[[10, 10]], [[116,128]], [[254,261]]])
M = cv.getPerspectiveTransform(pts1,pts2)
dst = cv.warpPerspective(img,M,(300,300))
# Transform the points
transformed = cv.perspectiveTransform(points, M)
# Perform the cropping -- filter out points that are outside the crop area
cropped = []
for pt in transformed:
x, y = pt[0]
if x >= 0 and x < dst.shape[1] and y >= 0 and y < dst.shape[0]:
print "Valid point (%d, %d)" % (x, y)
cropped.append([[x,y]])
else:
print "Out-of-bounds point (%d, %d)" % (x, y)
# Turn it back into a single numpy array
cropped = np.hstack(cropped)
# Visualize
plt.subplot(121)
plt.imshow(img)
for pt in points:
x, y = pt[0]
plt.scatter(x, y, s=100, c='red', marker='x')
plt.title('Input')
plt.subplot(122)
plt.imshow(dst)
for pt in transformed:
x, y = pt[0]
plt.scatter(x, y, s=100, c='red', marker='x')
plt.title('Output')
plt.show()
Console Output:
Out-of-bounds point (-53, -63)
Valid point (63, 67)
Valid point (192, 194)
Visualization:
Related
I am trying to generate a 3D matrix with a tube structure running through it. I can make the tube straight by copying a 2D numpy array with a circle centered at (x,y) inside, and I can make the tube slanted by adding an int to either the x or y axis for each slice I generate. My question is, how can I move the (x,y) coordinates so that they can form a curve? I can't add step sizes of curved functions like sine and cosine to the coordinates since to index the numpy array it must be an integer. What is a smart way to generate a curved tube from 2D slices by shifting the center coordinates?
Here is the code I am using to generate a straight tube as a 3D matrix:
import numpy as np
import cv2
import matplotlib.pyplot as plt
slice_2d = np.zeros((128,128))
circle_center = (50,50)
radius=10
slice_2d = cv2.circle(slice_2d, circle_center, radius, color=1, thickness=-1)
plt.imshow(slice_2d)
# then we repeat the slice 128 times to create a straight tube in a 3D matrix of 128,128,128
tube_matrix = []
for i in range(0,128):
tube_matrix.append(slice_2d)
tube_matrix = np.array(tube_matrix)
You may use any curve, scale and add offset as needed, and round the center coordinates to integer.
I used the curve for this post.
Here is the loop that adds the slices:
tube_matrix = []
for i in range(128):
circle_center = np.round(curve[i]*12 + 15).astype(int)
slice_2d = cv2.circle(np.zeros((128,128)), tuple(circle_center), radius, color=1, thickness=-1)
tube_matrix.append(slice_2d)
Each iteration the circle center changes according to the value of curve[i].
Note that curve[i] is scaled and rounded (and converted to int).
Here is the complete code (with some testing code):
import numpy as np
import cv2
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
# https://stackoverflow.com/questions/52014197/how-to-interpolate-a-2d-curve-in-python
# Define some points:
points = np.array([[0, 1, 8, 2, 2],
[1, 0, 6, 7, 2]]).T # a (nbre_points x nbre_dim) array
# Linear length along the line:
distance = np.cumsum( np.sqrt(np.sum( np.diff(points, axis=0)**2, axis=1 )) )
distance = np.insert(distance, 0, 0)/distance[-1]
alpha = np.linspace(0, 1, 128)
method = 'cubic'
interpolator = interp1d(distance, points, kind=method, axis=0)
curve = interpolator(alpha)
#slice_2d = np.zeros((128,128))
#circle_center = (30, 30)
img = np.zeros((128, 128, 3), np.uint8) + 255
radius = 10
tube_matrix = []
for i in range(128):
circle_center = np.round(curve[i]*12 + 15).astype(int)
slice_2d = cv2.circle(np.zeros((128,128)), tuple(circle_center), radius, color=1, thickness=-1)
tube_matrix.append(slice_2d)
#Draw cicle on image - for testing
img = cv2.circle(img, tuple(circle_center), radius, color=(i*10 % 255, i*20 % 255, i*30 % 255), thickness=2)
# Graph:
plt.figure(figsize=(7,7))
plt.plot(*curve.T, 'o')
plt.axis('equal'); plt.legend(); plt.xlabel('x'); plt.ylabel('y')
plt.figure(figsize=(7,7))
plt.imshow(img)
plt.show()
Testing image (img):
Description:
I have this data represented in a cartesian coordinate system with 256 columns and 640 rows. Each column represents an angle, theta, from -65 deg to 65 deg. Each row represents a range, r, from 0 to 20 m.
An example is given below:
With the following code I try to make a grid and transform each pixel location to the location it would have on a polar grid:
def polar_image(image, bearings):
(h,w) = image.shape
x_max = (np.ceil(np.sin(np.deg2rad(np.max(bearings)))*h)*2+1).astype(int)
y_max = (np.ceil(np.cos(np.deg2rad(np.min(np.abs(bearings))))*h)+1).astype(int)
blank = np.zeros((y_max,x_max,1), np.uint8)
for i in range(w):
for j in range(h):
X = (np.sin(np.deg2rad( bearings[i]))*j)
Y = (-np.cos(np.deg2rad(bearings[i]))*j)
blank[(Y+h).astype(int),(X+562).astype(int)] = image[h-1-j,w-1-i]
return blank
This returns an image as below:
Questions:
This is sort of what I actually want to achieve except from two things:
1) there seem to be some artifacts in the new image and also the mapping seems a bit coarse.
Does someone have a suggestion on how to interpolate to get rid of this?
2) The image remains in a Cartesian representation, meaning that I don't have any polar gridlines, nor can I visualize intervals of range/angle.
Anybody know how to visualize the polar grids with axis ticks in theta and range?
You can use pyplot.pcolormesh() to plot the converted mesh:
import numpy as np
import pylab as pl
img = pl.imread("c:/tmp/Wnov4.png")
angle_max = np.deg2rad(65)
h, w = img.shape
angle, r = np.mgrid[-angle_max:angle_max:h*1j, 0:20:w*1j]
x = r * np.sin(angle)
y = r * np.cos(angle)
fig, ax = pl.subplots()
ax.set_aspect("equal")
pl.pcolormesh(x, y, img, cmap="gray");
or you can use the remap() in OpenCV to convert it to a new image:
import cv2
import numpy as np
from PIL import Image
img = cv2.imread(r"c:/tmp/Wnov4.png", cv2.IMREAD_GRAYSCALE)
angle_max = np.deg2rad(65)
r_max = 20
x = np.linspace(-20, 20, 800)
y = np.linspace(20, 0, 400)
y, x = np.ix_(y, x)
r = np.hypot(x, y)
a = np.arctan2(x, y)
map_x = r / r_max * img.shape[1]
map_y = a / (2 * angle_max) * img.shape[0] + img.shape[0] * 0.5
img2 = cv2.remap(img, map_x.astype(np.float32), map_y.astype(np.float32), cv2.INTER_CUBIC)
Image.fromarray(img2)
I am learning how to properly use a selective search algorithm to create bounding boxes around an image, extract the smaller images within the bounding box and then run further analysis on the smaller images.
I am able to obtain the bounding boxes through the following, but how do I save/extract/export the images within each bounding box?
import skimage.data
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import selectivesearch
import time
import io
import PIL
import scipy.misc
from skimage.io import imread
from PIL import Image
from skimage.transform import rescale, resize, downscale_local_mean
def main():
# loading astronaut image
# image = skimage.io.imread('/Users/vivek/Desktop/IMG_3350.JPG')
# img = resize(image, (500,500), mode = 'reflect')
img = skimage.io.imread('/Users/vivek/Downloads/IMG_3350_640x480.JPG')
print ('image loaded')
# perform selective search
print ('initializing selective search')
start = time.time()
img_lbl, regions = selectivesearch.selective_search(
img, scale=600, sigma=0.9, min_size=10)
candidates = set()
for r in regions:
# excluding same rectangle (with different segments)
if r['rect'] in candidates:
continue
# excluding regions smaller than 2000 pixels
if r['size'] < 2000:
continue
# distorted rects
x, y, w, h = r['rect']
if w / h > 1.2 or h / w > 1.2:
continue
candidates.add(r['rect'])
print ('selective search complete')
end = time.time()
totalTime = end - start
print ('time taken to run this is : ' + str(totalTime))
# draw rectangles on the original image
fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6))
ax.imshow(img)
for x, y, w, h in candidates:
print x, y, w, h
rect = mpatches.Rectangle(
(x, y), w, h, fill=False, edgecolor='red', linewidth=1)
ax.add_patch(rect)
#plt.imsave("testerimage.jpg", None)
plt.show()
if __name__ == "__main__":
main()
Thanks in advance
You know how to get each rectangle using the lines
for x, y, w, h in candidates:
To get the image of the in this rectangle just do:
imgRect = img[y:y+h,x:x+w]
I want to create a binary image mask, containing only ones and zeros in python. The Region of Interest(white) is non-rectangular, defined by 4 corner points and looks for example as follows:
In my approach, I first calculate the line equation of the upper and lower ROI border and then I check for each mask element, if it's smaller or bigger than the boarders. The code is working, but far to slow. A 2000x1000 mask takes up to 4s of processing my machine.
from matplotlib import pyplot as plt
import cv2
import numpy as np
import time
def line_eq(line):
"""input:
2 points of a line
returns:
slope and intersection of the line
"""
(x1, y1), (x2, y2) = line
slope = (y2 - y1) / float((x2 - x1))
intersect = int(slope * (-x1) + y1)
return slope,intersect
def maskByROI(mask,ROI):
"""
input:
ROI: with 4 corner points e.g. ((x0,y0),(x1,y1),(x2,y2),(x3,y3))
mask:
output:
mask with roi set to 1, rest to 0
"""
line1 = line_eq((ROI[0],ROI[1]))
line2 = line_eq((ROI[2],ROI[3]))
slope1 = line1[0]
intersect1 = line1[1]
#upper line
if slope1>0:
for (x,y), value in np.ndenumerate(mask):
if y > slope1*x +intersect1:
mask[x,y] = 0
else:
for (x,y), value in np.ndenumerate(mask):
if y < slope1*x +intersect1:
mask[x,y] = 0
#lower line
slope2 = line2[0]
intersect2 = line2[1]
if slope2<0:
for (x,y), value in np.ndenumerate(mask):
if y > slope2*x +intersect2:
mask[x,y] = 0
else:
for (x,y), value in np.ndenumerate(mask):
if y < slope2*x +intersect2:
mask[x,y] = 0
return mask
mask = np.ones((2000,1000))
myROI = ((750,0),(900,1000),(1000,1000),(1500,0))
t1 = time.time()
mask = maskByROI(mask,myROI)
t2 = time.time()
print "execution time: ", t2-t1
plt.imshow(mask,cmap='Greys_r')
plt.show()
What is a more efficient way to create a mask like this?
Are there any similar solutions for non-rectangular shapes provided by
numpy, OpenCV or a similar Library?
Draw the mask with fillPoly:
mask = np.ones((1000, 2000)) # (height, width)
myROI = [(750, 0), (900, 1000), (1000, 1000), (1500, 0)] # (x, y)
cv2.fillPoly(mask, [np.array(myROI)], 0)
This should take ~1ms.
I want to use OCR to capture the bowling scores from the monitor at the lances. I had a look at this sudoku solver, as I think its pretty similar - numbers and grids right? It has trouble finding the horizontal lines. Has anyone got any tips for pre-processing this image to make it easier to detect the lines (or numbers!). Also any tips for how to deal with the split (the orange ellipse around some of the 8's int he image)?
So far I have got the outline of the score area and cropped it.
import matplotlib
matplotlib.use('TkAgg')
from skimage import io
import numpy as np
import matplotlib.pyplot as plt
from skimage import measure
from skimage.color import rgb2gray
# import pytesseract
from matplotlib.path import Path
from qhd import *
def polygonArea(poly):
"""
Return area of an unclosed polygon.
:see: https://stackoverflow.com/a/451482
:param poly: (n,2)-array
"""
# we need a plain list for the following operations
if isinstance(poly, np.ndarray):
poly = poly.tolist()
segments = zip(poly, poly[1:] + [poly[0]])
return 0.5 * abs(sum(x0*y1 - x1*y0
for ((x0, y0), (x1, y1)) in segments))
filename = 'good.jpg'
image = io.imread(filename)
image = rgb2gray(image)
# Find contours at a constant value of 0.8
contours = measure.find_contours(image, 0.4)
# Display the image and plot all contours found
fig, ax = plt.subplots()
c = 0
biggest = None
biggest_size = 0
for n, contour in enumerate(contours):
curr_size = polygonArea(contour)
if curr_size > biggest_size:
biggest = contour
biggest_size = curr_size
biggest = qhull2D(biggest)
# Approximate that so we just get a rectangle.
biggest = measure.approximate_polygon(biggest, 500)
# vertices of the cropping polygon
yc = biggest[:,0]
xc = biggest[:,1]
xycrop = np.vstack((xc, yc)).T
# xy coordinates for each pixel in the image
nr, nc = image.shape
ygrid, xgrid = np.mgrid[:nr, :nc]
xypix = np.vstack((xgrid.ravel(), ygrid.ravel())).T
# construct a Path from the vertices
pth = Path(xycrop, closed=False)
# test which pixels fall within the path
mask = pth.contains_points(xypix)
# reshape to the same size as the image
mask = mask.reshape(image.shape)
# create a masked array
masked = np.ma.masked_array(image, ~mask)
# if you want to get rid of the blank space above and below the cropped
# region, use the min and max x, y values of the cropping polygon:
xmin, xmax = int(xc.min()), int(np.ceil(xc.max()))
ymin, ymax = int(yc.min()), int(np.ceil(yc.max()))
trimmed = masked[ymin:ymax, xmin:xmax]
plt.imshow(trimmed, cmap=plt.cm.gray), plt.title('trimmed')
plt.show()
https://imgur.com/LijB85I is an example of how the score is displayed.