Skip to content

Commit

Permalink
Create test
Browse files Browse the repository at this point in the history
  • Loading branch information
mcm001 committed Dec 27, 2024
1 parent 66a8065 commit 32a67e4
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 5 deletions.
46 changes: 42 additions & 4 deletions cscore/src/main/java/edu/wpi/first/cscore/CvSink.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private int getCVFormat(PixelFormat pixelFormat) {
* Create a sink for accepting OpenCV images. grabFrame() must be called on the created sink to
* get each new image.
*
* @param name Source name (arbitrary unique identifier)
* @param name Sink name (arbitrary unique identifier)
* @param pixelFormat Source pixel format
*/
public CvSink(String name, PixelFormat pixelFormat) {
Expand Down Expand Up @@ -139,7 +139,26 @@ public long grabFrame(Mat image, double timeout, long lastFrameTime) {
* is in 1 us increments.
*/
public long grabFrameNoTimeout(Mat image) {
long rv = grabFrameNoTimeoutDirect();
return grabFrameNoTimeout(image, 0);
}

/**
* Wait for the next frame and get the image. May block forever. The provided image will have the
* pixelFormat this class was constructed with.
*
* <p>If lastFrameTime is provided and non-zero, the sink will fill image with the first frame
* from the source that is not equal to lastFrameTime. If lastFrameTime is zero, the time of the
* current frame owned by the CvSource is used, and this function will block until the connected
* CvSource provides a new frame.
*
* @param image Where to store the image.
* @param lastFrameTime Timestamp of the last frame - used to compare new frames against, or 0 to
* use the current time
* @return Frame time, or 0 on error (call GetError() to obtain the error message); the frame time
* is in 1 us increments.
*/
public long grabFrameNoTimeout(Mat image, long lastFrameTime) {
long rv = grabFrameNoTimeoutDirect(lastFrameTime);
if (rv <= 0) {
return rv;
}
Expand Down Expand Up @@ -263,10 +282,29 @@ public long grabFrameDirect(double timeout, long lastFrameTime) {
* @return Frame time, or 0 on error (call GetError() to obtain the error message); the frame time
* is in 1 us increments.
*/
@SuppressWarnings("PMD.CompareObjectsWithEquals")
public long grabFrameNoTimeoutDirect() {
return grabFrameNoTimeoutDirect(0);
}

/**
* Wait for the next frame and store the image. May block forever. The provided image will have
* the pixelFormat this class was constructed with. Use getDirectMat() to grab the image.
*
* <p>If lastFrameTime is provided and non-zero, the sink will fill image with the first frame
* from the source that is not equal to lastFrameTime. If lastFrameTime is zero, the time of the
* current frame owned by the CvSource is used, and this function will block until the connected
* CvSource provides a new frame.
*
* @param lastFrameTime Timestamp of the last frame - used to compare new frames against, or 0 to
* use the current time
* @return Frame time, or 0 on error (call GetError() to obtain the error message); the frame time
* is in 1 us increments.
*/
@SuppressWarnings("PMD.CompareObjectsWithEquals")
public long grabFrameNoTimeoutDirect(long lastFrameTime) {
m_frame.setInfo(0, 0, 0, m_pixelFormat);
long rv = CameraServerJNI.grabRawSinkFrame(m_handle, m_frame, m_frame.getNativeObj());
long rv =
CameraServerJNI.grabRawSinkFrame(m_handle, m_frame, m_frame.getNativeObj(), lastFrameTime);
if (rv <= 0) {
return rv;
}
Expand Down
21 changes: 20 additions & 1 deletion cscore/src/main/java/edu/wpi/first/cscore/raw/RawSink.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,25 @@ public long grabFrame(RawFrame frame, double timeout, long lastFrameTime) {
* is in the same time base as wpi::Now(), and is in 1 us increments.
*/
public long grabFrameNoTimeout(RawFrame frame) {
return CameraServerJNI.grabRawSinkFrame(m_handle, frame, frame.getNativeObj());
return CameraServerJNI.grabRawSinkFrame(m_handle, frame, frame.getNativeObj(), 0);
}

/**
* Wait for the next frame and get the image. May block forever. The provided image will have
* three 8-bit channels stored in BGR order.
*
* <p>If lastFrameTime is provided and non-zero, the sink will fill image with the first frame
* from the source that is not equal to lastFrameTime. If lastFrameTime is zero, the time of the
* current frame owned by the CvSource is used, and this function will block until the connected
* CvSource provides a new frame.
*
* @param frame The frame object in which to store the image.
* @param lastFrameTime Timestamp of the last frame - used to compare new frames against, or 0 to
* use the current time
* @return Frame time, or 0 on error (call getError() to obtain the error message); the frame time
* is in the same time base as wpi::Now(), and is in 1 us increments.
*/
public long grabFrameNoTimeout(RawFrame frame, long lastFrameTime) {
return CameraServerJNI.grabRawSinkFrame(m_handle, frame, frame.getNativeObj(), lastFrameTime);
}
}
119 changes: 119 additions & 0 deletions cscore/src/test/java/edu/wpi/first/cscore/LastFrameTimeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.

package edu.wpi.first.cscore;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import edu.wpi.first.util.PixelFormat;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.jupiter.api.Test;
import org.opencv.core.CvType;
import org.opencv.core.Mat;

class LastFrameTimeTest {
@Test
void testLastFrameTimeBehavior() throws InterruptedException {
CvSource source = new CvSource("source1", new VideoMode(PixelFormat.kBGR, 320, 240, 30));

CvSink sink = new CvSink("sink1");
sink.setSource(source);

Mat cameraFrame = Mat.zeros(320, 240, CvType.CV_8UC3);

// Make sure no frame was grabbed yet (paranoia)
Mat img = new Mat();
sink.grabFrame(img);
assertTrue(img.empty());

// Add a single frame
source.putFrame(cameraFrame);
var lastTime = source.getLastFrameTime();
assertNotEquals(0, lastTime);

// And if we set lastTime to something different from the frame we just added, it should be
// grabbed right away successfully
assertNotEquals(0, sink.grabFrame(img, lastTime - 1));
assertEquals(cameraFrame.size(), img.size());

// Wait for a "new" frame to show up - this should time out and return 0
assertEquals(0, sink.grabFrame(img, lastTime));

// And another new frame for fun.
source.putFrame(cameraFrame);
var newSourceTime = source.getLastFrameTime();
var newSinkTime = sink.grabFrameNoTimeout(img, lastTime);
assertNotEquals(0, newSinkTime);
assertEquals(newSourceTime, newSinkTime);

{
// Given a thread waiting for a new frame from our sink via the current time
final AtomicLong newNewSinkTime = new AtomicLong(0);
var sinkThread =
new Thread(
() -> {
var t = sink.grabFrameNoTimeout(img);
newNewSinkTime.set(t);
});
sinkThread.start();

// When we put a new frame with a timestamp != lastTime into the source
Thread.sleep(10);
source.putFrame(cameraFrame);
var newNewSourceTime = source.getLastFrameTime();
sinkThread.join();

// Then we see the same image present at the source
assertEquals(newNewSourceTime, newNewSinkTime.get());
}

{
// Given a thread waiting for a new frame from our sink via the last frame time
final long lastFrameTime = source.getLastFrameTime();
final AtomicLong newNewSinkTime = new AtomicLong(0);
var sinkThread =
new Thread(
() -> {
var t = sink.grabFrame(img, lastFrameTime);
newNewSinkTime.set(t);
});
sinkThread.start();

// When we put a new frame with a timestamp != lastTime into the source
Thread.sleep(10);
source.putFrame(cameraFrame);
var newNewSourceTime = source.getLastFrameTime();
sinkThread.join();

// Then we see the same image present at the source
assertEquals(newNewSourceTime, newNewSinkTime.get());
}

{
// Given a thread waiting for a new frame from our sink via the last frame time
final long lastFrameTime = source.getLastFrameTime();
final AtomicLong newNewSinkTime = new AtomicLong(0);
var sinkThread =
new Thread(
() -> {
var t = sink.grabFrame(img, lastFrameTime);
newNewSinkTime.set(t);
});
sinkThread.start();

// When we do not put new frames into the sink
sinkThread.join();

// Then the source reports an error
assertEquals(0, newNewSinkTime.get());
}

sink.close();
source.close();
img.release();
cameraFrame.release();
}
}

0 comments on commit 32a67e4

Please sign in to comment.