Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSDK-7532 - Adding getProperties to vision service #3925

Merged
merged 7 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ require (
go.uber.org/atomic v1.10.0
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.24.0
go.viam.com/api v0.1.293
go.viam.com/api v0.1.296
go.viam.com/test v1.1.1-0.20220913152726-5da9916c08a2
go.viam.com/utils v0.1.77
goji.io v2.0.2+incompatible
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1540,8 +1540,8 @@ go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
go.viam.com/api v0.1.293 h1:BO82qY1mWOZbIjK9kMUyoRv/apdy7O76RT7BNPH29Jo=
go.viam.com/api v0.1.293/go.mod h1:msa4TPrMVeRDcG4YzKA/S6wLEUC7GyHQE973JklrQ10=
go.viam.com/api v0.1.296 h1:CuGF7IVLUmVn5cvWvmGuF7TCviZ7iYJKBqABxcb8G4M=
go.viam.com/api v0.1.296/go.mod h1:msa4TPrMVeRDcG4YzKA/S6wLEUC7GyHQE973JklrQ10=
go.viam.com/test v1.1.1-0.20220913152726-5da9916c08a2 h1:oBiK580EnEIzgFLU4lHOXmGAE3MxnVbeR7s1wp/F3Ps=
go.viam.com/test v1.1.1-0.20220913152726-5da9916c08a2/go.mod h1:XM0tej6riszsiNLT16uoyq1YjuYPWlRBweTPRDanIts=
go.viam.com/utils v0.1.77 h1:eI2BzUxf2kILSqPT5GbWw605Z26gKAiJ7wGSqx8OfCw=
Expand Down
20 changes: 20 additions & 0 deletions services/vision/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,26 @@ func protoToObjects(pco []*commonpb.PointCloudObject) ([]*vision.Object, error)
return objects, nil
}

func (c *client) GetProperties(ctx context.Context, extra map[string]interface{}) (*Properties, error) {
ctx, span := trace.StartSpan(ctx, "service::vision::client::GetProperties")
defer span.End()

ext, err := protoutils.StructToStructPb(extra)
if err != nil {
return nil, err
}

resp, err := c.client.GetProperties(ctx, &pb.GetPropertiesRequest{
Name: c.name,
Extra: ext,
})
if err != nil {
return nil, err
}

return &Properties{resp.ClassificationsSupported, resp.DetectionsSupported, resp.ObjectPointCloudsSupported}, nil
}

func (c *client) DoCommand(ctx context.Context, cmd map[string]interface{}) (map[string]interface{}, error) {
ctx, span := trace.StartSpan(ctx, "service::vision::client::DoCommand")
defer span.End()
Expand Down
7 changes: 7 additions & 0 deletions services/vision/colordetector/color_detector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ func TestColorDetector(t *testing.T) {
img, err := rimage.NewImageFromFile(artifact.MustPath("vision/objectdetection/detection_test.jpg"))
test.That(t, err, test.ShouldBeNil)

// Test properties. Should support detections and not classifications or object PCDs
props, err := srv.GetProperties(ctx, nil)
test.That(t, err, test.ShouldBeNil)
test.That(t, props.DetectionSupported, test.ShouldEqual, true)
test.That(t, props.ClassificationSupported, test.ShouldEqual, false)
test.That(t, props.ObjectPCDsSupported, test.ShouldEqual, false)

// Does implement Detections
det, err := srv.Detections(ctx, img, nil)
test.That(t, err, test.ShouldBeNil)
Expand Down
7 changes: 7 additions & 0 deletions services/vision/obstaclesdepth/obstacles_depth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ func TestObstacleDepth(t *testing.T) {
test.That(t, err, test.ShouldBeNil)
test.That(t, srv.Name(), test.ShouldResemble, name)

// Test properties. Should support object PCDs and not detections or classifications
props, err := srv.GetProperties(ctx, nil)
test.That(t, err, test.ShouldBeNil)
test.That(t, props.ObjectPCDsSupported, test.ShouldEqual, true)
test.That(t, props.DetectionSupported, test.ShouldEqual, false)
test.That(t, props.ClassificationSupported, test.ShouldEqual, false)

// Not a detector or classifier
img, err := rimage.NewImageFromFile(artifact.MustPath("vision/objectdetection/detection_test.jpg"))
test.That(t, err, test.ShouldBeNil)
Expand Down
7 changes: 7 additions & 0 deletions services/vision/obstaclesdistance/obstacles_distance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ func TestObstacleDist(t *testing.T) {
img, err := rimage.NewImageFromFile(artifact.MustPath("vision/objectdetection/detection_test.jpg"))
test.That(t, err, test.ShouldBeNil)

// Test properties. Should support object PCDs and not detections or classifications
props, err := srv.GetProperties(context.Background(), nil)
test.That(t, err, test.ShouldBeNil)
test.That(t, props.ObjectPCDsSupported, test.ShouldEqual, true)
test.That(t, props.DetectionSupported, test.ShouldEqual, false)
test.That(t, props.ClassificationSupported, test.ShouldEqual, false)

// Does not implement Detections
_, err = srv.Detections(ctx, img, nil)
test.That(t, err, test.ShouldNotBeNil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ func TestRadiusClusteringSegmentation(t *testing.T) {
test.That(t, err, test.ShouldBeNil)
test.That(t, seg.Name(), test.ShouldResemble, name)

// Test properties. Should support object PCDs and not detections or classifications
props, err := seg.GetProperties(context.Background(), nil)
test.That(t, err, test.ShouldBeNil)
test.That(t, props.ObjectPCDsSupported, test.ShouldEqual, true)
test.That(t, props.DetectionSupported, test.ShouldEqual, false)
test.That(t, props.ClassificationSupported, test.ShouldEqual, false)

// fails on not finding camera
_, err = seg.GetObjectPointClouds(context.Background(), "no_camera", map[string]interface{}{})
test.That(t, err, test.ShouldNotBeNil)
Expand Down
22 changes: 22 additions & 0 deletions services/vision/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,28 @@ func segmentsToProto(frame string, segs []*vision.Object) ([]*commonpb.PointClou
return protoSegs, nil
}

func (server *serviceServer) GetProperties(ctx context.Context,
req *pb.GetPropertiesRequest,
) (*pb.GetPropertiesResponse, error) {
ctx, span := trace.StartSpan(ctx, "service::vision::server::GetProperties")
defer span.End()
svc, err := server.coll.Resource(req.Name)
if err != nil {
return nil, err
}
props, err := svc.GetProperties(ctx, req.Extra.AsMap())
if err != nil {
return nil, err
}

out := &pb.GetPropertiesResponse{
ClassificationsSupported: props.ClassificationSupported,
DetectionsSupported: props.DetectionSupported,
ObjectPointCloudsSupported: props.ObjectPCDsSupported,
}
return out, nil
}

// DoCommand receives arbitrary commands.
func (server *serviceServer) DoCommand(ctx context.Context,
req *commonpb.DoCommandRequest,
Expand Down
34 changes: 33 additions & 1 deletion services/vision/vision.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ type Service interface {

// GetObjectPointClouds returns a list of 3D point cloud objects and metadata from the latest 3D camera image using a specified segmenter.
GetObjectPointClouds(ctx context.Context, cameraName string, extra map[string]interface{}) ([]*viz.Object, error)
// properties
GetProperties(ctx context.Context, extra map[string]interface{}) (*Properties, error)
}

// SubtypeName is the name of the type of service.
Expand Down Expand Up @@ -155,13 +157,22 @@ func FromDependencies(deps resource.Dependencies, name string) (Service, error)
type vizModel struct {
resource.Named
resource.AlwaysRebuild
r robot.Robot // in order to get access to all cameras
r robot.Robot // in order to get access to all cameras
properties Properties
closerFunc func(ctx context.Context) error // close the underlying model
classifierFunc classification.Classifier
detectorFunc objectdetection.Detector
segmenter3DFunc segmentation.Segmenter
}

// Properties returns various information regarding the current vision service,
// specifically, which vision tasks are supported by the resource.
type Properties struct {
ClassificationSupported bool
DetectionSupported bool
ObjectPCDsSupported bool
}

// NewService wraps the vision model in the struct that fulfills the vision service interface.
func NewService(
name resource.Name,
Expand All @@ -175,9 +186,22 @@ func NewService(
return nil, errors.Errorf(
"model %q does not fulfill any method of the vision service. It is neither a detector, nor classifier, nor 3D segmenter", name)
}

p := Properties{false, false, false}
if cf != nil {
p.ClassificationSupported = true
}
if df != nil {
p.DetectionSupported = true
}
if s3f != nil {
p.ObjectPCDsSupported = true
}

return &vizModel{
Named: name.AsNamed(),
r: r,
properties: p,
closerFunc: c,
classifierFunc: cf,
detectorFunc: df,
Expand Down Expand Up @@ -283,6 +307,14 @@ func (vm *vizModel) GetObjectPointClouds(ctx context.Context, cameraName string,
return vm.segmenter3DFunc(ctx, cam)
}

// GetProperties returns a Properties object that details the vision capabilities of the model.
func (vm *vizModel) GetProperties(ctx context.Context, extra map[string]interface{}) (*Properties, error) {
_, span := trace.StartSpan(ctx, "service::vision::GetProperties::"+vm.Named.Name().String())
defer span.End()

return &vm.properties, nil
}

func (vm *vizModel) Close(ctx context.Context) error {
if vm.closerFunc == nil {
return nil
Expand Down
12 changes: 12 additions & 0 deletions testutils/inject/vision_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type VisionService struct {
n int, extra map[string]interface{}) (classification.Classifications, error)
// segmentation functions
GetObjectPointCloudsFunc func(ctx context.Context, cameraName string, extra map[string]interface{}) ([]*viz.Object, error)
GetPropertiesFunc func(ctx context.Context, extra map[string]interface{}) (*vision.Properties, error)
DoCommandFunc func(ctx context.Context,
cmd map[string]interface{}) (map[string]interface{}, error)
CloseFunc func(ctx context.Context) error
Expand Down Expand Up @@ -92,6 +93,17 @@ func (vs *VisionService) GetObjectPointClouds(
return vs.GetObjectPointCloudsFunc(ctx, cameraName, extra)
}

// GetProperties calls the injected GetProperties or the real variant.
func (vs *VisionService) GetProperties(
ctx context.Context,
extra map[string]interface{},
) (*vision.Properties, error) {
if vs.GetPropertiesFunc == nil {
return vs.Service.GetProperties(ctx, extra)
}
return vs.GetPropertiesFunc(ctx, extra)
}

// DoCommand calls the injected DoCommand or the real variant.
func (vs *VisionService) DoCommand(ctx context.Context,
cmd map[string]interface{},
Expand Down
Loading