Skip to content

Conversation

@Andon-A
Copy link
Contributor

@Andon-A Andon-A commented Sep 14, 2020

Adds a function called save_image to allow for, unsurprisingly, saving the image using only the library. Then added a function called take_and_save that will take the image, save it, and then turn the camera back to video mode. Both examples are updated to take advantage of this.

take_and_save takes three arguments: Filename (Surprising, I know), overwrite, and image size. While I've left a lot of explanations and things, I've taken a picture with four lines of code:

import adafruit_vc0706 vc0706 = adafruit_vc0706.VC0706(board.UART()) vc0706.take_and_save("quicktest.jpg", True, adafruit_vc0706.IMAGE_SIZE_640x480) 

Nothing in the library has been re-named or even modified, only added to, so existing code shouldn't be affected. I've tested with no issues on CircuitPython and Windows via a FTDI connector.

Added a save_img function and take_and_save function to the library, and updated the examples to match. This puts all the saving right into the library, as it was the same across all platforms. Should work just fine with any existing code. But new code can just use a single function to take *and* save a picture!
"board" doesn't need to be imported for PC/Pi versions. Also left a note for modifying serial ports for PC version. This will require some minor changes to the CP part of the learn documentation.
Condensed all the switcher stuff into a single switcher.
Allows for even simpler image taking.
@tannewt
Copy link
Member

tannewt commented Sep 14, 2020

Please checkout this PR where we're discussing a camera API: adafruit/circuitpython#3369 It'd be good to have the same API for this. (I'd love your feedback too since you are currently using the camera.) Thanks!

@Andon-A
Copy link
Contributor Author

Andon-A commented Sep 14, 2020

I will have a look in a bit! A unified camera deal would be great, but a lot this sort of stuff is out of my league.

@Andon-A
Copy link
Contributor Author

Andon-A commented Sep 14, 2020

Implementing such an API is, well. At like, floor 20, there's no elevators, and I really prefer to be in the park and occasionally poke my head in the lobby for a snack.

To put in other terms: It requires a lot of knowledge I don't have and, to be honest, don't particularly care to learn. I don't like programming much. It seems like a really cool idea, there's some camera-specific issues maybe unless I missed things, but it will not be I that implements it here right now.

@tannewt
Copy link
Member

tannewt commented Sep 15, 2020

I don't expect you to change the whole api. Instead I'd like take_and_save be replaced by a compatible take_picture that matches what CircuitPython's camera will have. Are you willing to do that?

@Andon-A
Copy link
Contributor Author

Andon-A commented Sep 15, 2020

The only reason I didn't use take_picture in the first place is it's already used and I didn't want to break existing code, so I can do that easily by renaming things. With the discussion of the API and understanding it more, I can easily match that with no issue.

@Andon-A
Copy link
Contributor Author

Andon-A commented Sep 15, 2020

Well. I think no issues.

@tannewt
Copy link
Member

tannewt commented Sep 16, 2020

The only reason I didn't use take_picture in the first place is it's already used and I didn't want to break existing code, so I can do that easily by renaming things. With the discussion of the API and understanding it more, I can easily match that with no issue.

Ah, bummer! I think we should just hold off on unifying the API. We can merge this as-is and follow up with a big restructure when we want them to be the same.

@Andon-A
Copy link
Contributor Author

Andon-A commented Sep 16, 2020

I could potentially restructure take_and_save to follow the same form, but still he called take_and_save (or something better), to make unification easier when it does happen?

@tannewt
Copy link
Member

tannewt commented Sep 17, 2020

@Andon-A Ya, making it take the same arguments as the future take_picture could help. We could even add a comment to take_picture recommending using the newer function. Then, when we do change it we say rename take_and_save to take_picture.

@Andon-A
Copy link
Contributor Author

Andon-A commented Sep 17, 2020

Further thinking on this:

While we could use various methods to make take_picture function in multiple ways (arguments, constructor variable, etc), but what about leaving the entire class as it is now, but making a new class for the compatibility, say VC0706_Camera or something? Old code would default to the old class (which could eventually be removed when it comes time to clean things up), but the new class would be made from the ground up to be in line with the API?

Also, the major thing I am not sure of is the buffer. What exactly is it supposed to be - A variable that's been set aside for use? I THINK the M4 boards have enough ram for this camera but larger images definitely not, and it would infringe on other things so I'm not sure.

@Andon-A
Copy link
Contributor Author

Andon-A commented Sep 17, 2020

Oh, also. Would you be available this weekend sometime when I actually go and poke at this? I can hit you up on Discord or wherever for specific things

@tannewt
Copy link
Member

tannewt commented Sep 18, 2020

While we could use various methods to make take_picture function in multiple ways (arguments, constructor variable, etc), but what about leaving the entire class as it is now, but making a new class for the compatibility, say VC0706_Camera or something? Old code would default to the old class (which could eventually be removed when it comes time to clean things up), but the new class would be made from the ground up to be in line with the API?

That's one way to go. I tend to replace things more than I should. The only thing to consider is the code size of having two different implementations to store in memory. We could split them into two files though.

Also, the major thing I am not sure of is the buffer. What exactly is it supposed to be - A variable that's been set aside for use? I THINK the M4 boards have enough ram for this camera but larger images definitely not, and it would infringe on other things so I'm not sure.

I was thinking it could either be an in-memory buffer, like a bytearray, or a stream, like a file. The latter wouldn't need everything in memory at once.

Oh, also. Would you be available this weekend sometime when I actually go and poke at this? I can hit you up on Discord or wherever for specific things

I'm around. Let's coordinate a time. I try not to be on the computer a ton but can be if we coordinate.

@Andon-A
Copy link
Contributor Author

Andon-A commented Sep 19, 2020

I'm looking at this now, and if it's going to be like you mentioned here, the only thing I'm not sure of how to handle is the format. The VC0706 only returns a jpg, so I want to make it yell if you try something else. But I'm not sure what to check against for that, especially without having the camera module opened as well.

Feel free to poke me here or on Discord whenever's handy for you, I'm basically going to be here all weekend

Split into a legacy (doesn't break code) class and a VC0706Camera class that is intended to be in line with the camera API. Some tweaks to make Pylint happy. Left in the save_image bit to the legacy version, so there's an easy way to save files there (Although take_and_save is gone). Camera version doesn't use fileformat currently because I'm not sure how it should be checked. It's not super important since the camera only suports one. I've had some issues changing between image resolutions on the camera, but that's likely something specifically with the camera.
Both classes: Adds a reset() function, which resets the camera and resets the baudrate to whatever it was previously. This seems to be required in order to change the camera image size on the one I have, at least. This required adding an internal set_baudrate. External: Added a get_image_size which converts width and height arguments into the proper const. Added some retries for reading the buffer for VC0706Camera's take_picture. Added the "Failed to halt frame!" to the stop_video instead of having it external. Still need to handle that filetype.
@Andon-A
Copy link
Contributor Author

Andon-A commented Sep 20, 2020

So the updates have done largely what I've said I was going to do. Still not sure how to handle that format, but otherwise it works. Had to fiddle with a few other things to get stuff to work well, but it's all in the commit notes.

Examples need to be updated still. And the learn guide will need an update, too.

Added a get_image_size to the VC0706Camera class, that returns (Width, Height) tuple. Updated the examples to use the VC0706Camera class. The filetype will default to JPG so they shouldn't need to be updated once that's done (But we'll see)
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I didn't see this over the weekend. Please mention me on Discord to coordinate. I don't read email until Monday.

Stripped out additions to VC0706 class, changed VC0706Camera to Camera, used "with" with examples, changed sizes to a dict use, Removed extraneous file.
Issues with file saves meant this never got into the prior committ. Removes new things from VC0706 class. Changes get_image_size to a dict Changes VC0706Camera class to just Camera
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've suggested removing some APIs and marking some private. The new class should be smaller and simpler ideally.

Let me know if you'd like me to reorganize it. I'll need your help testing though because I don't have a camera.

Comment on lines +336 to +353
def set_baudrate(self, baud):
"""As above. For internal use."""
divider = None
if baud == 9600:
divider = _BAUDRATE_9600
elif baud == 19200:
divider = _BAUDRATE_19200
elif baud == 38400:
divider = _BAUDRATE_38400
elif baud == 57600:
divider = _BAUDRATE_57600
elif baud == 115200:
divider = _BAUDRATE_115200
else:
raise ValueError("Unsupported baud rate")
args = [0x03, 0x01, (divider >> 8) & 0xFF, divider & 0xFF]
self._run_command(_SET_PORT, bytes(args), 7)
self._uart.baudrate = baud
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def set_baudrate(self, baud):
"""As above. For internal use."""
divider = None
if baud == 9600:
divider = _BAUDRATE_9600
elif baud == 19200:
divider = _BAUDRATE_19200
elif baud == 38400:
divider = _BAUDRATE_38400
elif baud == 57600:
divider = _BAUDRATE_57600
elif baud == 115200:
divider = _BAUDRATE_115200
else:
raise ValueError("Unsupported baud rate")
args = [0x03, 0x01, (divider >> 8) & 0xFF, divider & 0xFF]
self._run_command(_SET_PORT, bytes(args), 7)
self._uart.baudrate = baud
Comment on lines +378 to +399
def set_img_size(self, size):
"""Literally the above, but for use internally."""
if size not in (IMAGE_SIZE_640x480, IMAGE_SIZE_320x240, IMAGE_SIZE_160x120):
raise ValueError(
"Size must be one of IMAGE_SIZE_640x480, IMAGE_SIZE_320x240, or "
"IMAGE_SIZE_160x120!"
)
return self._run_command(
_WRITE_DATA, bytes([0x05, 0x04, 0x01, 0x00, 0x19, size & 0xFF]), 5
)

def get_image_size(self):
"""Get the current image size. Will return a WIDTH, HEIGHT tuple."""
size = self.image_size
if size == 0x00:
size = (640, 480)
elif size == 0x11:
size = (320, 240)
elif size == 0x22:
size = (160, 120)
return size

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def set_img_size(self, size):
"""Literally the above, but for use internally."""
if size not in (IMAGE_SIZE_640x480, IMAGE_SIZE_320x240, IMAGE_SIZE_160x120):
raise ValueError(
"Size must be one of IMAGE_SIZE_640x480, IMAGE_SIZE_320x240, or "
"IMAGE_SIZE_160x120!"
)
return self._run_command(
_WRITE_DATA, bytes([0x05, 0x04, 0x01, 0x00, 0x19, size & 0xFF]), 5
)
def get_image_size(self):
"""Get the current image size. Will return a WIDTH, HEIGHT tuple."""
size = self.image_size
if size == 0x00:
size = (640, 480)
elif size == 0x11:
size = (320, 240)
elif size == 0x22:
size = (160, 120)
return size
# Get the image size const
size = get_image_size(width, height)
# And set it.
self.set_img_size(size)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.set_img_size(size)
self.img_size = size
self.resume_video()
return True

def stop_video(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def stop_video(self):
def _stop_video(self):
raise RuntimeError("Failed to halt frame!")
return attempt

def resume_video(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def resume_video(self):
def _resume_video(self):
"""
return self._run_command(_FBUF_CTRL, bytes([0x1, _RESUMEFRAME]), 5)

def read_picture_into(self, buf):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def read_picture_into(self, buf):
def _read_picture_into(self, buf):
@kattni
Copy link
Contributor

kattni commented Jan 6, 2021

@Andon-A Hey! Thanks for the contribution. Scott suggested a few changes. Are you still interested in working on this?

@Andon-A
Copy link
Contributor Author

Andon-A commented Jan 7, 2021

@kattni Oh gosh, I completely forgot about this. I'll try to find some time to look at it in the upcoming few weeks and remember where I was.

@Andon-A
Copy link
Contributor Author

Andon-A commented Apr 19, 2021

I saw that there's camera support being worked on for CircuitPython. I have no idea when I'll end up working on this again, and as the camera updates may change how things are wanted, I'm just going to close this and come back to it at a later date.

@Andon-A Andon-A closed this Apr 19, 2021
@tannewt
Copy link
Member

tannewt commented Apr 19, 2021

@jepler Food for thought.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants