Opencv: detect mouse position clicking over a picture - python-2.7

I have this code in which I simply display an image using OpenCV:
import numpy as np
import cv2
class LoadImage:
def loadImage(self):
self.img=cv2.imread('photo.png')
cv2.imshow('Test',self.img)
self.pressedkey=cv2.waitKey(0)
# Wait for ESC key to exit
if self.pressedkey==27:
cv2.destroyAllWindows()
# Start of the main program here
if __name__=="__main__":
LI=LoadImage()
LI.loadImage()
Once the window displayed with the photo in it, I want to display on the console (terminal) the position of the mouse when I click over the picture. I have no idea how to perform this. Any help please?

Here is an example mouse callback function, that captures the left button double-click
def draw_circle(event,x,y,flags,param):
global mouseX,mouseY
if event == cv2.EVENT_LBUTTONDBLCLK:
cv2.circle(img,(x,y),100,(255,0,0),-1)
mouseX,mouseY = x,y
You then need to bind that function to a window that will capture the mouse click
img = np.zeros((512,512,3), np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw_circle)
then, in a infinite processing loop (or whatever you want)
while(1):
cv2.imshow('image',img)
k = cv2.waitKey(20) & 0xFF
if k == 27:
break
elif k == ord('a'):
print mouseX,mouseY
What Does This Code Do?
It stores the mouse position in global variables mouseX & mouseY every time you double click inside the black window and press the a key that will be created.
elif k == ord('a'):
print mouseX,mouseY
will print the current stored mouse click location every time you press the a button.
Code "Borrowed" from here.

Below is my implementation:
No need to store the click position, ONLY display it:
def onMouse(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
# draw circle here (etc...)
print('x = %d, y = %d'%(x, y))
cv2.setMouseCallback('WindowName', onMouse)
If you want to use the positions in other places of your code, you can use below way to obtain the coordinates:
posList = []
def onMouse(event, x, y, flags, param):
global posList
if event == cv2.EVENT_LBUTTONDOWN:
posList.append((x, y))
cv2.setMouseCallback('WindowName', onMouse)
posNp = np.array(posList) # convert to NumPy for later use

import cv2
cv2.imshow("image", img)
cv2.namedWindow('image')
cv2.setMouseCallback('image', on_click)
def on_click(event, x, y, p1, p2):
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(lastImage, (x, y), 3, (255, 0, 0), -1)

You can detect mouse position clicking over a picture via performing the various mouse click events.
You just to remember one thing while performing the mouse clicks events is that, you should have to use the same window name at all places wherever you are using the cv2.imshow or cv2.namedWindow
I given the working code in answer that uses python 3.x and opencv in the following the stackoverflow post:
https://stackoverflow.com/a/60445099/11493115
You can refer the above link for better explanation.
Code:
import cv2
import numpy as np
#This will display all the available mouse click events
events = [i for i in dir(cv2) if 'EVENT' in i]
print(events)
#This variable we use to store the pixel location
refPt = []
#click event function
def click_event(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
print(x,",",y)
refPt.append([x,y])
font = cv2.FONT_HERSHEY_SIMPLEX
strXY = str(x)+", "+str(y)
cv2.putText(img, strXY, (x,y), font, 0.5, (255,255,0), 2)
cv2.imshow("image", img)
if event == cv2.EVENT_RBUTTONDOWN:
blue = img[y, x, 0]
green = img[y, x, 1]
red = img[y, x, 2]
font = cv2.FONT_HERSHEY_SIMPLEX
strBGR = str(blue)+", "+str(green)+","+str(red)
cv2.putText(img, strBGR, (x,y), font, 0.5, (0,255,255), 2)
cv2.imshow("image", img)
#Here, you need to change the image name and it's path according to your directory
img = cv2.imread("D:/pictures/abc.jpg")
cv2.imshow("image", img)
#calling the mouse click event
cv2.setMouseCallback("image", click_event)
cv2.waitKey(0)
cv2.destroyAllWindows()

Here is class based implementation of OpenCV mouse call back for getting point on an image,
import cv2
import numpy as np
#events = [i for i in dir(cv2) if 'EVENT' in i]
#print (events)
class MousePts:
def __init__(self,windowname,img):
self.windowname = windowname
self.img1 = img.copy()
self.img = self.img1.copy()
cv2.namedWindow(windowname,cv2.WINDOW_NORMAL)
cv2.imshow(windowname,img)
self.curr_pt = []
self.point = []
def select_point(self,event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDOWN:
self.point.append([x,y])
#print(self.point)
cv2.circle(self.img,(x,y),5,(0,255,0),-1)
elif event == cv2.EVENT_MOUSEMOVE:
self.curr_pt = [x,y]
#print(self.point)
def getpt(self,count=1,img=None):
if img is not None:
self.img = img
else:
self.img = self.img1.copy()
cv2.namedWindow(self.windowname,cv2.WINDOW_NORMAL)
cv2.imshow(self.windowname,self.img)
cv2.setMouseCallback(self.windowname,self.select_point)
self.point = []
while(1):
cv2.imshow(self.windowname,self.img)
k = cv2.waitKey(20) & 0xFF
if k == 27 or len(self.point)>=count:
break
#print(self.point)
cv2.setMouseCallback(self.windowname, lambda *args : None)
#cv2.destroyAllWindows()
return self.point, self.img
if __name__=='__main__':
img = np.zeros((512,512,3), np.uint8)
windowname = 'image'
coordinateStore = MousePts(windowname,img)
pts,img = coordinateStore.getpt(3)
print(pts)
pts,img = coordinateStore.getpt(3,img)
print(pts)
cv2.imshow(windowname,img)
cv2.waitKey(0)

I have ported the PyIgnition library from Pygame to opencv2. Find the code at https://github.com/bunkahle/particlescv2
There also several examples on how to use the particle engine for Python.

In case, you want to get the coordinates by hovering over the image in Python 3, you could try this:
import numpy as np
import cv2 as cv
import os
import sys
# Reduce the size of image by this number to show completely in screen
descalingFactor = 2
# mouse callback function, which will print the coordinates in console
def print_coord(event,x,y,flags,param):
if event == cv.EVENT_MOUSEMOVE:
print(f'{x*descalingFactor, y*descalingFactor}\r', end="")
img = cv.imread(cv.samples.findFile('TestImage.png'))
imgheight, imgwidth = img.shape[:2]
resizedImg = cv.resize(img,(int(imgwidth/descalingFactor), int(imgheight/descalingFactor)), interpolation = cv.INTER_AREA)
cv.namedWindow('Get Coordinates')
cv.setMouseCallback('Get Coordinates',print_coord)
cv.imshow('Get Coordinates',resizedImg)
cv.waitKey(0)

If anyone wants a multi-process-based GUI for drawing points and dragging to move them, here is a single file script for same.

Related

Mouse click results in unexpected behavior using wxPython & wx.Panel

I am writing an application in which the following steps are intended to be executed:
1 - user clicks in blue area (a wx.Panel) and a white circle appears;
2 - user clicks Select Start button;
3 - user clicks in the white circle and it changes to green (by drawing a green circle over the white one)
The problem is that when step 3 is executed the green circle does not appear where the mouse is clicked. For example, clicking in the blue area registers a point at (223, 486). After clicking the Select Start button and then clicking in the white circle a point registers at (211, 464), and a green circle appears outside the white circle. I don’t understand why this is happening and would appreciate any help in resolving the problem.
Using python 2.7 with wxPython 3.0.3.0 on MacOs
import wx
class Test(wx.Frame):
def __init__(self, parent, title):
super(Test, self).__init__(parent, title = title, size = (820,900))
self.startSelected = False
self.radius = 10
self.panel = wx.Panel(self, size=(800,800))
self.panel.SetBackgroundColour(wx.Colour(0,0,200))
self.panel.Bind(wx.EVT_LEFT_DOWN, self.onMouseDown)
self.gbs = wx.GridBagSizer(0,0)
self.startBtn = wx.Button(self, wx.ID_ANY, label = 'Select Start')
self.startBtn.Bind(wx.EVT_BUTTON, self.selectStart)
self.gbs.Add(self.panel, span = (1,4), pos=(1,1),flag=wx.EXPAND | wx.ALL)
self.gbs.Add(self.startBtn, pos=(2,1))
self.SetSizer(self.gbs)
def onMouseDown(self, e):
pt = e.GetPosition()
print pt.x, pt.y
if e.LeftDown():
if self.startSelected:
color = wx.Colour(0,255,0)
self.paint(pt, color)
else:
color = wx.Colour(255,255,255)
self.paint(pt, color)
def paint(self, pt, color):
dc = wx.ClientDC(self)
b = wx.Brush(color)
dc.SetBrush(b)
dc.DrawCircle(pt.x, pt.y, self.radius)
def selectStart(self, e):
self.startSelected = True
if __name__ == '__main__':
app = wx.App()
Test(None, 'Test')
app.MainLoop()
The wx.ClientDC is relative to the object passed to it. As coded in the paint method, dc = wx.ClientDC(self), self is the wx.Frame. Since drawing is done on the wx.Panel, dc = wx.ClientDC(self.panel) should be used. This results in the expected behavior.

Each blitted image in list disappear instead of remaining on screen. I wanted to fill up the whole screen with image as it blits on random locations

import pygame
import random
pygame.init()
screen = pygame.display.set_mode((476,800))
clock = pygame.time.Clock()
win = pygame.display.set_mode((500, 600))
pygame.display.set_caption("DOGGO GAME")
dogimg = pygame.image.load('dog.png')
*doglist will contain the image rects so that multiple images/entities will fill the screen
doglist = []
dogx = []
dogy = []
blot triggers the creation of element(dog image rect = dogimg)
BLOT = pygame.USEREVENT
pygame.time.set_timer(BLOT, 500)
drawdog blits the images in the doglist
def drawdog(doglist):
for dog in doglist:
screen.blit(dogimg, new_dog)
run = True
while run:
#pygame.time.delay(50)
win.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == BLOT:
dogimg = pygame.image.load('dog.png')
dogranx = random.randint(50,200)
dograny = random.randint(50,450)
new_dog = dogimg.get_rect(midtop=(dogranx, dograny))
doglist.append(new_dog)
print(doglist)
drawdog(doglist)
pygame.display.update()
clock.tick(1)
pygame.quit()
I was able to correct it by replacing:
for dog in doglist
with
for new_dog in doglist
In the former, I was using extend. I changed it to append and used the latter code, and it worked.

how can I select object from image and replace it with other one by using c++ and opencv

enter image description here
for example if i need to select the Ipad and I need to replace it by other thing with the same size
Let's say, we want to put an Android tablet from another image over the iPad.
Knowing corner coordinates of both objects on both images, you can use OpenCV getPerspectiveTransform function to create the transformation matrix. Make an empty mask, use fillPoly to draw a quadrangle on it, corresponding to the Android corner points, fill it with 1-s, it's going to be the binary mask. Apply the perspective transform, calculated earlier, to both the mask and the android image (warpPerspective). Copy the transformed Android image over the original iPad image with copyTo function, using the transformed mask. Done.
Here's a 'proof of concept' Python implementation, just because I did something not so different not so long ago. Click all the Android corners in order, then the iPad corners in the same order, press A key to apply the transform. Don't expect miracles from it, of course - it's not going to paint missing edges for you, etc.
import cv2
import numpy as np
def on_mouse_click_from(event, x, y, flags, param):
global image_from, points_from
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(image_from, (x, y), 2, (255, 0, 0), cv2.FILLED)
points_from.append([x, y])
cv2.imshow("Image1", image_from)
def on_mouse_click_to(event, x, y, flags, param):
global image_to, points_to
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(image_to, (x, y), 2, (255, 0, 0), cv2.FILLED)
points_to.append([x, y])
cv2.imshow("Image2", image_to)
points_from = []
points_to = []
image_from = cv2.imread("android.jpg")
image_to = cv2.imread("ipad.jpg")
max_dim = [max(x, y) for x, y in zip(image_from.shape[:2], image_to.shape[:2])][::-1]
max_dim = tuple(max_dim)
image_from = cv2.resize(image_from, max_dim)
image_to = cv2.resize(image_to, max_dim)
clone_from = image_from.copy()
cv2.namedWindow("Image1")
cv2.setMouseCallback("Image1", on_mouse_click_from)
clone_to = image_to.copy()
cv2.namedWindow("Image2")
cv2.setMouseCallback("Image2", on_mouse_click_to)
image_res = None
cv2.namedWindow("Result")
while True:
cv2.imshow("Image1", image_from)
cv2.imshow("Image2", image_to)
key = cv2.waitKey(1) & 0xFF
if key == ord("r"):
image_from = clone_from.copy()
image_to = clone_to.copy()
points_from = []
points_to = []
elif key == ord("a"):
trans = cv2.getPerspectiveTransform(np.array(points_from, dtype='f4'), np.array(points_to, dtype='f4'))
height, width, n_colors = clone_from.shape
stencil = np.zeros((height, width, n_colors))
contours = [np.array(points_from)]
color = [1, 1, 1]
cv2.fillPoly(stencil, contours, color)
stencil = cv2.warpPerspective(stencil, trans, (width, height))
img_from_transformed = cv2.warpPerspective(clone_from, trans, (width, height))
cnd = (stencil != 0)
image_res = clone_to.copy()
image_res[cnd] = img_from_transformed[cnd]
cv2.imwrite("result.jpg", image_res)
cv2.imshow("Result", image_res)
elif key == ord("q"):
break
cv2.destroyAllWindows()
Follow these steps:
1) First, select the object to be replaced using Mousecallback fuction from opencv. You can find a simple demo here
2) When selecting the object, save the 4 coordinates of the object being selected. Find the size of the rectangle by using these coordinates.
3) Use Resize function to resize the image(with which you want to replace the original)
4) Now You can simply use copyTo function to do the replacement. Check here
Hope this helps!

Pygame - rendering multiline text using Sprites

Struggle is that I am capable of rendering one line of text with Sprites in Pygame, I am also capable of rendering multi-line text, but without the Sprites, but I can't figure out way how to render multi-line text using Sprites.
I've got this for rendering one line text using Sprites (I am going to skip the basic for running Pygame code - background, init etc.):
class Score(pygame.sprite.Sprite):
.
.
def update(self, lives, score):
.
.
self.text = "LIVES: %d SCORE: %d" % (lives, score)
self.image = self.font.render(self.text, True, self.color)
self.rect = self.image.get_rect()
.
.
.
.
def main():
.
.
score = pygame.sprite.RenderUpdates()
score.add(Score())
.
.
while 1:
.
.
score.clear(screen, background)
score_list = score.draw(screen)
pygame.display.update(score_list)
score.update(lives, score)
.
.
I just want to know if it's even possible to do it using my render method or if I should focus on doing it some other way?
And maybe one related question; Is this method the right way to render objects (images) in Pygame?
Thank you for any help.
You could render the strings with FONT.render first, then create a transparent surface and blit the separate text surfaces onto it. To figure out the width of the transparent base image, you can use the .get_width() method of the separate text surfaces and for the height you can use FONT.get_height().
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
FONT = pg.font.Font(None, 40)
BLUE = pg.Color('dodgerblue1')
class Score(pg.sprite.Sprite):
def __init__(self, pos):
super(Score, self).__init__()
self.lives = 3
self.score = 0
self.rect = pg.Rect(pos, (1, 1))
self.update_image()
def update_image(self):
height = FONT.get_height()
# Put the rendered text surfaces into this list.
text_surfaces = []
for txt in ('lives {}'.format(self.lives),
'score {}'.format(self.score)):
text_surfaces.append(FONT.render(txt, True, BLUE))
# The width of the widest surface.
width = max(txt_surf.get_width() for txt_surf in text_surfaces)
# A transparent surface onto which we blit the text surfaces.
self.image = pg.Surface((width, height*2), pg.SRCALPHA)
for y, txt_surf in enumerate(text_surfaces):
self.image.blit(txt_surf, (0, y*height))
# Get a new rect (if you maybe want to make the text clickable).
self.rect = self.image.get_rect(topleft=self.rect.topleft)
def main():
score = Score((100, 100))
all_sprites = pg.sprite.Group(score)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_s:
# Increment the score and update the image.
# It would be nicer to turn the score into a
# property and update the image automatically.
score.score += 1
score.update_image()
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()

one entrance for loop in threading

I am using pyqt4 , python2.7. opencv3 , raspbian
I create Function with name Vision_thread()to search faces in every frame. and create funcation with name start_thread() to execute Vision_thread()Function by thread. look here:
def start_thread():
t1 = threading.Thread(target=Vision_thread)
t1.start()
#t1.join()
now I call Funcation by button created by PYqt4 as way:
self.pushButtonStart.setText(_translate("MainWindow", "Start Vision", None))
# program event click for start Vision button
self.pushButtonStart.clicked.connect(start_thread)
my problem is : when click on button . the loop in function Vision_thread() execute for first entrance only . and do not continuous .
but when I write:
t1 = threading.Thread(target=Vision_thread())
insted of
t1 = threading.Thread(target=Vision_thread)
the function Vision_thread() execute without any problem . but this is no thread .
what is problem??????
Vision_thread() code is :
def Vision_thread():
print("Vision")
print (search)
GPIO.output(32,True)
for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):
image = frame.array
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
cv2.rectangle(image,(x,y),(x+w,y+h),(255,0,0),2)
face = gray[y:y + h, x:x + w]
face_resize = cv2.resize(face, (width, height))
#Try to recognize the face
prediction = model.predict(face_resize)
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 3)
x_left=160
if prediction[1]<500:
cv2.putText(image,'%s - %.0f' % (names[prediction[0]],prediction[1]),(x-10, y-10), cv2.FONT_HERSHEY_PLAIN,1,(0, 255, 0))
if names[prediction[0]]==search:
x_left = x-width
x_right = width - w - x
sum_final=x_left+x_right
if x_left>180:
Left_thread()
if x_left<140:
Right_thread()
print "left={0}\nright={1}\nsum={2}".format(x_left, x_right,sum_final)
else:
cv2.putText(image,'not recognized',(x-10, y-10), cv2.FONT_HERSHEY_PLAIN,1,(0, 255, 0))
cv2.imshow('OpenCV', image)
key = cv2.waitKey(10)
rawCapture.truncate(0)
if key == 27:
GPIO.output(32,False)
break