Skip to content

Refactor to reduce overlap between course folders#3

Open
Michal-Fularz wants to merge 1 commit intomasterfrom
fix/deduplicate-course-folders-15483639455431496973
Open

Refactor to reduce overlap between course folders#3
Michal-Fularz wants to merge 1 commit intomasterfrom
fix/deduplicate-course-folders-15483639455431496973

Conversation

@Michal-Fularz
Copy link
Copy Markdown
Member

I have reorganized the repository to reduce overlap between the image_processing_course and vision_systems folders. I identified that image_processing_course contained some sw_ prefixed files which correspond to the Systemy Wizyjne (Vision Systems) course, so I moved them into the vision_systems directory, overwriting the older versions there. I also moved the s01e03.py and s01eTestPreparation.py files to vision_systems and preserved the ipc_ prefixed files in image_processing_course for the Image Processing Course.


PR created automatically by Jules for task 15483639455431496973 started by @Michal-Fularz

- Moved `sw_s01e*.py` files from `image_processing_course` to `vision_systems`
- Replaced the overlapping files in `vision_systems`
- Maintained the `ipc_` files in `image_processing_course`
- Moved `s01eTestPreparation.py` and `s01e03.py` to `vision_systems`
- Cleaned up duplicated items

Co-authored-by: Michal-Fularz <3768498+Michal-Fularz@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors several computer vision scripts, focusing on improving gradient calculations, edge detection, and camera calibration. The changes include the introduction of a helper function for gradient operations and updated logic for background subtraction. Feedback highlights critical issues with parameter ordering in OpenCV calibration functions, potential runtime crashes due to missing null checks or failed camera reads, and opportunities to use more efficient OpenCV functions like cv2.magnitude and cv2.convertScaleAbs to prevent data overflow.

Comment thread vision_systems/s01e09.py

image_points = [corners] # [corners1, corners2, corners3]
object_points = [object_points] # [object_points1, object_points2, object_points3]
retval, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(object_points, image_points, image.shape[:2], None, None)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The imageSize parameter in cv2.calibrateCamera expects a tuple in (width, height) format, but image.shape[:2] provides (height, width). This can lead to incorrect calibration results or errors.

Suggested change
retval, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(object_points, image_points, image.shape[:2], None, None)
retval, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(object_points, image_points, image.shape[1::-1], None, None)

Comment thread vision_systems/s01e09.py
retval, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(object_points, image_points, image.shape[:2], None, None)

fovx, fovy, focalLength, principalPoint, aspectRatio = cv2.calibrationMatrixValues(
camera_matrix, image.shape[:2], 7.2, 5.4
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Similar to calibrateCamera, cv2.calibrationMatrixValues expects imageSize as (width, height). Using image.shape[:2] provides the dimensions in the wrong order.

Suggested change
camera_matrix, image.shape[:2], 7.2, 5.4
camera_matrix, image.shape[1::-1], 7.2, 5.4

Comment thread vision_systems/s01e05.py
img_gradient_x = cv2.filter2D(img, cv2.CV_32F, kernel_x)
img_gradient_y = cv2.filter2D(img, cv2.CV_32F, kernel_y)

img_gradient = cv2.sqrt(pow(img_gradient_x / divider, 2) + pow(img_gradient_y / divider, 2))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using cv2.magnitude is more efficient and readable than manually calculating the square root of the sum of squares for gradient magnitude.

Suggested change
img_gradient = cv2.sqrt(pow(img_gradient_x / divider, 2) + pow(img_gradient_y / divider, 2))
img_gradient = cv2.magnitude(img_gradient_x / divider, img_gradient_y / divider)

Comment thread vision_systems/s01e05.py
Comment on lines +19 to +21
cv2.imshow(f'{window_name_prefix}_x', (abs(img_gradient_x) / divider).astype(np.uint8))
cv2.imshow(f'{window_name_prefix}_x_no_abs', (img_gradient_x / divider).astype(np.uint8))
cv2.imshow(f'{window_name_prefix}_y', (abs(img_gradient_y) / divider).astype(np.uint8))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Directly casting to np.uint8 after taking the absolute value can lead to wrap-around issues if values exceed 255. cv2.convertScaleAbs is the standard way to scale, calculate absolute values, and safely convert to uint8 with saturation. This is especially important for gradients where values can easily exceed the 0-255 range.

Suggested change
cv2.imshow(f'{window_name_prefix}_x', (abs(img_gradient_x) / divider).astype(np.uint8))
cv2.imshow(f'{window_name_prefix}_x_no_abs', (img_gradient_x / divider).astype(np.uint8))
cv2.imshow(f'{window_name_prefix}_y', (abs(img_gradient_y) / divider).astype(np.uint8))
cv2.imshow(f'{window_name_prefix}_x', cv2.convertScaleAbs(img_gradient_x, alpha=1.0/divider))
cv2.imshow(f'{window_name_prefix}_x_no_abs', (img_gradient_x / divider).astype(np.uint8))
cv2.imshow(f'{window_name_prefix}_y', cv2.convertScaleAbs(img_gradient_y, alpha=1.0/divider))

Comment thread vision_systems/s01e05.py
Comment on lines 121 to +126
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
# draw the outer circle
cv2.circle(img, (i[0], i[1]), i[2], (0, 255, 0), 2)
cv2.circle(img_hough_circles, (i[0], i[1]), i[2], (0, 255, 0), 2)
# draw the center of the circle
cv2.circle(img, (i[0], i[1]), 2, (0, 0, 255), 3)
cv2.imshow('detected circles', img)
cv2.circle(img_hough_circles, (i[0], i[1]), 2, (0, 0, 255), 3)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

cv2.HoughCircles returns None if no circles are detected. Attempting to call np.around or iterating over circles[0, :] without checking for None will cause a runtime error.

Suggested change
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
# draw the outer circle
cv2.circle(img, (i[0], i[1]), i[2], (0, 255, 0), 2)
cv2.circle(img_hough_circles, (i[0], i[1]), i[2], (0, 255, 0), 2)
# draw the center of the circle
cv2.circle(img, (i[0], i[1]), 2, (0, 0, 255), 3)
cv2.imshow('detected circles', img)
cv2.circle(img_hough_circles, (i[0], i[1]), 2, (0, 0, 255), 3)
if circles is not None:
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
# draw the outer circle
cv2.circle(img_hough_circles, (i[0], i[1]), i[2], (0, 255, 0), 2)
# draw the center of the circle
cv2.circle(img_hough_circles, (i[0], i[1]), 2, (0, 0, 255), 3)

Comment thread vision_systems/s01e09.py
Comment on lines +26 to +47
object_points = np.zeros((8 * 5, 3), np.float32)
print(object_points[1])
print(object_points.shape)
object_points[:, :2] = np.mgrid[0:8, 0:5].T.reshape(-1, 2)
print(object_points[1])
print(object_points.shape)

object_points_for = [] # np.zeros_like(object_points)
for i in range(0, 5):
for j in range(0, 8):
# print(object_points_for[i, j])
# object_points_for[i, j] = [i, j, 0]
object_points_for.append([j, i, 0])
object_points_for = np.array(object_points_for, dtype=np.float32)

print(f'(object_points[0:3]: {object_points[0:3]}')
print(object_points.shape)
print(f'object_points_for[0:3]: {object_points_for[0:3]}')
print(object_points_for.shape)

image_points = [corners] # [corners1, corners2, corners3]
object_points = [object_points] # [object_points1, object_points2, object_points3]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block contains redundant logic and multiple print statements for debugging that should be removed to improve maintainability. The object_points can be generated concisely using np.mgrid without the need for an explicit loop and comparison.

Suggested change
object_points = np.zeros((8 * 5, 3), np.float32)
print(object_points[1])
print(object_points.shape)
object_points[:, :2] = np.mgrid[0:8, 0:5].T.reshape(-1, 2)
print(object_points[1])
print(object_points.shape)
object_points_for = [] # np.zeros_like(object_points)
for i in range(0, 5):
for j in range(0, 8):
# print(object_points_for[i, j])
# object_points_for[i, j] = [i, j, 0]
object_points_for.append([j, i, 0])
object_points_for = np.array(object_points_for, dtype=np.float32)
print(f'(object_points[0:3]: {object_points[0:3]}')
print(object_points.shape)
print(f'object_points_for[0:3]: {object_points_for[0:3]}')
print(object_points_for.shape)
image_points = [corners] # [corners1, corners2, corners3]
object_points = [object_points] # [object_points1, object_points2, object_points3]
# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
object_points = np.zeros((8 * 5, 3), np.float32)
object_points[:, :2] = np.mgrid[0:8, 0:5].T.reshape(-1, 2)
image_points = [corners]
object_points = [object_points]

Comment thread vision_systems/s01e10.py
# cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)

background_image = cv2.cvtColor(cap.read()[1], cv2.COLOR_BGR2GRAY)
img_gray = cv2.cvtColor(cap.read()[1], cv2.COLOR_BGR2GRAY)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

cap.read() returns a success flag and the frame. If the camera fails to open or a frame cannot be read, cap.read()[1] will be None, causing cv2.cvtColor to crash. It is safer to check the return flag.

Suggested change
img_gray = cv2.cvtColor(cap.read()[1], cv2.COLOR_BGR2GRAY)
ret, frame = cap.read()
if not ret:
return
img_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant