Mouse.position from pynput not working [python2, opencv, mac, jupyter] - opencv

I'm new to this so it may just be a syntax problem, but can anyone figure out why line 77 mouse.position = (x,y) isn't moving my mouse? It should be mapped to a dot drawn by holding a green object up to my webcam.
Furthermore, when I introduce the while mouse.position!=(x,y): pass the camera freezes when a green object is introduced.
Code here (no errors shown when freezing):
import cv2
import numpy as np
from pynput.mouse import Button, Controller
import wx
mouse=Controller()
#get monitor size
app=wx.App(False)
(sx,sy)=wx.GetDisplaySize()
print sx, sy
#output window size
(camx,camy)=(320,240)
#set filter limits
lowerBound=np.array([33,80,40])
upperBound=np.array([102,255,255])
#initialise cam
cam= cv2.VideoCapture(0)
cam.set(3,camx)
cam.set(4,camy)
#mask parameters
kernelOpen=np.ones((5,5))
kernelClose=np.ones((20,20))
while True:
ret, img=cam.read()
img=cv2.resize(img,(340,220))
#convert BGR to HSV
imgHSV= cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
#create the Mask
mask=cv2.inRange(imgHSV,lowerBound,upperBound)
#morphology
maskOpen=cv2.morphologyEx(mask,cv2.MORPH_OPEN,kernelOpen)
maskClose=cv2.morphologyEx(maskOpen,cv2.MORPH_CLOSE,kernelClose)
maskFinal=maskClose
#Find contours
conts,h=cv2.findContours(maskFinal.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
#For two contours (open):
if(len(conts)==2):
x1,y1,w1,h1=cv2.boundingRect(conts[0])
x2,y2,w2,h2=cv2.boundingRect(conts[1])
cv2.rectangle(img,(x1,y1),(x1+w1,y1+h1),(255,0,0),2)
cv2.rectangle(img,(x2,y2),(x2+w2,y2+h2),(255,0,0),2)
#finding points in middle of rectangles
cx1=x1+w1/2
cy1=y1+h1/2
cx2=x2+w2/2
cy2=y2+h2/2
#find centre of line
cx=(cx1+cx2)/2
cy=(cy1+cy2)/2
#create line between centres of contours
cv2.line(img,(cx1,cy1),(cx2,cy2),(255,0,0),2)
#create dot in middle of line
cv2.circle(img, (cx,cy),2,(0,0,255),2)
#to check values
print (cx*sx/camx)
print (cy*sy/camy)
#move mouse to dot cx,cy (scaled for monitor)
mouse.position = (cx*sx/camx,cy*sy/camy)
while mouse.position != (cx*sx/camx,cy*sy/camy):
pass
#For one contour (closed):
elif (len(conts)==1):
x,y,w,h= cv2.boundingRect(conts[0])
#draw single rectangle around 'clicked' object
cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
#show when clicked with large circle
cx=x+w/2
cy=y+h/2
cv2.circle(img,(cx,cy),(w+h)/4,(0,0,255),2)
#set mouse to dot cx,cy
mouse.position = (cx*sx/camx,cy*sy/camy)
while mouse.position != (cx*sx/camx,cy*sy/camy):
pass
#cv2.imshow("maskClose",maskClose)
#cv2.imshow("mask",mask)
cv2.imshow("cam",img)
cv2.waitKey(5)
I appreciate any help!

My problem was that I needed to allow permission for terminal and jupyter to access mouse control (Mac Mojave 10.14.2).
The reason for the freeze is that it was stuck in the while loop - but with the mouse.position now working its all good!

Related

How can I convert bounding box pixels of an image to white, and the background to black?

I have a set of images similar to this one:
And for each image, I have a text file with bounding box regions expressed in normalized pixel values, YOLOv5 format (a text document with rows of type: class, x_center, y_center, width, height). Here's an example:
3 0.1661542727623449 0.6696164480452673 0.2951388888888889 0.300925925925926
3 0.41214353459362196 0.851908114711934 0.2719907407407405 0.2961837705761321
I'd like to obtain a new dataset of masked images, where the bounding box area from the original image gets converted into white pixels, and the rest gets converted into black pixels. This would be and example of the output image:
I'm sure there is a way to do this in PIL (Pillow) in Python, but I just can't seem to find a way.
Would somebody be able to help?
Kindest thanks!
so here's the answer:
import os
import numpy as np
from PIL import Image
label=open(os.path.join(labPath, filename), 'r')
lines=label.read().split('\n')
square=np.zeros((1152,1152))
for line in lines:
if line!='':
line=line.split() #line: class, x, y, w, h
left=int((float(line[1])-0.5*float(line[3]))*1152 )
bot=int((float(line[2])+0.5*float(line[4]))*1152)
top=int(bot-float(line[4])*1152)
right=int(left+float(line[3])*1152)
square[top:bot, left:right]=255
square_img = Image.fromarray(square)
square_img=square_img.convert("L")
Let me know if you have any questions!

OpenCV - Circle Detection Too Sensitive Even with Blur

Hi, just posting this on behalf of my 10yo son. He's working on a Python/OpenCV/GUI application and having some issues. Hoping someone might be able to point him in the right direction. Information as per below (maybe he needs to take a different approach?)
At the moment in my project I am having a problem with no errors. The only problem is the code isn't doing exactly what I want it to be. I can not tell if the blur is too strong, the blur is making the circle detection more sensitive or something else. My code is below.
I am trying to make the circle detection less sensitive by using a blur, however I can not tell what it's doing because there is no error.
What I want it to do is:
blur the image
ensure the circle detection is not to sensitive (not too many circles)
show the image unblurred and on the unblurred image show the circles from the blurred image
For an example, I should be able to detect moon craters.
import tkinter as tk
from tkinter import filedialog
from PIL import ImageTk, Image
import numpy as np
import cv2
root = tk.Tk()
root.title("Circle detecter")
root.geometry("1100x600")
root.iconbitmap('C:/Users/brett/')
def open():
global my_image
filename = filedialog.askopenfilename(initialdir="images", title="Select A File", filetypes=(("jpg files", "*.jpg"),("all files", "*.*")))
my_label.config(text=filename)
my_image = Image.open(filename)
tkimg = ImageTk.PhotoImage(my_image)
my_image_label.config(image=tkimg)
my_image_label.image = tkimg # save a reference of the image
def find_circles():
# convert PIL image to OpenCV image
circles_image = np.array(my_image.convert('RGB'))
gray_img = cv2.cvtColor(circles_image, cv2.COLOR_BGR2GRAY)
img = cv2.medianBlur(gray_img, 5)
circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 20,
param1=20, param2=60, minRadius=20, maxRadius=200)
if circles is not None:
circles = np.uint16(np.around(circles))
for i in circles[0]:
# draw the outer circle
cv2.circle(circles_image, (i[0],i[1]), i[2], (0,255,0), 2)
# draw the center of the circle
cv2.circle(circles_image, (i[0],i[1]), 2, (0,0,255), 3)
# convert OpenCV image back to PIL image
image = Image.fromarray(circles_image)
# update shown image
my_image_label.image.paste(image)
tk.Button(root, text="Load Image", command=open).pack()
tk.Button(root, text="Find circles", command=find_circles).pack()
# for the filename of selected image
my_label = tk.Label(root)
my_label.pack()
# for showing the selected image
my_image_label = tk.Label(root)
my_image_label.pack()
root.mainloop()

Detect handwritten characters in boxes from a filled form using Fourier transforms

I am trying to extract handwritten characters from boxes. The scanning of the forms is not consistent, so the width and height of the boxes are also not constants.
Here is a part of the form.
My current approach:
1. Extract horizontal lines
2. Extract vertical lines
3. Combine both the above images
4. Find contours ( used opencv)
This approach gives me most of the boxes. But, when the box is filled with characters like "L" or "I", the vertical line in the character is also getting extracted as a part of vertical lines extraction. Hence the contours also get messed up.
Since the boxes are arranged periodically, is there a way to extract the boxes using Fast Fourier transforms?
I recently came up with a python package that deals with this exact problem.
I called it BoxDetect and after installing it through:
pip install boxdetect
It may look somewhat like this (you need to adjust parameters for different forms:
from boxdetect import config
config.min_w, config.max_w = (20,50)
config.min_h, config.max_h = (20,50)
config.scaling_factors = [0.4]
config.dilation_iterations = 0
config.wh_ratio_range = (0.5, 2.0)
config.group_size_range = (1, 100)
config.horizontal_max_distance_multiplier = 2
from boxdetect.pipelines import get_boxes
image_path = "dumpster/m1nda.jpg"
rects, grouped_rects, org_image, output_image = get_boxes(image_path, config, plot=False)
You might want to check below thread for more info:
How to detect all boxes for inputting letters in forms for a particular field?
The Fourier transform is the last thing I would think of.
I'd rather try with a Hough line detector to get long lines or as you did, with edge detection, but I would reconstruct the grids explicitly, finding their pitch and the exact locations of the rows/columns, hence every individual cell.
You can try select handwritten characters by color.
example:
import cv2
import numpy as np
img=cv2.imread('YdUqv .jpg')
#convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
#color definition
color_lower = np.array([105,80,60])
color_upper = np.array([140,255,255])
# select color objects
mask = cv2.inRange(hsv, color_lower, color_upper)
cv2.imwrite('hand.png', mask)
Result:

Remove outliers lines after findContours in image using python

I want to detect all rectangles in image and I use findContours in OpenCv , and I want to delete unnecessary shapes that have been identified by FindContours.
My image https://i.stack.imgur.com/eLb1s.png
My result: https://i.stack.imgur.com/xQqeF.png
My code:
img =cv2.imread('CD/A.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,50,150,apertureSize = 3)
img1=np.ones(img.shape, dtype=np.uint8)*255
ret,thresh = cv2.threshold(gray,127,255,1)
(_,contours,h) = cv2.findContours(thresh,1,2)
for cnt in contours:
approx = cv2.approxPolyDP(cnt,0.01*cv2.arcLength(cnt,True),True)
if len(approx)==4:
cv2.drawContours(img1,[cnt],0,(0,255,0),2)
cv2.imshow('Detected line',img1)
cv2.waitKey(0)
cv2.destroyAllWindows()
I want to remove these extreme lines that exist within the rectangles :
https://i.stack.imgur.com/n9byP.png
Need your help guys .
One thing you could do is find the connected components and remove the ones that are small:
from skimage.morphology import label
import numpy as np
comps = label(thresh) # get the label map of the connected components
# The comps array will have a unique integer for each connected component
# and 0 for the background. np.unique gets the unique label values.
#
# Therefore, this loop allows us to pluck out each component from the image
for i in range(1, len(np.unique(comps))):
# comps == i will convert the array into True (1) if that pixel is in the
# i-th component and False (0) if it is not.
#
# Therefore, np.sum(comps == i) returns the "area" of the component
if np.sum(comps == i) < small_number:
# If the area is less than some number of pixels,
# set the pixels of this component to 0 in the thresholded image
thresh[comps == i] = 0
You can do the labeling with OpenCV as well with connectedComponentsWithStats or something like that but I'm more familiar with skimage.
If you can convert your image into a binary image (with a simple threshold), you can perform a morphological open operation which can help you filter out small lines in your image within the rectangle and then find contours again on the new image.
https://docs.opencv.org/trunk/d9/d61/tutorial_py_morphological_ops.html

Get Depth image in grayscale in ROS with imgmsg_to_cv2 [python]

I am using Kinect v1 and I want to get the depth image in greyscale mode from the channel "/camera/depth_registered/image" in ROS. As I found here, I can do it by using the function imgmsg_to_cv2. The default desired_encoding for my depth messages is "32FC1", which I keep. The problem is that when I use the cv2.imshow() function to show it, I get the image in binary... When I do the same for the RGB image everything is being shown just fine...
Any help appreciated!
So after all, I found a solution, which you can see here:
def Depthcallback(self,msg_depth): # TODO still too noisy!
try:
# The depth image is a single-channel float32 image
# the values is the distance in mm in z axis
cv_image = self.bridge.imgmsg_to_cv2(msg_depth, "32FC1")
# Convert the depth image to a Numpy array since most cv2 functions
# require Numpy arrays.
cv_image_array = np.array(cv_image, dtype = np.dtype('f8'))
# Normalize the depth image to fall between 0 (black) and 1 (white)
# http://docs.ros.org/electric/api/rosbag_video/html/bag__to__video_8cpp_source.html lines 95-125
cv_image_norm = cv2.normalize(cv_image_array, cv_image_array, 0, 1, cv2.NORM_MINMAX)
# Resize to the desired size
cv_image_resized = cv2.resize(cv_image_norm, self.desired_shape, interpolation = cv2.INTER_CUBIC)
self.depthimg = cv_image_resized
cv2.imshow("Image from my node", self.depthimg)
cv2.waitKey(1)
except CvBridgeError as e:
print(e)
However, the result is not that perfect as the one I get from the image_view node of ROS, but it is still pretty acceptable!

Resources