Skip to content

Commit

Permalink
RSDK-9188 - Add toggle for fake cam intrinsic & distortion props (via…
Browse files Browse the repository at this point in the history
  • Loading branch information
seanavery authored Nov 1, 2024
1 parent 9c30b69 commit b8d7747
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 145 deletions.
74 changes: 21 additions & 53 deletions components/camera/fake/camera.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,20 @@ func NewCamera(
if paramErr != nil {
return nil, paramErr
}
resModel, width, height := fakeModel(newConf.Width, newConf.Height)
width := initialWidth
if newConf.Width > 0 {
width = newConf.Width
}
height := initialHeight
if newConf.Height > 0 {
height = newConf.Height
}
var resModel *transform.PinholeCameraModel
if newConf.Model {
resModel = fakeModel(width, height)
} else {
resModel = nil
}
cancelCtx, cancelFn := context.WithCancel(context.Background())
cam := &Camera{
ctx: cancelCtx,
Expand Down Expand Up @@ -107,6 +120,7 @@ type Config struct {
Height int `json:"height,omitempty"`
Animated bool `json:"animated,omitempty"`
RTPPassthrough bool `json:"rtp_passthrough,omitempty"`
Model bool `json:"model,omitempty"`
}

// Validate checks that the config attributes are valid for a fake camera.
Expand Down Expand Up @@ -147,60 +161,14 @@ var fakeDistortion = &transform.BrownConrady{
TangentialP2: 0.19969297,
}

func fakeModel(width, height int) (*transform.PinholeCameraModel, int, int) {
fakeModelReshaped := &transform.PinholeCameraModel{
PinholeCameraIntrinsics: fakeIntrinsics,
func fakeModel(width, height int) *transform.PinholeCameraModel {
intrinsics := *fakeIntrinsics
intrinsics.Width = width
intrinsics.Height = height
return &transform.PinholeCameraModel{
PinholeCameraIntrinsics: &intrinsics,
Distortion: fakeDistortion,
}
switch {
case width > 0 && height > 0:
widthRatio := float64(width) / float64(initialWidth)
heightRatio := float64(height) / float64(initialHeight)
intrinsics := &transform.PinholeCameraIntrinsics{
Width: int(float64(fakeIntrinsics.Width) * widthRatio),
Height: int(float64(fakeIntrinsics.Height) * heightRatio),
Fx: fakeIntrinsics.Fx * widthRatio,
Fy: fakeIntrinsics.Fy * heightRatio,
Ppx: fakeIntrinsics.Ppx * widthRatio,
Ppy: fakeIntrinsics.Ppy * heightRatio,
}
fakeModelReshaped.PinholeCameraIntrinsics = intrinsics
return fakeModelReshaped, width, height
case width > 0 && height <= 0:
ratio := float64(width) / float64(initialWidth)
intrinsics := &transform.PinholeCameraIntrinsics{
Width: int(float64(fakeIntrinsics.Width) * ratio),
Height: int(float64(fakeIntrinsics.Height) * ratio),
Fx: fakeIntrinsics.Fx * ratio,
Fy: fakeIntrinsics.Fy * ratio,
Ppx: fakeIntrinsics.Ppx * ratio,
Ppy: fakeIntrinsics.Ppy * ratio,
}
fakeModelReshaped.PinholeCameraIntrinsics = intrinsics
newHeight := int(float64(initialHeight) * ratio)
if newHeight%2 != 0 {
newHeight++
}
return fakeModelReshaped, width, newHeight
case width <= 0 && height > 0:
ratio := float64(height) / float64(initialHeight)
intrinsics := &transform.PinholeCameraIntrinsics{
Width: int(float64(fakeIntrinsics.Width) * ratio),
Height: int(float64(fakeIntrinsics.Height) * ratio),
Fx: fakeIntrinsics.Fx * ratio,
Fy: fakeIntrinsics.Fy * ratio,
Ppx: fakeIntrinsics.Ppx * ratio,
Ppy: fakeIntrinsics.Ppy * ratio,
}
fakeModelReshaped.PinholeCameraIntrinsics = intrinsics
newWidth := int(float64(initialWidth) * ratio)
if newWidth%2 != 0 {
newWidth++
}
return fakeModelReshaped, newWidth, height
default:
return fakeModelReshaped, initialWidth, initialHeight
}
}

// Camera is a fake camera that always returns the same image.
Expand Down
133 changes: 41 additions & 92 deletions components/camera/fake/camera_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,71 +15,8 @@ import (
"go.viam.com/rdk/components/camera/rtppassthrough"
"go.viam.com/rdk/logging"
"go.viam.com/rdk/resource"
"go.viam.com/rdk/rimage/transform"
)

//nolint:dupl
func TestFakeCameraHighResolution(t *testing.T) {
model, width, height := fakeModel(1280, 720)
cancelCtx, cancelFn := context.WithCancel(context.Background())
camOri := &Camera{
ctx: cancelCtx, cancelFn: cancelFn,
Named: camera.Named("test_high").AsNamed(), Model: model, Width: width, Height: height,
}
src, err := camera.NewVideoSourceFromReader(context.Background(), camOri, model, camera.ColorStream)
test.That(t, err, test.ShouldBeNil)
cameraTest(t, src, 1280, 720, 921600, model.PinholeCameraIntrinsics, model.Distortion)
// (0,0) entry defaults to (1280, 720)
model, width, height = fakeModel(0, 0)
cancelCtx2, cancelFn2 := context.WithCancel(context.Background())
camOri = &Camera{
ctx: cancelCtx2, cancelFn: cancelFn2,
Named: camera.Named("test_high_zero").AsNamed(), Model: model, Width: width, Height: height,
}
src, err = camera.NewVideoSourceFromReader(context.Background(), camOri, model, camera.ColorStream)
test.That(t, err, test.ShouldBeNil)
cameraTest(t, src, 1280, 720, 921600, model.PinholeCameraIntrinsics, model.Distortion)
}

func TestFakeCameraMedResolution(t *testing.T) {
model, width, height := fakeModel(640, 360)
cancelCtx, cancelFn := context.WithCancel(context.Background())
camOri := &Camera{
ctx: cancelCtx, cancelFn: cancelFn,
Named: camera.Named("test_high").AsNamed(), Model: model, Width: width, Height: height,
}
src, err := camera.NewVideoSourceFromReader(context.Background(), camOri, model, camera.ColorStream)
test.That(t, err, test.ShouldBeNil)
cameraTest(t, src, 640, 360, 230400, model.PinholeCameraIntrinsics, model.Distortion)
err = src.Close(context.Background())
test.That(t, err, test.ShouldBeNil)
}

//nolint:dupl
func TestFakeCameraUnspecified(t *testing.T) {
// one unspecified side should keep 16:9 aspect ratio
// (320, 0) -> (320, 180)
model, width, height := fakeModel(320, 0)
cancelCtx, cancelFn := context.WithCancel(context.Background())
camOri := &Camera{
ctx: cancelCtx, cancelFn: cancelFn,
Named: camera.Named("test_320").AsNamed(), Model: model, Width: width, Height: height,
}
src, err := camera.NewVideoSourceFromReader(context.Background(), camOri, model, camera.ColorStream)
test.That(t, err, test.ShouldBeNil)
cameraTest(t, src, 320, 180, 57600, model.PinholeCameraIntrinsics, model.Distortion)
// (0, 180) -> (320, 180)
model, width, height = fakeModel(0, 180)
cancelCtx2, cancelFn2 := context.WithCancel(context.Background())
camOri = &Camera{
ctx: cancelCtx2, cancelFn: cancelFn2,
Named: camera.Named("test_180").AsNamed(), Model: model, Width: width, Height: height,
}
src, err = camera.NewVideoSourceFromReader(context.Background(), camOri, model, camera.ColorStream)
test.That(t, err, test.ShouldBeNil)
cameraTest(t, src, 320, 180, 57600, model.PinholeCameraIntrinsics, model.Distortion)
}

func TestFakeCameraParams(t *testing.T) {
// test odd width and height
cfg := &Config{
Expand All @@ -96,35 +33,6 @@ func TestFakeCameraParams(t *testing.T) {
test.That(t, err, test.ShouldNotBeNil)
}

func cameraTest(
t *testing.T,
cam camera.VideoSource,
width, height, points int,
intrinsics *transform.PinholeCameraIntrinsics,
distortion transform.Distorter,
) {
t.Helper()
stream, err := cam.Stream(context.Background())
test.That(t, err, test.ShouldBeNil)
img, _, err := stream.Next(context.Background())
test.That(t, err, test.ShouldBeNil)
test.That(t, img.Bounds().Dx(), test.ShouldEqual, width)
test.That(t, img.Bounds().Dy(), test.ShouldEqual, height)
pc, err := cam.NextPointCloud(context.Background())
test.That(t, err, test.ShouldBeNil)
test.That(t, pc.Size(), test.ShouldEqual, points)
prop, err := cam.Properties(context.Background())
test.That(t, err, test.ShouldBeNil)
test.That(t, prop.IntrinsicParams, test.ShouldResemble, intrinsics)
if distortion == nil {
test.That(t, prop.DistortionParams, test.ShouldBeNil)
} else {
test.That(t, prop.DistortionParams, test.ShouldResemble, distortion)
}
err = cam.Close(context.Background())
test.That(t, err, test.ShouldBeNil)
}

func TestCameraValidationAndCreation(t *testing.T) {
attrCfg := &Config{Width: 200000, Height: 10}
cfg := resource.Config{
Expand Down Expand Up @@ -251,3 +159,44 @@ func TestRTPPassthrough(t *testing.T) {
test.That(t, camera.Close(context.Background()), test.ShouldBeNil)
})
}

func TestPropertiesToggle(t *testing.T) {
// Test fake camera without setting model
// IntrinsicParams and DistortionParams Properties should be nil
ctx := context.Background()
cfg1 := resource.Config{
Name: "test1",
API: camera.API,
Model: Model,
ConvertedAttributes: &Config{},
}
cam1, err := NewCamera(ctx, nil, cfg1, logging.NewTestLogger(t))
test.That(t, err, test.ShouldBeNil)
test.That(t, cam1, test.ShouldNotBeNil)
propsRes1, err := cam1.Properties(ctx)
test.That(t, err, test.ShouldBeNil)
test.That(t, propsRes1, test.ShouldNotBeNil)
test.That(t, propsRes1.IntrinsicParams, test.ShouldBeNil)
test.That(t, propsRes1.DistortionParams, test.ShouldBeNil)
test.That(t, cam1.Close(ctx), test.ShouldBeNil)

// Test fake camera with model set to true
// IntrinsicParams and DistortionParams Properties should be set
cfg2 := resource.Config{
Name: "test2",
API: camera.API,
Model: Model,
ConvertedAttributes: &Config{
Model: true,
},
}
cam2, err := NewCamera(ctx, nil, cfg2, logging.NewTestLogger(t))
test.That(t, err, test.ShouldBeNil)
test.That(t, cam2, test.ShouldNotBeNil)
propsRes2, err := cam2.Properties(ctx)
test.That(t, err, test.ShouldBeNil)
test.That(t, propsRes2, test.ShouldNotBeNil)
test.That(t, propsRes2.IntrinsicParams, test.ShouldNotBeNil)
test.That(t, propsRes2.DistortionParams, test.ShouldNotBeNil)
test.That(t, cam2.Close(ctx), test.ShouldBeNil)
}

0 comments on commit b8d7747

Please sign in to comment.