Refactor to reduce overlap between course folders#3
Refactor to reduce overlap between course folders#3Michal-Fularz wants to merge 1 commit intomasterfrom
Conversation
- 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>
|
👋 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 New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
There was a problem hiding this comment.
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.
|
|
||
| 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) |
There was a problem hiding this comment.
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.
| 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) |
| 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 |
There was a problem hiding this comment.
| 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)) |
There was a problem hiding this comment.
Using cv2.magnitude is more efficient and readable than manually calculating the square root of the sum of squares for gradient magnitude.
| 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) |
| 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)) |
There was a problem hiding this comment.
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.
| 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)) |
| 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) |
There was a problem hiding this comment.
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.
| 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) |
| 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] |
There was a problem hiding this comment.
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.
| 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] |
| # 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) |
There was a problem hiding this comment.
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.
| 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) |
I have reorganized the repository to reduce overlap between the
image_processing_courseandvision_systemsfolders. I identified thatimage_processing_coursecontained somesw_prefixed files which correspond to the Systemy Wizyjne (Vision Systems) course, so I moved them into thevision_systemsdirectory, overwriting the older versions there. I also moved thes01e03.pyands01eTestPreparation.pyfiles tovision_systemsand preserved theipc_prefixed files inimage_processing_coursefor the Image Processing Course.PR created automatically by Jules for task 15483639455431496973 started by @Michal-Fularz