problem saving video to SD

I had asked earlier about sending video frames from the MV7 to a PC. Decided it would be simpler for now to save video to the SD card and then copy to the PC. But I have had very erratic results, both with GIF and MPEG.

I copied some code from you site and modified it a bit doe debugging:

import sensor, mjpeg, time
# Setup camera.
sensor.reset()
sensor.set_pixformat(sensor.GRAYSCALE)
sensor.set_framesize(sensor.QQQVGA)
sensor.skip_frames()
c = time.clock()
# Create the mjpeg object.
m = mjpeg.Mjpeg("example3.mjpeg")
# Add frames.
for i in range(100):
    print(0, end ='')
    c.tick()
    img = sensor.snapshot()
    print(1, end ='')
    m.add_frame(img)
# Finalize
fps = c.fps()
m.close(fps)
print("\nFPS: {}".format(fps) )

This should print a long string of ones and zeroes: ‘0’ at the start of the loop and ‘1’ between capturing the image and saving it to the MJPEG file. At the end it should print the FPS value. Sometimes it does do this, although the FPS value varies a lot from run to run - typically about 50, 76, or 91 FPS. Does not seem to matter if the frame buffer in the IDE is enabled or disabled. But many times it just prints ‘0’ or ‘010’ and hangs. I can press the run button three times in a row and get two good runs and then a hang. Or once (after a reset) and it runs and then hangs or just hangs immediately.

I am using a tested 15Gb Sandisk Ultra uSD card and have tried two USB cables. I thought that the problem was with the SD card but realized that the program hangs after printing a ‘0’ so it seems it is getting stuck on reading the image. Note that I am using QQQVGA grayscale - very small images. I also noticed that the last five rows (approximately) in many frames have a lot of random pixels.(Is there a way to upload images?) Is it possible that the board is defective or that there is a problem with the QQQVGA mode?

What I really want to do is use pyp.Timer to control the frame rate exactly. I wrote some code for that but could never get it to work at more than 4 fps. The code here is just test code to try to debug the problem. Since the MV7 is hanging on the simple code it is not surprising it has problems with a callback function. So I would greatly appreciate any help here.

Just did a little more testing. Now I am seeing the program hanging after printing either a ‘1’ or a ‘0’. So maybe is another problem entirely?

Also, I tried some of the other small formats: QQCIF, QQSIF, etc. They all show several rows of random pixels at the bottom of the frame, but only about half the times I try it. With QQQCIF there was also severe jitter in the image. Note that these are just results looking at the image in the OpenMV IDE. I grabbed a few using Windows Snipping Tool. Again, is there a way to upload images?

Hi, there’s an issue with the IDE v1.6.0. I set the timeout for waiting for the cmaera way too low trying to fix another issue. Please download a pre-release of the latest IDE here (note that the camera is not crashing bu the IDE disconnects):

http://upload.openmv.io/openmv-ide-windows-1.7.0/openmv-ide-windows-1.7.0.exe

If you want a consistent frame rate you need to limit the FPS by making the loop wait a certain amount of time. For example:

start = pyb.millis()
while pyb.elapsed_millis(start) < record_time:
    clock.tick()
    img = sensor.snapshot()
    # Modify the image if you feel like here...

    mjpeg.add_frame(img)
    print(clock.fps())

Thanks for quick reply! I don’t understand your code sample. Where is the delay in the loop? And shouldn’t the print(clock.fps()) be after the loop rather that inside it?

I just downloaded the new IDE and will give it a try.

Here is what I was thinking as a way to get a precise fps rate using a callback function. Do you think it should work?

import sensor, image, mjpeg
import pyb, time

# Callback function:
def ftick(timer):
    # ftick raises a flag. The main program loop resets the flag.
    global tick, tickerror
    if tick == True:  # tick should always be false when entering callback, otherwise main loop has run for too long.
        tickerror += 1  # accumulate number of overruns for debugging.
    tick = True

tick = False   # This is the timing flag.
tickerror = 0   # Overrun errors
fcount = 0 #current frame
ftotal = 100 #total number of frames to be acquired
frate  = 30 #frame rate (frequency: frames per sec)

sensor.reset()
sensor.set_pixformat(sensor.GRAYSCALE) #grayscale image
sensor.set_framesize(sensor.HQQQVGA)    #60x80 resolution
sensor.skip_frames()
m =mjpeg.Mjpeg("test2.mjpeg")
m = mjpeg.Mjpeg("test1.mjpeg")      #intitiate video save

# Set up call back timer with frequency frate and function ftick.
ftimer = pyb.Timer(4, freq = frate)
ftimer.callback(ftick)

while fcount <= ftotal:
    while tick == False:   # Wait for ftick() to raise flag
        pass
    img1 = sensor.snapshot()  # Then grab image and save it.
    m.add_frame(img1)
    fcount += 1  # Increment frame count.
    tick = False  # Cancel flag.
    print(fcount)  #for debugging.

# Close everything down:
ftimer.callback(None)
m.close(frate)
print(tickerror)

Opps, sorry, the snippet I posted allows you to control the record time precisely. Not the FPS. Note that the clock value in there was just for printing the FPS.

I think this simple example will do the trick:

record_time = 10000 # in ms
record_ms_per_frame = 50 # in ms
start = pyb.millis()
old_time = start
while pyb.elapsed_millis(start) < record_time:
    img = sensor.snapshot()
    mjpeg.add_frame(img)
    new_time = pyb.millis()
    delta_time = new_time - old_time
    old_time = new_time
    if delta_time > 0: pyb.delay(delta_time)

The while loop will run until the record time is exhausted. But, to keep the frame rate constant the inner loop will wait after every frame to make sure enough time has passed. Note that it only slows the FPS down if you’re going very fast.

Again - thanks for the really quick support. Things are working much better with the new IDE.

I tried a modified version of the code you posted in you last message. One problem with that code is that it will always run a little slower than the programmed delay because they call to pyb.delay() comes several instructions later than that for pyb.millis(). I have used code before that uses the total elapsed time rather than the incremental time and I’m trying to encapsulate that into a class. Probably will have a bit of overhead because of Python method calls. Capturing frames at an exact rate can be really important in some applications. Maybe this could be built into the pyb library at some point? I’ll post Python code when I get it working, but I don’t have the skills to write it in C.

One other quick question: The serial window shows a prompt for typed input. But I can only get it to work for output (print functions). Should it be working for input and is there any REPL ability within the IDE?

We can probably add a frame rate limiter to the after next firmware release. Make an enhancement bug on this on the github repo. This is easy to do.

As for text input, we don’t have that. If you want that you have to go into full repl mode using the open terminal feature but in that mode you don’t get all the debug experience.

Um, so, I plan to add a really robust library for doing a lot of input and output in the feature based on some previous work I did. However, I don’t have time to make that a priority right now. So, you’ve just got output but no input.

For what I’m planning Google OmniaCreator. It’s a project that never got popular but I plan to integrate it into OpenMV IDE eventually.

Hello I’m facing a different error using the same code.
I get “OSerror: A hard error occured in the low level disk I/O layer”
Camera firmware: 3.0.0
IDE version: 2.0.0

import sensor, image, time, pyb, mjpeg
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 2000)
m = mjpeg.Mjpeg('openmv_video.mjpeg')
clock = time.clock()
start = pyb.millis()
record_time=5000
while(pyb.elapsed_millis(start) < record_time):
    clock.tick()
    m.add_frame(sensor.snapshot())
#m.close(clock.fps())

Hi, that’s probably due to a disk read/write issue. Um, can you plug the card into your computer and ask the PC to fix disk errors?

Our code is just based on FATFS which is rather picky on drive issues.

Thanks for the quick reply and the awesome diagnosis, it put me on the right track.
It seems micropython only accepts SD card FAT32 formatted as a single unit.

Ah, yeah, we only support FAT32 drives. You basically need linux for anything beyond FAT since the file systems get really complex otherwise.