Find image in scaled Image - python-3.x

I have to find an image during a stream of desktop. My code work, but if the image, during the stream, is resized, the program not work. How can I solve this problems?
from PIL import ImageGrab
import numpy as np
import cv2
template = cv2.imread('piccola.png') #image to find
w, h = template.shape[:-1]
while 1:
img = ImageGrab.grab(bbox=(0,0,800,600)) #bbox specifies specific region (bbox= x,y,width,height *starts top-left)
img_np = np.array(img) #this is the array obtained from conversion
#frame = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY)
res = cv2.matchTemplate(img_np, template, cv2.TM_CCOEFF_NORMED)
threshold = .85
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]): # Switch columns and rows
cv2.rectangle(img_np, pt, (pt[0] + h, pt[1] + w), (0, 0, 255), 2)
cv2.imshow("output", img_np)
if cv2.waitKey(25) & 0xFF == ord('q'):
cv2.destroyAllWindows()
break

Instead of using cv2.matchTemplate, you can actually extract features from your template image, i.e. extract features such as SIFT/ORB/KAZE/BRISK and match them against by extracting the same features from the grabbed image. You can set up a threshold for the matching criteria.
you can read more about feature description and matching here - https://docs.opencv.org/3.4/d5/dde/tutorial_feature_description.html
Sample code for your understanding.
import cv2
import numpy as np
img1 = cv2.imread("template.jpg", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE)
# ORB Detector
orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
# Brute Force Matching
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
matches = sorted(matches, key = lambda x:x.distance)
#drawing the matches
matching_result = cv2.drawMatches(img1, kp1, img2, kp2, matches[:50], None, flags=2)
you can filter the matches which have the distance > 0.7 (usual threshold) and check the percentage of matches. Based on that you can decide how well it's finding the similar images.
Methods like SIFT is patented but performs well.
Methods like ORB is fastest, but not invariant to scale.
you can try methods like KAZE and AKAZE.

Related

how to use a warping to copy a source image to the target image, based on the adapted transformation

I am working on a task that involves the use of image processing techniques to clean a noisy image with the help of other (noisy) images which overlap it in
their area. To achieve this, I will need to calculate the warp of each
of the images to the target image, i.e. calculate the alignment
between them.
My goal is to apply the necessary warps in order to copy each of the
images to the target image.
Example images:
source_01.jpg, source_02.jpg, target.jpg
To achieve the above, I first implemented SIFT using the OpenCV module to obtain the [x, y, r, t] values, calculate keypoints distances, and also Implemented a simple RANSAC loop with the homography solver in other to calculate more accurate the locations of matched key points in both the images.
My code.
import numpy as np
import cv2
import os
import matplotlib.pyplot as plt
import tqdm
from functools import reduce
from operator import concat
from skimage.measure import ransac
from skimage.transform import ProjectiveTransform, AffineTransform
from functools import reduce
from operator import concat
file_path = 'denoising_sets\cameleon__N_8__sig_noise_5__sig_motion_103'
def read_images(file_path):
images= []
for root,dir,files in os.walk(os.path.join(os.getcwd(),file_path )):
for file in files:
images.append(cv2.imread(os.path.join(root,file),0))
return images
images = read_images(file_path)
img1 = images[0]
img2 = images[1]
target_image = images[-1]
def good_match_keypoints(img1,img2, show=True):
# Initiate SIFT detector
sift = cv2.SIFT_create()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# BFMatcher with default params
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)
# Apply ratio test
good = []
for m, n in matches:
if m.distance < 0.8 * n.distance:
good.append([m])
good_match = reduce(concat, good)
# cv2.drawMatchesKnn expects list of lists as matches.
img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, good, flags=2, outImg=img2)
if show:
plt.imshow(img3), plt.show()
return good_match, kp1, kp2
good_match, kp1, kp2 = good_match_keypoints(img1,img2, show=True)
keypoints distance
pts1 = np.float32([kp1[m.queryIdx].pt for m in good_match])
pts2 = np.float32([kp2[m.trainIdx].pt for m in good_match])
I used the RANSAC loop with the homography solver in other to calculate more accurately the locations of matched key points in both the images.
def ransc_loop(pts1, pts2, show=True):
model, inliers = ransac(
(pts1, pts2),
AffineTransform, min_samples=4,
residual_threshold=8, max_trials=10000
)
n_inliers = np.sum(inliers)
inlier_keypoints_left = [cv2.KeyPoint(point[0], point[1], 1) for point in pts1[inliers]]
inlier_keypoints_right = [cv2.KeyPoint(point[0], point[1], 1) for point in pts2[inliers]]
placeholder_matches = [cv2.DMatch(idx, idx, 1) for idx in range(n_inliers)]
image3 = cv2.drawMatches(img1, inlier_keypoints_left, img2, inlier_keypoints_right, placeholder_matches, None)
if show:
plt.imshow(image3)
plt.show()
src_pts = np.float32([ inlier_keypoints_left[m.queryIdx].pt for m in placeholder_matches ]).reshape(-1, 2)
dst_pts = np.float32([ inlier_keypoints_right[m.trainIdx].pt for m in placeholder_matches ]).reshape(-1, 2)
return src_pts, dst_pts
src_pts, dst_pts = ransc_loop(pts1, pts2)
This is what the src_pts and dst_pts look like
(array([[ 106.41315, 332.88037],
[ 120.28672, 314.56943],
array([[ 116.75639, 576.8563 ],
[ 130.71513, 555.35364],
Finally, I want to use warping to copy a source image to the target image, based on the adapted transformation. I don't know how to achieve this. Please I need assistance on how to achieve this.
This are the steps involve

How to crop images based on mask threshold?

I have to crop a lot of images manually. Not the funniest thing to do. So I thought I'd try to do it using Python.
I can detect the subject, create a mask, but I have no idea how to get the points from the very bottom part and crop based on them.
Any help is appreciated
import cv2
img = cv2.imread('image5.jpg')
h, w = img.shape[:2]
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thr = cv2.threshold(gray, 192, 255, cv2.THRESH_BINARY_INV)[1]
cv2.imwrite('result5.png', thr)
you can try to find all external contours using cv2.RETR_EXTERNAL and pick the bottom most point, like this:
import cv2
import numpy as np
import imutils
im = cv2.imread('images/tennis.jpg')
# Percent of original size
scale_percent = 20
width = int(im.shape[1] * scale_percent / 100)
height = int(im.shape[0] * scale_percent / 100)
dim = (width, height)
# Resize image
im = cv2.resize(im, dim, interpolation = cv2.INTER_AREA)
# Convert to grayscale
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
# Canny
canny_output = cv2.Canny(im, 120, 240)
# Find external contours
contours, hierarchy = cv2.findContours(canny_output, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#cv2.drawContours(im, [contours[0]], 0, (0,255,0), 3) # Uncomment this line to see what contour opencv is finding
# Pick the bottom most point and add an offset (whatever value you want, this is just for aesthetics)
c = contours[0]
bottommost = tuple(c[c[:, :, 1].argmax()][0])[1] + 5
# Crop image
im = im[:bottommost, :]
# Show image
cv2.imshow('image', im)
cv2.waitKey()
Very good thinking I'd say! now the implementation:
xx,yy = thrs.nonzero()
max_crop_h = xx.max()
crop = img[:max_crop_h,:]
numpy has your back!

Question about skimage.transform.PolynomialTransform

I have two images (channel 1 and channel 2) and I'm trying to compute the polynomial transform that warps one image into the other image. First, I created an ORB object and computed the affine transform between the two images (post-affine). Then I decided to try to use skimage.transform.PolynomialTransform. However, when I try to compute the transform, the returned NumPy array has either NaN values or 0 values, even though the original image had a non-zero float value at that location (post-polynomial). What am I doing wrong? Code included below, images in following link. https://drive.google.com/drive/folders/1mWxUvLFLK5-rYCrxs3-uGKFxKq2wXDjS?usp=sharing Thanks in advance!
Note: I know that the question Image warping with scikit-image and transform.PolynomialTransform is similar, but in my opinion the two aren't duplicates. Although that user's problem is with the same function, the pixels in their transformed images have values, whereas by and large mine don't.
import cv2
from ImageConversion import ImageConversion # self-written, irrelevant
import matplotlib
matplotlib.use('macosX')
import matplotlib.pyplot as plt
from scipy.ndimage import uniform_filter
from skimage.draw import circle_perimeter
from skimage.transform import PolynomialTransform, warp
def affine_transform(self):
channel1_u8 = self.channel1.astype('uint8') # necessary for detectAndCompute
channel2_u8 = self.channel2.astype('uint8')
orb = cv2.ORB_create(100)
#kp1, des1 = orb.detectAndCompute(channel1_32, None)
#kp2, des2 = orb.detectAndCompute(channel2_32, None)
kp1, des1 = orb.detectAndCompute(channel1_u8, None)
kp2, des2 = orb.detectAndCompute(channel2_u8, None)
matcher = cv2.DescriptorMatcher_create(cv2.DESCRIPTOR_MATCHER_BRUTEFORCE_HAMMING)
matches = matcher.match(des1, des2, None)
matches = sorted(matches, key = lambda x:x.distance)
points1 = np.zeros((len(matches), 2), dtype = np.float32)
points2 = np.zeros((len(matches), 2), dtype = np.float32)
for i, match in enumerate(matches):
points1[i, :] = kp1[match.queryIdx].pt # index of descriptor in query descriptors, ie index of descriptor in channel 1 which is the image we wish to map to channel 2
points2[i, :] = kp2[match.trainIdx].pt
mat_coeff, inliers = cv2.estimateAffine2D(points1, points2) # inliers only here because estimateAffine2D returns both matrix coefficients and inliers
print(mat_coeff)
rows, cols = channel1_u8.shape
#dst = cv2.warpAffine(channel1_u8, mat_coeff, (cols, rows))
dst = cv2.warpAffine(self.channel1, mat_coeff, (cols, rows))
return mat_coeff, dst
tform = PolynomialTransform()
tform.estimate(self.channel2, dst, order = 3)
warped_1 = warp(dst, tform, mode = 'constant')
I found the error. I was trying to feed PolynomialTransform.estimate the entire image, rather than identified key points in the image.

how do I extract all pixel values from a certain ROI and then store it as a CSV file and again use that csv file to get back that ROI

After applying mask original image
import cv2
import dlib
import numpy as np
img = cv2.imread("Aayush.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
msk = np.zeros_like(img_gray)
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
faces = detector(img_gray)
for face in faces:
landmarks = predictor(img_gray, face)
lp = []
for n in range(0,68):
x = landmarks.part(n).x
y = landmarks.part(n).y
lp.append((x,y))
p = np.array(lp, np.int32)
#cv2.circle(img, (x,y), 3, (0, 0, 255), -1)
convexhull = cv2.convexHull(p)
#cv2.polylines(img, [convexhull], True, (255,0,0), 3)
cv2.fillConvexPoly(msk, convexhull, 255)
img1 = cv2.bitwise_and(img, img, mask = msk)
img1 containsa complete black image with face cut from img, I just require the pixel values of face portion and not complete image
As original image and mask have not been provided in the question itself. I am assuming a simple input image and a mask image with circular cavity as:
The mask here is a single channel matrix with a value of 255 in the central cavity. To get the pixel info inside the cavity only you can use following numpy operation:
pixel_info = original_image[mask == 255]
# You may need to convert the numpy array to Python list.
pixel_info_list = pixel_info.tolist()
Now you may serialize the list to any format you want (csv in this case.)
Full code:
import cv2
import numpy as np
original_image = cv2.imread("/path/to/lena.png")
mask = np.zeros(original_image.shape[:2], dtype=original_image.dtype)
mask = cv2.circle(mask, (256, 256), 100, [255], -1)
pixel_info = original_image[mask == 255]
pixel_info_list = pixel_info.tolist()

How can i count segments in an image in python?

I am new to image processing and python. You might've seen my amateur codes on this site in the last couple of days.
I am trying to count the number of trees using aerial images. This is my code:
from PIL import Image
import cv2
import numpy as np
from skimage import io, filters, measure
from scipy import ndimage
img = Image.open("D:\\Texture analysis\\K-2.jpg")
row, col = img.size
hsvimg = img.convert('HSV')
hsvimg.mode = 'RGB'
hsvimg.save('newImage2.jpg')
npHSI = np.asarray(hsvimg) #Convert HSI Image to np image
blur = cv2.GaussianBlur(npHSI, (45, 45), 5)
assert isinstance(blur, np.ndarray) ##############################
assert len(blur.shape) == 3 #Convert np Image to HSI Image
assert blur.shape[2] == 3 ##############################
hsiBlur = Image.fromarray(blur, 'RGB')
hsiBlur.save('hsiBlur.jpg') #Save the blurred image
## Read
img = cv2.imread("D:\\Texture analysis\\hsiBlur.jpg")
## convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
#Threshold the image and segment the trees
mask = cv2.inRange(hsv, (36, 25, 25), (70, 255,255))
imask = mask>0
green = np.zeros_like(img, np.uint8)
green[imask] = img[imask]
## save
cv2.imwrite("green.png", green)
#Count the number of trees
im = io.imread('green.png', as_grey=True)
val = filters.threshold_otsu(im)
drops = ndimage.binary_fill_holes(im < val)
labels = measure.label(drops)
print(labels.max())
Original image:
HSI image with gaussian filter:
Segmented image:
The last part of the code returns 7, which is a wrong output. The value should be above 50. How can I properly count the number of green segments in the final segmented image?
EDIT
I converted green.png to binary and applied erosion with a 3x3 filter and iterated it 7 times to remove the noise.
This is what I did at the end. I followed this stackoverflow link
##save
cv2.imwrite("green.png", green)
#Convert to grayscale
gray = np.dot(green[...,:3], [0.299, 0.587, 0.114])
cv2.imwrite("grayScale.jpg", gray)
#Binarize the grayscale image
ret,bin_img = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
cv2.imwrite("bin_img.jpg", bin_img)
#Erosion to remove the noise
kernel = np.ones((3, 3),np.uint8)
erosion = cv2.erode(gray, kernel, iterations = 7)
cv2.imwrite("erosion.jpg", erosion)
#Count the number of trees
finalImage = cv2.imread('erosion.jpg')
finalImage = cv2.cvtColor(finalImage, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(finalImage, 127, 255, 1)
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
cv2.drawContours(finalImage,[cnt],0,(0,0,255),1)
Saurav mentioned in his answer ... size of "contours" will give you the count. This print(contour.size())gives an error and print(contour) just prints a long 2D array. How can i get the size of contour?
PS. I didn't upload the grayscale, binary and eroded image because i felt that the images were already taking too much space, I can still upload them if anyone wants to.
I've found 52 trees with that script:
from PIL import Image, ImageDraw, ImageFont
image = Image.open('04uX3.jpg')
pixels = image.load()
size = image.size
draw = ImageDraw.Draw(image)
font = ImageFont.truetype('arial', 60)
i = 1
for x in range(0, size[0], 100):
for y in range(0, size[1], 100):
if pixels[x, y][1] > 200:
draw.text((x, y), str(i), (255, 0, 0), font=font)
i += 1
image.save('result.png')
You can see that some trees weren't detected and some non-trees were detected. So this is very rough calculation:

Resources