If Statement based on SIFT Key Point Matches in Python - python

I'm in the process of developing a Flask Python application that's able to find a logo within different background images. The goal is to have a solution that states "yes, the logo is in the background", or "no, it doesn't look like the logo is in the background". I'm utilizing the SIFT package to match keypoints between the logo and the image with the logo in the background.
I want to figure out how I can write an IF statement that's able to deliver the message above depending on the keypoints. Is there anyone that can give me guidance on the first steps of doing this? I'll attach the code to this message (note that the function should have an indent there):
#app.route('/identify/<filename>')
def identify(filename):
""" After uploading the image,
show the identification of the uploaded image
"""
# TODO: Logic to load the uploaded image filename and identify other images
image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
image_url = url_for('images', filename=filename)
background_image = cv2.imread(image_path)
background_image = cv2.cvtColor(background_image, cv2.COLOR_BGR2RGB)
#still need to figure out how to load a second photo of the user's choice
#loading in the photo to find within the uploaded photo
logo = cv2.imread("chevy-open-road-logo-300.png")
logo = cv2.cvtColor(logo, cv2.COLOR_BGR2RGB)
#creating a copy and implementing a SIFT Image Matching method
background_image_copy = background_image.copy()
#sift
sift = cv2.xfeatures2d.SIFT_create()
background_keypoints, background_descriptors = sift.detectAndCompute(background_image_copy, None)
logo_keypoints, logo_descriptors = sift.detectAndCompute(logo, None)
#feature matching
bf = cv2.BFMatcher(cv2.NORM_L1, crossCheck=True)
matches = bf.match(background_descriptors,logo_descriptors)
matches = sorted(matches, key = lambda x:x.distance)
#img = BytesIO()
image_match = cv2.drawMatches(background_image_copy, background_keypoints, logo, logo_keypoints, matches[:45], logo, flags=2)
plt.imshow(image_match), plt.show()
'''
#Converting the plot to PNG image
plt.savefig(img, format='png', bbox_inches='tight')
img.seek(0)
plot_url = base64.b64encode(img.getvalue()).decode()
'''
'''
#Printing the number of keypoints detected in the training image
x = str(len(background_keypoints))
#Printing the number of keypoints detected in the logo image
y = str((len(logo_keypoints)))
# Printing total number of matching points between the training and logo images
z = str((len(matches)))
'''
return render_template('identify.html',
image = image_url#, plot_url=plot_url
#x=x, y=y, z=z
)

Related

How do I recognise an image on webpage using Python?

I've created a simple Python application that uses the CV2 computer vision library to recognise a template image on a webpage.
I give the application a template image that it needs to recognise on the source image. In this case, the source image is a screenshot of the website www.google.com and the template image is the Google search button.
Template image
I thought the application worked at first, but it's drawing the rectangle completely in the wrong place on the input (source) image. I've added a picture below of where the application located the template image.
Result
Here's the source code.
Main Application Source
import cv2
import numpy
from io import BytesIO
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
class Automate:
def __init__(self):
chrome_options = Options()
chrome_options.add_argument("kiosk")
self.driver = webdriver.Chrome(ChromeDriverManager("93.0.4577.63").install(), options=chrome_options)
#self.driver = webdriver.Chrome(executable_path='./chromedriver',options=chrome_options)
self.screenShot = None
self.finalImage = None
def open_webpage(self, url):
print(f"Open webpage {url}")
self.driver.get(url)
def close_webpage(self):
Event().wait(5)
self.driver.close()
print("Closing webpage")
def snap_screen(self):
print("Capturing screen")
self.screenShot = "screenshot.png"
self.driver.save_screenshot(self.screenShot)
print("done.")
def match(self, image, template):
# convert images to greyscale.
src = cv2.cvtColor(cv2.imread(image), cv2.COLOR_BGR2GRAY)
temp = cv2.cvtColor(cv2.imread(template), cv2.COLOR_BGR2GRAY)
cv2.imshow("out", temp)
cv2.waitKey(0)
height, width = src.shape
H, W = temp.shape
result = cv2.matchTemplate(src, temp, cv2.cv2.TM_CCOEFF_NORMED)
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(result)
location = maxLoc
bottomRight = (location[0] + W, location[1] + H)
src2 = cv2.imread(image)
cv2.rectangle(src2, location, bottomRight, (0, 0, 255), 5)
cv2.imshow("output", src2)
cv2.waitKey(0)
cv2.destroyAllWindows()
def main():
url = "http://www.google.com"
auto = Automate()
auto.open_webpage(url)
auto.snap_screen()
auto.close_webpage()
match_image = "images/templates/google-button.png"
# Match screenshot with template image.
auto.check_match(
image=auto.screenShot,
template=match_image
)
I'd appreciate any help or advice on how to solve this issue.
Update
Following the advice given by user zteffi, I resized my template image to the correct image dimensions. After doing this, the match template function works as expected.
You want to make sure that your template image is a close as possible to the actual size of the image you want to be located in the base image. In my case, this was around 150 x 150 or 200 x 200 so that it will be easier to find the button.

PIL doesn't paste properly

I'm trying to make a little weather forecast program which gives you an image with an overview of the weather in python
To get the weather, I'm using openweathermap and it works ok for me, but for the image I'm using PIL to paste the weather icon, but for some reason there's a part of it that's not being pasted, here you can see what the icon should be: https://openweathermap.org/img/wn/04n#2x.png, and here's how it appeared in the image that came out of my script:
Here's the part of the code that generates the image:
def drawImage(d):
img=PIL.Image.open("base.png")
url=f"https://openweathermap.org/img/wn/{d['icon']}#2x.png"
weatherIcon=Image.open(requests.get(url, stream=True).raw)
print(url)
img.paste(weatherIcon, (00, 10))
now=datetime.now()
name="boards/"+now.strftime('%d_%m_%Y_%H_%M_%S')+".png"
img.save(name)
return name
Some notes on this code:
The base.png is just a 720x720 blank image
The d that gets passed in is a dictionary with all the information, but here it only needs the icon, so I'll give this example: {"icon": "04n"}
I got the URL for the image from the website of OpenWeatherMap, see documentation: https://openweathermap.org/weather-conditions
This is happening because the icon image you download has transparency (an alpha channel). To remove that, you can use this answer.
I've simplified it slightly, define the following function:
def remove_transparency(im, bg_colour=(255, 255, 255)):
if im.mode in ('RGBA', 'LA') or (im.mode == 'P' and 'transparency' in im.info):
alpha = im.getchannel('A')
bg = Image.new("RGBA", im.size, bg_colour + (255,))
bg.paste(im, mask=alpha)
return bg
else:
return im
and call it in your code:
weatherIcon=Image.open(requests.get(url, stream=True).raw)
print(url)
weatherIcon = remove_transparency(weatherIcon)
img.paste(weatherIcon, (00, 10))
You might want to adjust that bg_colour parameter.

Converting a class function to iterate through multiple images with OpenCV and Python

I'm using OpenCV to detect images in real-time. This is working with single images. I'm trying to update the script to work with multiple images at once with the end goal to be to detect a few different elements on the screen at once and draw rectangles around them while returning the centre points of the images for use elsewhere.
I've gotten as far as passing the images in from my main.py file and initiating the search.
# load source images
source_images = glob.glob(r'C:\\\\\\\*.jpg')
# perform the search
search = Search(source_images)
I can't seem to pass loaded_images into my find function. I've tried defining loaded_images in the Class properties and within the constructor but neither work and give me the error NameError: name 'loaded_images' is not defined
class Search:
# properties
needle_img = None
needle_w = 0
needle_h = 0
method = None
# empty list to store the source images
#loaded_images = [] <-------------------------------------------------
# constructor
def __init__(self, source_images, method=cv.TM_CCOEFF_NORMED):
# Set the method we're using when we load the image
self.method = method
# empty list to store the source images
# loaded_images = [] <-------------------------------------------------
# load the needle image into loaded_iamges array
for img in source_images:
self.needle_img = cv.imread(img, cv.IMREAD_UNCHANGED)
loaded_images.append(self.needle_img)
return self.loaded_images <--------------------------------
def find(self, haystack_img, threshold=0.5, debug_mode=None):
for img in loaded_images:
# Save the dimensions of the needle images
self.needle_w = self.needle_img.shape[1]
self.needle_h = self.needle_img.shape[0]
# run the OpenCV algorithm
result = cv.matchTemplate(haystack_img, self.needle_img, self.method)
I also wrote this next code segment that matches multiple images on a screenshot rather than a real-time image to try and figure out what I was doing wrong. It's helped me get this far but I am now stuck.
# load source images
source_images = glob.glob(r'C:\\\\\\\*.jpg')
# empty list to store the source images
loaded_images = []
for img in source_images:
needle_img = cv.imread(img, 0)
loaded_images.append(needle_img)
haystack_img = cv.imread(r'C:\\\\\\both.jpg')
haystack_img = cv.cvtColor(haystack_img, cv.COLOR_BGR2GRAY)
#loop for matching
for needles in loaded_images:
#save the dimensions of the needle images
(tH, tW) = needles.shape[:2]
result = cv.matchTemplate(haystack_img, needles, cv.TM_CCOEFF)
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
top_left = max_loc
bottom_right = (top_left[0] + tW, top_left[1] + tH)
cv.rectangle(haystack_img, top_left, bottom_right, 255, 2)
cv.imshow('Result', haystack_img)
cv.waitKey(0)
Either pass loaded images directly to find:
class Search:
def __init__(self, source_images, method=cv.TM_CCOEFF_NORMED):
# Set the method we're using when we load the image
self.method = method
def find(self, haystack_img, loaded_images, threshold=0.5, debug_mode=None):
for img in loaded_images:
# Save the dimensions of the needle images
self.needle_w = self.needle_img.shape[1]
self.needle_h = self.needle_img.shape[0]
# you are overwriting the same needle_w and needle_h variable over and over again in a loop...
# run the OpenCV algorithm
result = cv.matchTemplate(haystack_img, self.needle_img, self.method)
Or store loaded images in field:
class Search:
def __init__(self, source_images, method=cv.TM_CCOEFF_NORMED):
# Set the method we're using when we load the image
self.method = method
self.loaded_images = []
# load the needle image into loaded_iamges array
for img in source_images:
needle_img = cv.imread(img, cv.IMREAD_UNCHANGED)
self.loaded_images.append(needle_img)
def find(self, haystack_img, threshold=0.5, debug_mode=None):
for img in self.loaded_images:
# Save the dimensions of the needle images
self.needle_w = self.needle_img.shape[1]
self.needle_h = self.needle_img.shape[0]
# you are overwriting the same needle_w and needle_h variable over and over again in a loop...
# run the OpenCV algorithm
result = cv.matchTemplate(haystack_img, self.needle_img, self.method)
In general you need to learn about scopes and difference between class and instance variables. Also I don't see here why you would need class when you would probably only have one instance of it, so maybe read about OOP in general.
You have two options in this case. Either define load_images as an instance variable with self.. But then you should call it as self.loaded_images everywhere within the class.
class Search:
self.loaded_images = []
for ...
self.loaded_images.append(...)
Or, you can define as a parameter in the function.
def find(self, loaded_images, haystack_img, threshold=0.5, debug_mode=None):
Then you can leave the definition of load_images as it is currently in __init__ method (uncomment load_images=[ ]), but be sure that you call the find function with your load_images where you had "loaded" images.
It is completely up to you how to structure your variable.

Python select ROI OpenCV

Sample Image
Hello,
I created an application in python that select the Region of Interest(ROI) of an image, record and label it. But I has a limit of one ROI per image, anyone know how to have multiple selection of ROI per image? Also on attached image, as you can see I have multiple window, I want it to be in one window with different options, what packages are use on this kind of application.
here's my code in python using opencv2. Thank you in advance for the help
for image in filelist:
img = cv2.imread(image)
fromCenter = False
r = cv2.selectROI(img, fromCenter)
lbl = simpledialog.askstring("Image Label", "Please Enter Label")
result = eTree.SubElement(results, "Image")
path = eTree.SubElement(result, 'Path')
roi = eTree.SubElement(result, 'ROI')
label = eTree.SubElement(result, 'Label')
path.text = str(image)
roi.text = str(r)
label.text = str(lbl)
tree = eTree.ElementTree(results)
i = i + 1
if i == count:
format = [('XML Files', '*.xml'), ('All Files', '*.*')]
save = filedialog.asksaveasfilename(filetype=format, defaultextension='*.xml')
tree.write(save, xml_declaration=True, encoding='utf-8', method="xml")
Well at least for the first part of the question, have you considered to try the cv2.createROIs() instead of cv2.createROI() ? When the image window is opened you then select your first ROI and press enter, then the second and press enter etc. And when you are finished then press the escape key. It returns x,y,w,h of each ROI. Note that you will have to change your code accordingly but it will allow you to select multiple ROI.
Input image:
Example:
import cv2
img = cv2.imread('rois.png')
fromCenter = False
ROIs = cv2.selectROIs('Select ROIs', img, fromCenter)
ROI_1 = img[ROIs[0][1]:ROIs[0][1]+ROIs[0][3], ROIs[0][0]:ROIs[0][0]+ROIs[0][2]]
ROI_2 = img[ROIs[1][1]:ROIs[1][1]+ROIs[1][3], ROIs[1][0]:ROIs[1][0]+ROIs[1][2]]
ROI_3 = img[ROIs[2][1]:ROIs[2][1]+ROIs[2][3], ROIs[2][0]:ROIs[2][0]+ROIs[2][2]]
cv2.imshow('1', ROI_1)
cv2.imshow('2', ROI_2)
cv2.imshow('3', ROI_3)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
For custom ROI you can use EasyROI. It supports rectangle, line, circle and polygon.
For using it:
pip install EasyROI
from EasyROI import EasyROI
roi_helper = EasyROI()
roi = roi_helper.draw_rectangle(frame, quantity=2)

NAO fails to save captured image to local computer

I'm trying to save a captured 640x480 RGB image with NAO's front camera to my computer. I'm using python and PIL to do so. Unfortunately, the image just won't save on my computer, no matter what image type or path I use for the parameters of the Image.save()- Method. the image created with PIL contains valid RGB-information though. Here's my code sample from choregraphe:
import Image
def onInput_onStart(self):
cam_input = ALProxy("ALVideoDevice")
nameId = cam_input.subscribeCamera("Test_Cam", 1, 2, 13, 20)
image = cam_input.getImageRemote(nameId) #captures an image
w = image[0] #get the image width
h = image[1] #get the image height
pixel_array = image[6] #contains the image data
result = Image.fromstring("RGB", (w, h), pixel_array)
#the following line doesnt work
result.save("C:\Users\Claudia\Desktop\NAO\Bilder\test.png", "PNG")
cam_input.releaseImage(nameId)
cam_input.unsubscribe(nameId)
pass
Thank you so much for your help in advance!
- a frustrated student
In the comment, you say the code is pasted from choregraphe, so I guess you launch it using choregraphe.
If so, then the code is injected into your robot then started.
So your image is saved to the NAO hard drive and I guess your robot doesn't have a folder named: "C:\Users\Claudia\Desktop\NAO\Bilder\test.png".
So change the path to "/home/nao/test.png", start your code, then log into your NAO using putty or browse folder using winscp (as it looks like you're using windows).
And you should see your image-file.
In order for your code to run correctly it needs to be properly indented. Your code should look like this:
import Image
def onInput_onStart(self):
cam_input = ALProxy("ALVideoDevice")
nameId = cam_input.subscribeCamera("Test_Cam", 1, 2, 13, 20)
image = cam_input.getImageRemote(nameId) #captures an image
w = image[0] #get the image width
h = image[1] #get the image height
pixel_array = image[6] #contains the image data
...
Make sure to indent everything that's inside the def onInput_onStart(self): method.
Sorry for the late response, but it maybe helpful for someone. You should try it with naoqi. Here is the documentation for retriving images
http://doc.aldebaran.com/2-4/dev/python/examples/vision/get_image.html
The original code was not working for me so I made some tweeks.
parser = argparse.ArgumentParser()
parser.add_argument("--ip", type=str, default="nao.local.",
help="Robot IP address. On robot or Local Naoqi: use
'nao.local.'.")
parser.add_argument("--port", type=int, default=9559,
help="Naoqi port number")
args = parser.parse_args()
session = qi.Session()
try:
session.connect("tcp://" + args.ip + ":" + str(args.port))
except RuntimeError:
pass
"""
First get an image, then show it on the screen with PIL.
"""
# Get the service ALVideoDevice.
video_service = session.service("ALVideoDevice")
resolution = 2 # VGA
colorSpace = 11 # RGB
videoClient = video_service.subscribe("python_client",0,3,13,1)
t0 = time.time()
# Get a camera image.
# image[6] contains the image data passed as an array of ASCII chars.
naoImage = video_service.getImageRemote(videoClient)
t1 = time.time()
# Time the image transfer.
print ("acquisition delay ", t1 - t0)
#video_service.unsubscribe(videoClient)
# Now we work with the image returned and save it as a PNG using ImageDraw
# package.
# Get the image size and pixel array.
imageWidth = naoImage[0]
imageHeight = naoImage[1]
array = naoImage[6]
image_string = str(bytearray(array))
# Create a PIL Image from our pixel array.
im = Image.fromstring("RGB", (imageWidth, imageHeight), image_string)
# Save the image.
im.save("C:\\Users\\Lenovo\\Desktop\\PROJEKTI\\python2-
connect4\\camImage.png", "PNG")
Be careful to use Python 2.7.
The code runs on your computer not the NAO robot!

Categories