Pixel Co-Ordinate Logging for an imported Image (Python) - python-3.x

Okay so, I want to be able to record the co-ordinates of pixels within a given image. The image is used to calibrate and determine the lens distortion and iFoV (Field of View) of a camera at varying distances.
I am trying to code (in Python) a program that will allow me to open up an initial image within a directory and allow me to pinpoint the pixel co-ordinates of the following areas;
TOP LEFT, TOP RIGHT, TOP MIDDLE, MID RIGHT, MID LEFT, BOT LEFT, BOT MID and BOT RIGHT
So basically, i need to open the image click those 8 points (recording the points in a csv file) then press a key e.g. ''SPACEBAR'' or ''ESC'' etc. which brings up the next image in the directory (Tool at a new length). Being able to repeat for many images.
Example Calibration Tool - Red Dots are the Wanted Co-Ordinates:

I have written a mock tool that addresses your purpose.
Pass the appropriate path conatining image in filepath.
Press and release left mouse button to see the marking.
Hit spacebar to clear markings on current image (in case you make a mistake).
Press lower case 'q' to move to next image.
Code:
import cv2
import numpy as np
import glob
ix, iy = -1, -1
# mouse callback function
def draw_circle(event, x, y, flags, param):
global ix, iy, mode
if event == cv2.EVENT_LBUTTONDOWN:
ix, iy = x, y
elif event == cv2.EVENT_LBUTTONUP:
cv2.circle(img, (x, y), 5, (0, 0, 255), -1)
print(x, y)
cv2.namedWindow('image')
cv2.setMouseCallback('image', draw_circle)
filepath = r'C:\Users\Jackson\Desktop\*.png'
for file in glob.glob(filepath):
img = cv2.imread(file)
img2 = img.copy()
while(1):
cv2.imshow('image', img)
k = cv2.waitKey(1) & 0xFF
if k == 32: #--- Press spacebar to clear markings on current image ---
img = img2.copy()
elif k == ord('q'): #--- Press lowercase q to move to next image ---
break
cv2.destroyAllWindows()

Related

How to find the location of a point in an image in millimeters using camera matrix?

I am using a standard 640x480 webcam. I have done Camera calibration in OpenCV in Python 3. This the Code I am using. The code is working and giving me the Camera Matrix and Distortion Coefficients successfully.
Now, How can I find how many millimeters are there in 640 pixels in my scene image. I have attached the webcam above a table horizontally and on the table, a Robotic arm is placed. Using the camera I am finding the centroid of an object. Using Camera Matrix my goal is to convert the location of that object (e.g. 300x200 pixels) to the millimeter units so that I can give the millimeters to the robotic arm to pick that object.
I have searched but not find any relevant information.
Please tell me that is there any equation or method for that. Thanks a lot!
import numpy as np
import cv2
import yaml
import os
# Parameters
#TODO : Read from file
n_row=4 #Checkerboard Rows
n_col=6 #Checkerboard Columns
n_min_img = 10 # number of images needed for calibration
square_size = 40 # size of each individual box on Checkerboard in mm
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) # termination criteria
corner_accuracy = (11,11)
result_file = "./calibration.yaml" # Output file having camera matrix
# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(n_row-1,n_col-1,0)
objp = np.zeros((n_row*n_col,3), np.float32)
objp[:,:2] = np.mgrid[0:n_row,0:n_col].T.reshape(-1,2) * square_size
# Intialize camera and window
camera = cv2.VideoCapture(0) #Supposed to be the only camera
if not camera.isOpened():
print("Camera not found!")
quit()
width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))
cv2.namedWindow("Calibration")
# Usage
def usage():
print("Press on displayed window : \n")
print("[space] : take picture")
print("[c] : compute calibration")
print("[r] : reset program")
print("[ESC] : quit")
usage()
Initialization = True
while True:
if Initialization:
print("Initialize data structures ..")
objpoints = [] # 3d point in real world space
imgpoints = [] # 2d points in image plane.
n_img = 0
Initialization = False
tot_error=0
# Read from camera and display on windows
ret, img = camera.read()
cv2.imshow("Calibration", img)
if not ret:
print("Cannot read camera frame, exit from program!")
camera.release()
cv2.destroyAllWindows()
break
# Wait for instruction
k = cv2.waitKey(50)
# SPACE pressed to take picture
if k%256 == 32:
print("Adding image for calibration...")
imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# Find the chess board corners
ret, corners = cv2.findChessboardCorners(imgGray, (n_row,n_col),None)
# If found, add object points, image points (after refining them)
if not ret:
print("Cannot found Chessboard corners!")
else:
print("Chessboard corners successfully found.")
objpoints.append(objp)
n_img +=1
corners2 = cv2.cornerSubPix(imgGray,corners,corner_accuracy,(-1,-1),criteria)
imgpoints.append(corners2)
# Draw and display the corners
imgAugmnt = cv2.drawChessboardCorners(img, (n_row,n_col), corners2,ret)
cv2.imshow('Calibration',imgAugmnt)
cv2.waitKey(500)
# "c" pressed to compute calibration
elif k%256 == 99:
if n_img <= n_min_img:
print("Only ", n_img , " captured, ", " at least ", n_min_img , " images are needed")
else:
print("Computing calibration ...")
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, (width,height),None,None)
if not ret:
print("Cannot compute calibration!")
else:
print("Camera calibration successfully computed")
# Compute reprojection errors
for i in range(len(objpoints)):
imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2)
tot_error += error
print("Camera matrix: ", mtx)
print("Distortion coeffs: ", dist)
print("Total error: ", tot_error)
print("Mean error: ", np.mean(error))
# Saving calibration matrix
try:
os.remove(result_file) #Delete old file first
except Exception as e:
#print(e)
pass
print("Saving camera matrix .. in ",result_file)
data={"camera_matrix": mtx.tolist(), "dist_coeff": dist.tolist()}
with open(result_file, "w") as f:
yaml.dump(data, f, default_flow_style=False)
# ESC pressed to quit
elif k%256 == 27:
print("Escape hit, closing...")
camera.release()
cv2.destroyAllWindows()
break
# "r" pressed to reset
elif k%256 ==114:
print("Reset program...")
Initialization = True
This the Camera Matrix:
818.6 0 324.4
0 819.1 237.9
0 0 1
Distortion coeffs:
0.34 -5.7 0 0 33.45
I was actually thinking that you should be able to solve your problem in a simple way:
mm_per_pixel = real_mm_width : 640px
Assuming that the camera initially moves in parallel to the plan with the object to pick [i.e. fixed distance], real_mm_width can be found measuring the physical distance corresponding to those 640 pixels of your picture. For the sake of an example say that you find that real_mm_width = 32cm = 320mm, so you get mm_per_pixel = 0.5mm/px. With a fixed distance this ratio doesn't change
It seems also the suggestion from the official documentation:
This consideration helps us to find only X,Y values. Now for X,Y
values, we can simply pass the points as (0,0), (1,0), (2,0), ...
which denotes the location of points. In this case, the results we get
will be in the scale of size of chess board square. But if we know the
square size, (say 30 mm), we can pass the values as (0,0), (30,0),
(60,0), ... . Thus, we get the results in mm
Then you just need to convert the centroid coordinates in pixels [e.g. (pixel_x_centroid, pixel_y_centroid) = (300px, 200px)] to mm using:
mm_x_centroid = pixel_x_centroid * mm_per_pixel
mm_y_centroid = pixel_y_centroid * mm_per_pixel
which would give you the final answer:
(mm_x_centroid, mm_y_centroid) = (150mm, 100mm)
Another way to see the same thing is this proportion where the first member is the measurable/known ratio:
real_mm_width : 640px = mm_x_centroid : pixel_x_centroid = mm_y_centroid = pixel_y_centroid

How to count objects in video using openCV and python?

I want to count vehicles when the rectangle touches(intersects) the line. I don't know more about counting objects in video frame. Based on my pre-search, I have found out some useful information such as centroid, euclidean distance and etc. But, i don't have much knowledge about the geometry math behind it. Here is an example video. https://www.youtube.com/watch?v=z1Cvn3_4yGo. I think they used the same method.
I have got the center of the rectangle and drawn a red line across the road. Now I want to count vehicles when it touches the line. Bellow, I have uploaded my current output of the system in a image format as well as my current code.
# import the necessary packages
from imutils.video import VideoStream
from imutils.video import FPS
import argparse
import imutils
import time
import cv2
tracker = cv2.TrackerCSRT_create()
vs = cv2.VideoCapture("Video.mp4")
initBB = None
# loop over frames from the video stream
while vs.isOpened():
ret,frame = vs.read()
cv2.line(frame, (933 , 462), (1170 , 462), (0,0,255), 3)
# check to see if we are currently tracking an object
if initBB is not None:
# grab the new bounding box coordinates of the object
(success, box) = tracker.update(frame)
# check to see if the tracking was a success
if success:
(x, y, w, h) = [int(v) for v in box]
cv2.rectangle(frame, (x, y), (x + w, y + h),
(0, 255, 0), 2)
cX = int((x + x+w) / 2.0)
cY = int((y + y+h) / 2.0)
cv2.circle(frame, (cX, cY), 4, (255, 0, 0), -1)
# show the output frame
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
if key == ord("s"):
# select the bounding box of the object we want to track (make
# sure you press ENTER or SPACE after selecting the ROI)
initBB = cv2.selectROI("Frame", frame, fromCenter=False,
showCrosshair=True)
# start OpenCV object tracker using the supplied bounding box
# coordinates, then start the FPS throughput estimator as well
tracker.init(frame, initBB)
fps = FPS().start()
# if the `q` key was pressed, break from the loop
elif key == ord("q"):
break
else:
vs.release()
cv2.destroyAllWindows()
You should just check whether the lower two x axis points of your bonding box are greater than or equal to the x axis points of the line

Opencv3 - How to give ID to the people I count based on contours and rectrangles?

I want to give ID to the contourareas that I draw rectangle on them. Now my code tracks moving object on the screen and put a rectangle around them. I want to give an ID to the each of rectangles. I know how to count how many rectangles on the screen but I don't know how to give rectangles an exact ID that doesn't change when another rectangle joins the screen.
The code I use to draw rectangles:
video_path = 'C:\\Users\\MONSTER\\Desktop\\video.avi'
cv2.ocl.setUseOpenCL(False)
version = cv2.__version__.split('.')[0]
print(version)
#read video file
cap = cv2.VideoCapture(video_path)
#check opencv version
if version == '2' :
fgbg = cv2.BackgroundSubtractorMOG2()
if version == '3':
fgbg = cv2.createBackgroundSubtractorMOG2()
while (cap.isOpened):
#if ret is true than no error with cap.isOpened
ret, frame = cap.read()
if ret==True:
#apply background substraction
fgmask = fgbg.apply(frame)
ret1,th1 = cv2.threshold(fgmask,150,200,cv2.THRESH_BINARY)
#check opencv version
if version == '2' :
(contours, hierarchy) = cv2.findContours(th1.copy(),
cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
if version == '3' :
(im2, contours, hierarchy) = cv2.findContours(th1.copy(),
cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
if cv2.contourArea(c) < 200:
continue
#get bounding box from countour
(x, y, w, h) = cv2.boundingRect(c)
#draw bounding box
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('foreground and background',th1)
cv2.imshow('rgb',frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
And I changed the code above to the code below to put text on rectangles but the text changes when another rectangle joins.
i = 1
for c in contours:
if cv2.contourArea(c) < 200:
continue
#get bounding box from countour
(x, y, w, h) = cv2.boundingRect(c)
#draw bounding box
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
i = i + 1
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(frame,str(i),(x,y), font, 2,(255,255,255),2,cv2.LINE_AA)
Do you have any idea to give rectangles exact ID's.
Your code does not do the tracking. only the detection from the background only. To make your code natively do the tracking, I am afraid it's not that simple. tracking will only happen if you have a perfect connected object and there is only 1 object. It usually rarely happens as fail detection happens all the time. Thus multiple RECT are created.
The ID will keep changes for different frames when you enter the scene and there are multiple detections like the image below. I tried it before. Each time the bounding rect will change from object to object. Especially when you use a simple method like bgfg, this lost track or lost id happens almost every frame.
The proper way is to use real tracking algorithm to constantly update the object. e.g
https://www.youtube.com/watch?v=qvcyK4ZMKbM
The input to TLD tracker is from the Rect obtained by
(x, y, w, h) = cv2.boundingRect(c)
The source code is in the github. feel free to test it
https://github.com/gnebehay/TLD
Follow the installation to get it up and integrate into your current detection routing.
https://github.com/zk00006/OpenTLD/wiki/Installation
You need to track multiple objects.
You need to check their intersections and stop tracking when they collide.
https://www.pyimagesearch.com/2018/08/06/tracking-multiple-objects-with-opencv/

Multi-object tracking initialization in Opencv using multiTracker object

I am using multiTracker in cv2 to track multiple objects. My code is built based on this link and this one. I want to initialize all the bounding boxes at once at any point during the video. However, I have trouble doing this. Here is the code:
import imutils
import cv2
from random import randint
trackerName = 'csrt'
videoPath = "C:/Users/Art/testVideo.mp4"
OPENCV_OBJECT_TRACKERS = {
"csrt": cv2.TrackerCSRT_create,
"kcf": cv2.TrackerKCF_create,
"boosting": cv2.TrackerBoosting_create,
"mil": cv2.TrackerMIL_create,
"tld": cv2.TrackerTLD_create,
"medianflow": cv2.TrackerMedianFlow_create,
"mosse": cv2.TrackerMOSSE_create
}
trackers = cv2.MultiTracker_create()
cap = cv2.VideoCapture(videoPath)
while cap.isOpened():
ret, frame = cap.read()
if frame is None:
break
# for fast processing resize the frame
frame = imutils.resize(frame, width=600)
(success, boxes) = trackers.update(frame)
for box in boxes:
(x, y, w, h) = [int(v) for v in box]
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
# if the 'p' key is selected, we select a bounding box to track
if key == ord("p"):
boxes = []
colors = []
boxFlag = True
while boxFlag:
box = cv2.selectROI('MultiTracker', frame, fromCenter=False,
showCrosshair=True)
boxes.append(box)
colors.append((randint(64, 255), randint(64, 255), randint(64,
255)))
print("Press q to quit selecting boxes and start tracking")
print("Press any other key to select next object")
if key == ord("q"): # q is pressed
boxFlag = False
# Initialize MultiTracker
for box in boxes:
tracker = OPENCV_OBJECT_TRACKERS[trackerName]()
trackers.add(tracker, frame, box)
cap.release()
cv2.destroyAllWindows()
However, there are some problems. First, when I click on 'p' key to select bounding boxes, the video pauses and another window opens that shows the frame at which video was paused and I can select bounding boxes on the new window only. Also, when I press 'q' key, nothing will happen and basically it stays there forever. My question is how I can fix this problem, and be able to initialize tracking after I select all of the bounding boxes.
I figured it out and thought this might be useful for someone else. I made some changes to the previous code. I also realized that cv2 has selectROIs method which can be useful if someone wants to select multiple bounding boxes at once. Here is the updated code:
import imutils
import cv2
from random import randint
trackerName = 'csrt'
videoPath = "C:/Users/Art/testVideo.mp4"
OPENCV_OBJECT_TRACKERS = {
"csrt": cv2.TrackerCSRT_create,
"kcf": cv2.TrackerKCF_create,
"boosting": cv2.TrackerBoosting_create,
"mil": cv2.TrackerMIL_create,
"tld": cv2.TrackerTLD_create,
"medianflow": cv2.TrackerMedianFlow_create,
"mosse": cv2.TrackerMOSSE_create
}
# initialize OpenCV's special multi-object tracker
trackers = cv2.MultiTracker_create()
cap = cv2.VideoCapture(videoPath)
while cap.isOpened():
ret, frame = cap.read()
if frame is None:
break
frame = imutils.resize(frame, width=600)
(success, boxes) = trackers.update(frame)
# loop over the bounding boxes and draw them on the frame
for box in boxes:
(x, y, w, h) = [int(v) for v in box]
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
# if the 's' key is selected, we are going to "select" a bounding
# box to track
if key == ord("s"):
colors = []
# select the bounding box of the object we want to track (make
# sure you press ENTER or SPACE after selecting the ROI)
box = cv2.selectROIs("Frame", frame, fromCenter=False,
showCrosshair=True)
box = tuple(map(tuple, box))
for bb in box:
tracker = OPENCV_OBJECT_TRACKERS[trackerName]()
trackers.add(tracker, frame, bb)
# if you want to reset bounding box, select the 'r' key
elif key == ord("r"):
trackers.clear()
trackers = cv2.MultiTracker_create()
box = cv2.selectROIs("Frame", frame, fromCenter=False,
showCrosshair=True)
box = tuple(map(tuple, box))
for bb in box:
tracker = OPENCV_OBJECT_TRACKERS[trackerName]()
trackers.add(tracker, frame, bb)
elif key == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
After selecting each bounding box, you need to click on "Enter" or "Space" button to finalize it and start selecting a new bounding box. Once you are done with bounding box selection, press "Esc" key to end ROI selection and start tracking. Also, if you need to reset bounding boxes for any reason, press "r" button.
Maybe You should try the "cv2.selectROIs" function before the "while cap.isOpened():" loop, without loop, because it looks like "cv2.selectROIs" function works as loop itself, while you are selecting ROIs.

How do i save the image result from this code?

I'm trying to write a code that identifies the eyes of parakeets. So far, i have managed to use a code that identifies the circles edges and got great results at a certain threshold. But i can't save the result image.
I've tried using imwrite('result.png', clone) to save the result at the end of the code, but when I run it I'm get TypeError: Expected cv::UMat for argument 'img'.
I need the clone image to be colored too, but I've no idea where to start.
import cv2
import numpy as np
import imutils
def nothing(x):
pass
# Load an image
img = cv2.imread('sample.png')
# Resize The image
if img.shape[1] > 600:
img = imutils.resize(img, width=600)
# Create a window
cv2.namedWindow('Treshed')
# create trackbars for treshold change
cv2.createTrackbar('Treshold','Treshed',0,255,nothing)
while(1):
# Clone original image to not overlap drawings
clone = img.copy()
# Convert to gray
gray = cv2.cvtColor(clone, cv2.COLOR_BGR2GRAY)
# get current positions of four trackbars
r = cv2.getTrackbarPos('Treshold','Treshed')
# Thresholding the gray image
ret,gray_threshed = cv2.threshold(gray,r,255,cv2.THRESH_BINARY)
# Blur an image
bilateral_filtered_image = cv2.bilateralFilter(gray_threshed, 5, 175, 175)
# Detect edges
edge_detected_image = cv2.Canny(bilateral_filtered_image, 75, 200)
# Find contours
contours, _= cv2.findContours(edge_detected_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contour_list = []
for contour in contours:
# approximte for circles
approx = cv2.approxPolyDP(contour,0.01*cv2.arcLength(contour,True),True)
area = cv2.contourArea(contour)
if ((len(approx) > 8) & (area > 30) ):
contour_list.append(contour)
# Draw contours on the original image
cv2.drawContours(clone, contour_list, -1, (255,0,0), 2)
# there is an outer boundary and inner boundary for each eadge, so contours double
print('Number of found circles: {}'.format(int(len(contour_list)/2)))
#Displaying the results
cv2.imshow('Objects Detected', clone)
cv2.imshow("Treshed", gray_threshed)
# ESC to break
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
# close all open windows
cv2.destroyAllWindows()
I just tried this modification and it works flawlessly.
# ESC to break
k = cv2.waitKey(1) & 0xFF
if k == 27:
cv2.imwrite('result.png', clone)
break
There's either a misconception about when/where to call imwrite or something is messy with your python/opencv version. I ran it in pycharm using python 3.6.8 and opencv-python 4.0.0.21

Resources