Skip to content

Commit

Permalink
feat: add camera calibration example (#125)
Browse files Browse the repository at this point in the history
  • Loading branch information
ruben-arts authored Jun 21, 2023
1 parent cbae55f commit efeeede
Show file tree
Hide file tree
Showing 5 changed files with 4,462 additions and 4,298 deletions.
1 change: 1 addition & 0 deletions examples/opencv/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# pixi environments
.pixi
haarcascade_frontalface_default.xml
*.png
29 changes: 29 additions & 0 deletions examples/opencv/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# OpenCV example
OpenCV is a powerfull tool to do computer vision and fully opensource.

Here are some example on how to use it with `pixi`.

## Simple face detection algorithm
```shell
pixi run start
```
![Face detection result](https://github.com/ruben-arts/pixi/assets/12893423/c0151496-caae-407c-9e90-0f71f3c19aa7)


## Simple camera calibration script
```shell
pixi run calibrate
```

You'll need a checkerboard for this to work.
Print this: [![chessboard](https://github.com/opencv/opencv/blob/4.x/doc/pattern.png?raw=true)](https://github.com/opencv/opencv/blob/4.x/doc/pattern.png)

To make a picture for calibration press `SPACE`
Do this approximately 10 times with the chessboard in view of the camera

After that press `ESC` which will start the calibration.

When the calibration is done the camera will be used again to find the distance to the checkerboard.

![callibrated camera result](https://github.com/ruben-arts/pixi/assets/12893423/f42825d7-5010-4805-9f6b-b02075395413)

132 changes: 132 additions & 0 deletions examples/opencv/calibrate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import cv2
import numpy as np

# Termination criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 0.001)

# Prepare object points
# The example chessboard is 9x6.
CHESSBOARD_X = 6
CHESSBOARD_Y = 9
# The example chessboard printed on a A4 paper will approximaly be 24mm in width and height.
SQUARE_SIZE_MM = 22.5

objp = np.zeros((CHESSBOARD_X * CHESSBOARD_Y, 3), np.float32)
objp[:,:2] = np.mgrid[0:CHESSBOARD_Y, 0:CHESSBOARD_X].T.reshape(-1, 2) * (SQUARE_SIZE_MM * 0.001)

# Arrays to store object points and image points
objpoints = []
imgpoints = []

# Initialize the camera
cap = cv2.VideoCapture(0)

# Set the frame width and height
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)

img_counter = 0

print("Press SPACE to capture an image for calibration.")
print("Press ESC to calibrate the camera using the previously captured images.")

while True:
ret, frame = cap.read()
if not ret:
break

frame_clean = cv2.copyTo(frame, None)
# Find the chess board corners
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, (CHESSBOARD_Y, CHESSBOARD_X), None)

if ret:
cv2.drawChessboardCorners(frame, (CHESSBOARD_Y, CHESSBOARD_X), corners, ret)

cv2.imshow("Test", frame)
k = cv2.waitKey(1)

if k%256 == 27:
# ESC pressed
print("Escape hit, closing...")
break
elif k%256 == 32:
# SPACE pressed
img_name = "opencv_frame_{}.png".format(img_counter)
cv2.imwrite(img_name, frame_clean)
print("{} written!".format(img_name))
img_counter += 1

# Convert to grayscale
gray = cv2.cvtColor(frame_clean, cv2.COLOR_BGR2GRAY)

# Find the chess board corners
ret, corners = cv2.findChessboardCorners(gray, (CHESSBOARD_Y, CHESSBOARD_X), None)

# If found, add object points, image points (after refining them)
if ret:
objpoints.append(objp)

corners2 = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria)
imgpoints.append(corners2)
else:
print("No chessboard detected in this image: {img_name}")

cv2.destroyAllWindows()

if len(objpoints) > 0:
# Perform camera calibration
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

# Print out the camera calibration results
print("Camera matrix : \n")
print(mtx)
print("dist : \n")
print(dist)
print("rvecs : \n")
print(rvecs)
print("tvecs : \n")
print(tvecs)

while True:
ret, frame = cap.read()
if not ret:
break
k = cv2.waitKey(1)

if k%256 == 27:
# ESC pressed
print("Escape hit, closing...")
break

# Convert to grayscale
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

# Find the chess board corners
ret, corners = cv2.findChessboardCorners(gray, (CHESSBOARD_Y, CHESSBOARD_X), None)

if ret:
corners2 = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria)
imgpoints.append(corners2)

# Draw and display the corners
frame = cv2.drawChessboardCorners(frame, (CHESSBOARD_Y, CHESSBOARD_X), corners2, ret)

# Estimate pose of pattern
_, rvecs, tvecs, _ = cv2.solvePnPRansac(objp, corners2, mtx, dist)

# Compute distance from camera to pattern
x_distance = tvecs[0][0]
y_distance = tvecs[1][0]
z_distance = tvecs[2][0]

text = f"X: {x_distance:.2f}m, Y: {y_distance:.2f}m, Z: {z_distance:.2f}m"
cv2.putText(frame, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA)

cv2.imshow('Result', frame)

else:
print("Not enough images where corners were found. Please capture more images.")

cap.release()
cv2.destroyAllWindows()
Loading

0 comments on commit efeeede

Please sign in to comment.