-
Notifications
You must be signed in to change notification settings - Fork 114
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-9132] Add (Get)Image to the camera interface #4487
Conversation
Warning your change may break code samples. If your change modifies any of the following functions please contact @viamrobotics/fleet-management. Thanks!
|
I'm gonna be out next week but this is ready for a first pass review. Also lmk if we should add Nick or/(and?) Dan to this review. Perhaps for future reviews that are a bit more invasive? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're breaking go modules by adding another method to the interface, we should make that method follow 1:1 the inputs and returns of the proto api.
This will mean bookkeeping the release
of each image stream,should you choose to use ReadImage. This will definitely go away once we get the image from the underlying comms connection like we do in the intel realsense and oak-d camera, I don't know how intense that is.
If changing the returns from GetImage to just be 1:1 the API type cause more problems in changing code outside of camera, we can focus on replacements in the camera packages, and let datamanger and vision packages use Stream and Next for this pr, but I think you're nearly there.
components/camera/client.go
Outdated
@@ -228,6 +228,10 @@ func (c *client) Stream( | |||
return stream, nil | |||
} | |||
|
|||
func (c *client) GetImage(ctx context.Context) (image.Image, func(), error) { | |||
return c.Read(ctx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we Read
and handle release so we do not need to include release func in returns? (perhaps that would make sense in the videosourcewrapper layer).
As mentioned, this would be a larger departure from Stream.Next which assuming will cause major problems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm gonna try to get rid of release in camera components and see what happens
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we just need to move the rimage.EncodeImage
step one level in i.e. outBytes
should come out of the Image
call instead of being handled in the data collector or camera server/client. Currently release
is called in the collector/server/client, so we should just move it into the Image
implementation. Same logic, just handled more nested in our abstraction.
I guess future module writers should get used to using rimage
to encode their output i.e. read from the source bytes and output a newly constructed []byte
result? Does that sound okay to everyone?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we just need to move the rimage.EncodeImage step one level in i.e. outBytes should come out of the Image call instead of being handled in the data collector or camera server/client.
In the client, I think that makes sense to return rimage
to match the python sdk functionality -- avoid unnecessary decodes if you just want bytes.
I guess future module writers should get used to using rimage to encode their output i.e. read from the source bytes and output a newly constructed []byte result? Does that sound okay to everyone?
Would this be similar to viamimage
in the python sdk, i think that works pretty well.
Looks like the server is already handling release and encoding for the caller.
Are you also suggesting removing encoding step in server GetImage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is my WIP in server.go
switch castedCam := cam.(type) {
case ReadImager:
// RSDK-8663: If available, call a method that reads exactly one image. The default
// `ReadImage` implementation will otherwise create a gostream `Stream`, call `Next` and
// `Close` the stream. However, between `Next` and `Close`, the stream may have pulled a
// second image from the underlying camera. This is particularly noticeable on camera
// clients. Where a second `GetImage` request can be processed/returned over the
// network. Just to be discarded.
// RSDK-9132(sean yu): In addition to what Dan said above, ReadImager is important
// for camera components that rely on the `release` functionality provided by gostream's `Read`
// such as viamrtsp.
// (check that this comment is 100% true before code review then delete this paranthetical statement)
img, release, err := castedCam.Read(ctx)
if err != nil {
return nil, err
}
defer func() {
if release != nil {
release()
}
}()
actualMIME, _ := utils.CheckLazyMIMEType(req.MimeType)
resp.MimeType = actualMIME
outBytes, err := rimage.EncodeImage(ctx, img, req.MimeType)
if err != nil {
return nil, err
}
resp.Image = outBytes
default:
imgBytes, mimeType, err := cam.Image(ctx, req.MimeType, ext)
if err != nil {
return nil, err
}
actualMIME, _ := utils.CheckLazyMIMEType(mimeType)
resp.MimeType = actualMIME
resp.Image = imgBytes
}
So I think yes, in the default case we don't encode anymore since the return type is now bytes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok cool I like that.
I am little confused on why we still need the ReadImager
path here. Shouldnt the camera interface now always have Image
defined so we can just use that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thinking here is since viamrtsp
uses the VideoReaderFunc
and Read
's release
functionality to keep track of pool frames in the frame allocation optimization flow, for the server.go
that serves viamrtsp
, we need to be able to call release()
I think we could refactor viamrtsp
though to just copy out the bytes on return early and use the new .Image
pathway... I'm down to remove ReadImager
entirely as long as we make it a high priority to refactor viamrtsp
to use .Image
and []byte
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, interesting. I think it makes sense to leave in for now for viamrtsp compatibility.
I think we could refactor viamrtsp though to just copy out the bytes on return early and use the new .Image pathway... I'm down to remove ReadImager entirely as long as we make it a high priority to refactor viamrtsp to use .Image and []byte
Yep the whole point to having a memory manager was the issue with passing in a pointer to the avframe in VideoReaderFunc
return causing segfaults with the encoding in the server.
Since we are removing encoding from the server and relying on the user to handle this, we should eventually be able to use Image pathway and simplify things quite a bit : )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All our go cameras will need a refactor for the full fix of this interface, I do not anticipate that we will have the best version of this code and go modules after one pr. Eventually we will get rid of Stream and Next completely. The problem is those videosourcewrapper helpers are all over the place in go modules since they're exported convenience functions.
Always export code conscientiously. But we're breaking this interface anyway, so we'll deal with the following breaks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay 👍 will remove ReadImager in this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering if it makes sense to do a full removal of Read
from client & server, ReadImage
, and ReadImager
here instead of just aliasing in those cases.
Yes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we replace the cemeraservice GetImage call in RenderFrame
with Image
?
func (s *serviceServer) RenderFrame(
ctx context.Context,
req *pb.RenderFrameRequest,
) (*httpbody.HttpBody, error) {
ctx, span := trace.StartSpan(ctx, "camera::server::RenderFrame")
defer span.End()
if req.MimeType == "" {
req.MimeType = utils.MimeTypeJPEG // default rendering
}
resp, err := s.GetImage(ctx, (*pb.GetImageRequest)(req))
serviceServers use the gRPC named version, so GetImage is right |
…t case in client_test.go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM Great work Sean!
I also confirmed that filtered camera still works as expected.
Also, the end to end of syncing camera.ReadImage (for modules). I did not test the transform camera. |
Co-authored-by: nicksanford <[email protected]>
Cool. I'm gonna run it through my own manual test gauntlet and then merge before EOD |
Co-authored-by: nicksanford <[email protected]>
Overview
https://viam.atlassian.net/browse/RSDK-9132
This PR does not remove
Stream
from theCamera
interface or any gostream logic. It just wraps it all in better English that corresponds with the gRPC methodGetImage
. Mostly just adding syntactic sugar overstream.Next
/ReadImage
using a newImage
method (for existing builtins that use gostream), and modifying the camera server and client appropriately to utilizeImage
.My hopes are that this encourages people to never be tempted to use
Stream
in modules or evenVideoReaderFunc
. We will eventually removeStream
from the baseCamera
interfaceIt will also provide a significant performance boost to the
ReadImage
data collector since we no longer have to transcode the oldimage.Image
output into bytes anymore incollector.go
.Summary of changes
Image
to camera interfaceImage
incamera/client.go
,camera/server.go
and data collectorImage
in builtins (webcam, replaypcd, ffmpeg, fake etc.) instead of Stream wrappers in preparation for removing Stream entirely from the camera interfaceNote
Will this break cli module generator? No
Manual Tests
Did this list of manual tests already twice at different points in development, but should re-test after these code reviews.
viamrtsp
h264 with and without passthroughAlso built the viamrtsp module with local replacement off this branch and the stream is working for both rtp passthrough true and false, indicating that videosourcewrapped modules shouldn't be affected by this change.
Profile for performance improvement in data collection
If the PDFs are insanely blurry, try downloading and opening with system viewer
Link
The big thing that Nick noted was alarming was the staggering memory used to alloc
image NewYCbCr
. This is no longer the case on this feature branch.Config used for pprof
Comparative performance from an operator's perspective
Config used
v0.50.0 branch (RPI4):
Feature branch (RPI4):
v0.50.0 (Orin Nano):
Feature branch (Orin Nano):
RPI4 memory improvement: I noticed that off the feature branch, memory stabilized around 1.9gb, while on the main branch, memory stabilized around 2.5gb
RPI4 CPU improvement: I learned how to filter for processes in this comparison. It's quite clear that the CPU usage is lower as all figures are lower across the board for the feature branch.
Orin Nano CPU improvement: Though the core usage was about the same between main and feature, I noticed that on the latest release, the viam-server processes were always taking the most CPU. Off the feature branch, viamrtsp were always on top.
I did not notice memory improvements on the Orin Nano. May be missing something. Operator skill issue perhaps