From 40f16ac5352d660bee7fdb62623984b44621bf04 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 9 Nov 2025 13:38:03 +0000 Subject: [PATCH 1/2] Initial plan From a661e177c00d1019756c2950ede9fd1be446fde4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 9 Nov 2025 13:44:26 +0000 Subject: [PATCH 2/2] Add calcOpticalFlowPyrLK documentation and comprehensive tests Co-authored-by: ttt43ttt <132509+ttt43ttt@users.noreply.github.com> --- src/types/opencv/video_track.ts | 44 +++++++++- test/OpticalFlow.test.ts | 141 ++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 test/OpticalFlow.test.ts diff --git a/src/types/opencv/video_track.ts b/src/types/opencv/video_track.ts index e2f26c3..dc7e761 100644 --- a/src/types/opencv/video_track.ts +++ b/src/types/opencv/video_track.ts @@ -130,9 +130,11 @@ export declare function calcOpticalFlowFarneback( * @param status output status vector (of unsigned chars); each element of the vector is set to 1 if * the flow for the corresponding features has been found, otherwise, it is set to 0. * - * @param err output vector of errors; each element of the vector is set to an error for the + * @param err output vector of errors (Mat); each element of the vector is set to an error metric for the * corresponding feature, type of the error measure can be set in flags parameter; if the flow wasn't * found then the error is not defined (use the status parameter to find such cases). + * **IMPORTANT**: This is an OUTPUT array where error metrics are stored, NOT an exception pointer. + * Do NOT call `cv.exceptionFromPtr(err)` on this parameter. * * @param winSize size of the search window at each pyramid level. * @@ -156,6 +158,46 @@ export declare function calcOpticalFlowFarneback( * number of pixels in a window; if this value is less than minEigThreshold, then a corresponding * feature is filtered out and its flow is not processed, so it allows to remove bad points and get a * performance boost. + * + * @throws {Error} Throws an exception if inputs are invalid (e.g., mismatched sizes, wrong types). + * Wrap calls in try-catch and use translateException() helper to properly handle errors. + * + * @example + * ```typescript + * try { + * const prevGray = new cv.Mat(); + * const curGray = new cv.Mat(); + * const prevPts = new cv.Mat(corners.rows, 1, cv.CV_32FC2); + * const nextPts = new cv.Mat(); + * const status = new cv.Mat(); + * const err = new cv.Mat(); // This stores error metrics, NOT exception pointers + * + * cv.calcOpticalFlowPyrLK( + * prevGray, curGray, prevPts, nextPts, status, err, + * new cv.Size(15, 15), 2, + * new cv.TermCriteria(cv.TermCriteria_EPS | cv.TermCriteria_COUNT, 10, 0.03) + * ); + * + * // Check status to see which points were successfully tracked + * for (let i = 0; i < status.rows; i++) { + * if (status.data[i] === 1) { + * // Point was successfully tracked + * const errorMetric = err.data32F[i]; // Access error metric for this point + * } + * } + * + * // Clean up + * prevGray.delete(); + * curGray.delete(); + * prevPts.delete(); + * nextPts.delete(); + * status.delete(); + * err.delete(); + * } catch (error) { + * // Handle OpenCV exceptions properly + * throw translateException(error); + * } + * ``` */ export declare function calcOpticalFlowPyrLK( prevImg: InputArray, diff --git a/test/OpticalFlow.test.ts b/test/OpticalFlow.test.ts new file mode 100644 index 0000000..676bb47 --- /dev/null +++ b/test/OpticalFlow.test.ts @@ -0,0 +1,141 @@ +import { Jimp } from "jimp"; +import path from "path"; +import { setupOpenCv, translateException } from "./cv"; + +beforeAll(setupOpenCv); + +describe("Optical Flow", () => { + it("should calculate optical flow with calcOpticalFlowPyrLK correctly", async () => { + try { + // Load test image + const jimpSrc = await Jimp.read(path.resolve(__dirname, "Lenna.png")); + const img = cv.matFromImageData(jimpSrc.bitmap); + + // Convert to grayscale + const gray = new cv.Mat(); + cv.cvtColor(img, gray, cv.COLOR_RGBA2GRAY); + + // Detect good features to track + const corners = new cv.Mat(); + const maxCorners = 100; + const qualityLevel = 0.01; + const minDistance = 10; + cv.goodFeaturesToTrack( + gray, + corners, + maxCorners, + qualityLevel, + minDistance + ); + + // For optical flow, we need two images + // For this test, we'll use the same image (in practice you'd use consecutive frames) + const prevGray = gray; + const curGray = gray.clone(); + + // Convert corners to the right format for calcOpticalFlowPyrLK + // prevPts should be a Mat with type CV_32FC2 + const prevPts = new cv.Mat(corners.rows, 1, cv.CV_32FC2); + for (let i = 0; i < corners.rows; i++) { + prevPts.data32F[i * 2] = corners.data32F[i * 2]; + prevPts.data32F[i * 2 + 1] = corners.data32F[i * 2 + 1]; + } + + // Output arrays + const nextPts = new cv.Mat(); + const status = new cv.Mat(); + const err = new cv.Mat(); // This is an OUTPUT array, NOT an exception pointer! + + // Optional parameters + const winSize = new cv.Size(15, 15); + const maxLevel = 2; + const criteria = new cv.TermCriteria( + cv.TermCriteria_EPS | cv.TermCriteria_COUNT, + 10, + 0.03 + ); + + // Calculate optical flow + // This may throw an exception if inputs are invalid + cv.calcOpticalFlowPyrLK( + prevGray, + curGray, + prevPts, + nextPts, + status, + err, // This is where error metrics are stored, NOT an exception pointer + winSize, + maxLevel, + criteria + ); + + // Verify results + expect(nextPts.rows).toBeGreaterThan(0); + expect(status.rows).toBe(nextPts.rows); + expect(err.rows).toBe(nextPts.rows); // err contains error metrics for each point + + // Check that we have some successfully tracked points + let successCount = 0; + for (let i = 0; i < status.rows; i++) { + if (status.data[i] === 1) { + successCount++; + } + } + expect(successCount).toBeGreaterThan(0); + + // Clean up + img.delete(); + gray.delete(); + curGray.delete(); + corners.delete(); + prevPts.delete(); + nextPts.delete(); + status.delete(); + err.delete(); + } catch (error) { + // Properly handle exceptions from OpenCV + throw translateException(error); + } + }); + + it("should throw exception when calcOpticalFlowPyrLK receives invalid inputs", async () => { + // Create invalid inputs to trigger an error + const prevGray = new cv.Mat(100, 100, cv.CV_8UC1); + const curGray = new cv.Mat(100, 100, cv.CV_8UC1); + const prevPts = new cv.Mat(0, 1, cv.CV_32FC2); // Empty points - this is invalid + const nextPts = new cv.Mat(); + const status = new cv.Mat(); + const err = new cv.Mat(); + + const winSize = new cv.Size(15, 15); + const maxLevel = 2; + const criteria = new cv.TermCriteria( + cv.TermCriteria_EPS | cv.TermCriteria_COUNT, + 10, + 0.03 + ); + + // This should throw an exception because of invalid input format + expect(() => { + cv.calcOpticalFlowPyrLK( + prevGray, + curGray, + prevPts, + nextPts, + status, + err, + winSize, + maxLevel, + criteria + ); + }).toThrow(); + + // Clean up + prevGray.delete(); + curGray.delete(); + prevPts.delete(); + nextPts.delete(); + status.delete(); + err.delete(); + }); +});