* The following issues still need fixing:
* Al fb_alloc nlr hooks are DISABLED.
* modnetwork causes cam to hardfault.
* Had to reduce heap by 1K (vfs buffer had to be moved to bss/data).
* self-tests are disabled (cam gets stuck after executing).
Now you can find circles with your OpenMV Cam! The alrogithm can eek out
about 7 FPS on a 160x120 image which is quite impressive given how
computationally expensive circle finding is...
For easy line following mainly. In non-robust mode the line is computed
using least squares. In robust mode the line is computed using the
Theil-Sen median of slopes method. We do not use the Siegel Median of
Medians operation because it costs more CPU time... but, more
importantly there's no way to improve the centroid estimate so even if
the slope is more robust the line will be drawn in the wrong place.
These two new classes allow you to record image data for later viewing
at the same speed the image data was recorded. Unlike GIF/MJPEG the
image data is stored on the file system completely uncompressed in
native frame buffer format making super fast reading and writing
possible. Recording VGA Grayscale at ~13 FPS is possible along with
playing it back. (That's about 30 Mb/s folks).
...
The motivation for writing these scripts is so that you can record video
of something like a line following track, take that video home, and work
on computer vision algorithms for that data.
These classes should make it a lot easier to use the camera at home now.
Moved structs along with image copying code from sensor into
framebuffer.c so that we can use the new copy_fb_to_jpeg_fb() function
in the image library for methods with "copy_to_fb" so that they update
the IDE preview when called.
Also, I noticed that the MAIN_FB_SIZE() value is not calculated
correctly in all cases. Will fix later. Trying to keep this commit clean
for just the refactoring.
All changes have been tested. Too.
With the new frame rate speed increase folks will be asking for smaller
resolutions to get 85 FPS or so when running an algorithm. This commit
adds all scaled modes of frame sizes we already support. We should be
good now on frame sizes for the present and future now.
Todo - skip frames does not run long enough anymore for auto white
balance and gain to stablize before they are turned off in some scripts.
This needs to be adjusted.
Frame rate now can hit 30 FPS when JPEG compression is off. Merging of
lines is perfected too which greatly reduces the noise output. Also,
lines are now objects so you can get their values in an easy way.
We now have a nice and fast malloc system that easily offers 300KB+
dynamic memory... No need to use xalloc anymore except when we're
transfering objects to MP memory space.
The user can now call compressed_for_ide() and compress_for_ide() on an
image to make a jpeg compressed image formatted for transmission over a
data link other than USB. Note that OpenMV IDE will automatically handle
one of these compressed images ending up in the frame buffer and display
it like normal.
To send the image data the user can do:
print(img.compress_for_ide(), end='')
print(img.compressed_for_ide(), end='')
uart.write(img.compress_for_ide())
uart.write(img.compressed_for_ide())
and etc. As mentioned above, compress() compresses the image in place.
And that in place compressed image will then end up in the jpeg buffer.
OpenMV IDE will automatically handling decoding these special compressed
images when this happens.
All variations of the above code have been tested and are working.
Main ZBar code, breaking the commit up because the main file is big.
I will refeactor UMM alloc out of apriltag.c and zbar.c once I'm
finished with this commit stream.
ZBar integration gives us support for basically all 1D linear barcodes.
* Delay the FB size check and corrections to snapshot(). If the frame doesn't
fit FB it gets cropped for GS, or the sensor is switched to bayer for RGB.
Everything works. Running out of memory is fixed and the rotation value
is valid now. For 320x240 operation on the STM32H7 we're going to need
on the order of 1 MB in the entire frame buffer. The code is designed to
handle us getting this amount of memory without any new changes for
320x240 support.
This file includes all of the relevant header/source files from the
april tag library merged into one big file. Additionally, it also
includes heap/quicksort code. I've done the work of going through
the april tag library line by line and fixing it to use fb_alloc,
floats, and our fast math functions.
Anyway, I'm sending this massive file by itself first since it's so
big. Note that we migh in the future want to pull things out of this
file for our own use later if we need linear algebra support.
I also tested the firmware for about an hour to make sure there was no
stack leak.
Note that I prefer for fb_free() to still be called versus
fb_free_till_mark() doing that for you in the code.
For functions without this fix they will just free the entire fb_alloc
stack when an exception happens. For functions with this fix they will
only free up to and including the mark. Since there are no places in the
firmware where you could start building a second fb_alloc stack when one
is already in place this point is moot currently. But, if we do
something like that in the future the problem will have already been
solved.
Any new code or re-worked code should use the mark function.
Speed up the algorithm by fixing the abs() issue. Do not use that
function in any of your code. It by itself cut the speed of the code
in half. I don't know what's in that function but I'm guessing it does
ABS of a float using ints or something.
I made the zoom parameter functional now too so you can use lens_corr to
zoom in on the image. Argument parsing is handled too. Finally, I
updated the only script where this is used.
Note that I'm able to get more than 10 FPS at 160x120 on the M4 and 15
FPS at 160x120 on the M7. Previous this was at about 5 FPS and 7.5 FPS
respectively.
* Detect when VBUS is connected and wait for enumeration, the IDE
timeout is only started after enumeration.
* A 2s timeout for enumeration is used so the cam doesn't get stuck
if it's connected to a charger or a power bank.
We now have a method to get an the normalized histogram of an image
patch. The histogram is returned as an object with methods too. You can
then get the stats off of the histogram or just get the CDF of it. The
CDF is particularly useful for automatically chaning the the color
tracking bounds.
* This function filters keypoints far from the centroid, it's very useful for finding an accurate bounding box for an object.
If a bounding box for the object is not needed, the centroid can be used instead since it's not affected too much by outliers.
* The filter finds the centroid of all the previously cross-matched keypoints then finds the mean, variance and standard deviation,
it then filters keypoints with a distance higher than standard deviation from the centroid.
The new API is backwards compatible with the previous one except for
advanced features. The new blob code uses a flood fill algorithm that is
3x faster in filling out blobs that the previous code. On the M7 the
performance cap of 30 FPS is usually reached.
Additionally, blobs are objects with named attributes now so you don't
have to index access them anymore. However, index access is still
supported.
* Added pooling functions to make getting small images easy. set_binning
works too... but, it zooms in way to much. pooling functions aout you to
shrink the image while not zooming in.
* To make the pooling functions easy to use I created a version that
pools the image out of place and one that pools the image in place. The
inplace pooling function can work on the frame buffer (see edits to
sensor.c)
* I added the code to do hann windowing to the FFT lib. However, I
commented it out after it improved performance by basically zero.
Specialized windowing stuff will only come in handy for folks trying to
tune their algorithm... not in general for everything.
* I added subpixel resolution for the phase correlation code. You can
now track the image movement really precisely. Additionally, I fixed up
the displacement outputs to give expected results. I also added a QoR
output for the displacement code so that you can know when the results
are bad.
* Finally, an example script has been added to show off the features.
There were some mistakes, they are fixed now. FFT 1D and 2D work
flawlessly. No problems with that code anymore.
As for phase correlation I need to study how to interpret the output
better. The function generates noisy results once you move the image too
far and I'm not quite sure if I have the code right for detecting
positive and negative displacements.
The heart of the 1D FFT works. I tested this on the PC. However, 2D FFTs
may have issues and the phase correlation algorithm does not generate
the expected results. That said, most of the work is done. Stuff just
needs to be deubgged.
The FFT lib is designed to handle up to 1024 point real FFTs and 512
complex FFTs. As for 2D FFTs, we can do up to 64x64 pixels. After which,
we don't have enough RAM to handle them because they use up about 128KB
each.
Things to do... the 2D FFT needs to be verified. So, we need to run an
image through it and then back again to verify that there are no
problems. Then we need to compare the 2D FFT output with another 2D FFT
algorithm on the PC...
Once the FFTs are known to be good we then need to make sure the phase
corelation algorithm outs the correct results. We need to test that with
multiple shifted images, etc.
Added the ability to turn AGC off. Kinda will need the ability to restore
AGC settings back to user specified ones in the future... but, this will
do for now.
Added the ability to turn AEC off. Objectively this function probably
won't be used. But, in low light situations it can help.
Added get_fb() to allow you to get the last image snapshot returned.
There was some old exposure function in the code that was getting
optimized out. So, I deleted the used methods that didn't have any code
in them and commented out the only method that did.
* Removed some unused descriptors, but mainly set the CDC interface number to (1)
same as MP, as Windows doesn't like different interface numbers for the same device.
Finished going through imlib.c.
-> Histeq uses fb_alloc now and has hook for RGB histeq when reserve YUV
LUT is added (coming soon in next PR).
Cleanuped py_helper.c/h
-> No functional changes. Just added some header info.
Finished going through py_image.c
* 1 - Finished general code cleanup and updating everything to using new
library functions. In particular, I updated the remaining find_*
functions with the new roi clipping code when they accept rois.
* 2 - Made blob stuff return a list when nothing is found so you don't
have to do an if on the returned value anymore.
* 3 - img subscr is more powerful now allowing image reading and
writing. I updated this because I had to use it to find a previous bug
with socket.send() for the WINC driver.
* 4 - Renamed find_eyes to find_eye. Because it just finds one eye.
* 5 - Other than that just general code cleanup to make functions look
consistent.
And yes, changes have been test. Face tracking, eye tracking, keypoints,
etc. all work still.
Future things todo before release:
1 - Change all LAB stuff to YUV.
2 - Add in reverse YUV->RGB LUT and update functions like Mode() to use
this so they don't generate messed up outputs, also histeq() too.
3 - Add any remaining sensor control functions like agc control.
* Added the ability to control the quality on JPEG functions... However,
due to our JPEG implementation this doesn't seem to help. 90% JPEG
quality images and regular images should be about equal. But, you can
see heavy degredation with 90% still. E.g. text is unreabable. Not
exactly sure why this is happening but it can be fixed later.
* Changed the compress() function to compressed(). Also, it now
compresses using FB_Alloc to prevent realloc issues when compressing.
* Added new compress() function. This function compresses an image in
place and if that image is the frame bufffer then it will update the
frame buffer bpp value to reflect the image was compressed. Users can use
this function to basically finalize the frame buffer and then pass the FB
to functions that need to send image bytes. The benefit of using this
function is that it should allow higher quality JPEGs and let everything
run at a faster speed while connected to the IDE.
I made this function to speed up WiFi. However, I encountered a bug with
the winc.send() method. It appears to zero the bytes it sends. I didn't
debug further except to verify that the image data became zero after
calling send.
*Changed subimg to copy.
*Made blend work the same way as all our other double image argument
functions.
*Changed bilt to replace (the name of bilt is way to esoteric). Replace
gives you the basic assignment op.
* Removed scale/sacled. I removed this code because we don't want to
encourage people to scale things and allocate additional images in
memory. I decided to keep copy() for completeness sakes... but, I don't
see anyone using it. (By completeness sakes I mean that we now have the
assignment op, copy op, etc. for an image object).
* Removed rainbow. This feature is built into the FIR module now.
Moving on, compress needs to be renamed to compressed and a new compress
function will need to be added.
The compress() function will compress the image (or frame buffer, etc)
and not return a new object. The compressed() function will return a new
object and not compress the original image.
The compress function will make it easier for users to compress images
once they are done working on them before sending the image some where.
I don't see compressed() being used much then after adding the
compress() function. Since the compress() function won't use up heap
space this makes it very good.
Removed micropython code from the image libary. Also, blobs are now 10
tuple values by default now. The multilist thing has been removed from
blobs and it will return just a list of blobs instead of a tree of
lists.
Filter functions still work too.
Pixels, centroid, and orientation are calculated in the blob code now.
As for threshold, it is no longer needed (plus, it required storing a
secondary image in RAM which isn't really something we can handle).
Blob tracking has now been updated to work without requiring prior
segmentation of the image. You can still run it on a segmented image,
but, that is not needed anymore.
Use the copy color feature of the OpenMV IDE to get a color in the
image. Once you have that you can then pass the color to find_blobs which
will output a tuple of lists of blobs for each color. By default, all
blobs less than 1/1000th of the image are filtered out, however, you can
add a custom filter function which gets the image and the blob about to
be added to the list and you can decide to filter it or not.
For marker tracking, we now have a function called find markers which
basically merges all the blobs found by find blobs into one list of
blobs. Each new blob will have a color code value which will tell you
what colors are part of that blob. We support tracking up to 30 unique
colors this way.
Mean filter -> Fast and easy to use. This will likely be the only filter
that gets alot of action on the M4.
Median filter -> Works really well, but, slow. On grayscale at 160x120
you can get also 10 FPS with it for a 3x3 kernel. That said, it's still
slow. Also, the code only works for 3x3 and 5x5 kernels.
About the previous histogram filter... technically, that filter should be
better. However, it suffers from a startup cost. The operation of finding
the median point in the histogram costs too much to compute. This is
what causes it to be slow. On very large kernels it will be faster than
the sorting median alrogithm I put up... but, large kernels will be too
slow for anyone to use anyway. The paper Ibrahim linked to about it
showed it being used for like 7x7 kernels and up... so, I think the
researcher who thought of the idea was really thinking about the
algorithm for large kernels.
Mode filter -> Works great on grayscale. Not so much on color. I think it
needs to be run on the LAB color space instead of the RGB color space. I
say this because it causes pretty strong artifacts around edges. When we
get more flash we'll be able to have a reverse lookup table for LAB to
make the mode filter better. Until then...
So I'm just adding a function to do it cleanly and efficently. Call
skip_frames() after changing any camera settings to let it settle.
10 frames by default works fine. Tested it.
Has a bias value that allows you to control if its really a midpoint,
min, max filter, or something inbetween. Run at 160x120 or lower. 320x240
is slow (seems to be the case for all convoltions at that res).
Added setters for these camera settings. AWB is necessary for color
tracking to work correctly. AGC still runs, which causes lighting
shifts. It may need to be disabled too. Not sure... if I want to do that
or not however, because without it lightning won't get normalized to
remain at a certain level. So, turning AGC off may cause issues in other
ways.
First, a few things:
The MLX 16x4 sensor has just too low of a resolution for mass appeal for
the price. The product is not going to sell very well. We need to look
into supporting sensors with a better res. Like the FLIR 1. The MLX
module was renamed to the "flir" module with this idea in mind.
The flir code now takes care of doing scaling and blending itself. I did
this to get rid of the user having to scale the image themselves and
blend themselves. Its too easy to run out of memory given our current
ultra small heap. In general, anything that requires multiple images in
RAM has got to go. When we do another OpenMV Cam with external RAM in
the MB range then maybe such functions will be safe. But, right now they
are definately not.
Anyway, moving on, I fixed a few bugs with the MLX math code. But, for
the most part was correct. I also added reconmended polling code for
brownouts as required by the datasheet.
Last, I designed this code like the LCD code to support a type value
when inited. This will allow the system to user a different sensor in the
future without any API changes to the user.
I will add test scripts for this next. Basic usage follows:
import flir
flir.init()
flir.display_ir(sensor.snapshot())
And that's it. Super easy. If the user wants the raw temp values they
can use flir.read_ir() to get the ta and to values. The display function
has a hidden alpha and scale argument for controling blending and the
min/max scaling.
The previous way we worked out scaling kinda sucked... it was a good
shot, but, controllable min and maxes that autoscale by default just
work better. If the user knows the temp range then they can just set the
min and max.'
Anyway, longest commit ever done.
File reading is runing ultra fast now. We're getting that SD card speed
the STM32 promised now. The file buffer commands have been updated to
alloc as much available memory to read as much of a file in as possible
now to speed up things. This works really great.
Note however, while the file buffer is active you have to use the file
buffer versions of tell and size. Spent a few hours on tracking down an
error related to not using the buffered versions.
All file write functions now use fb_alloc to go much faster. Writes are
re-directed to the extra frame buffer RAM and are grouped until they can
be written in a massive multi-block write to the SD card. We get the
best SD card write speed by doing things this way.
Ideally we'd want to buffer the whole file... but, this is about as good
as we're going to get for now.
Going to fix reading functions to use the same buffer next.
The built-in mjpeg module allows you to record videos seamlessly. It
will automatically compress the frame buffer using the extra space in the
main ram. So... you don't have to pass it jpeg images. Gets 7 FPS at
320x240 while connected to the computer too (it has to compress the
frame twice in this situation).
Anyway, the module work like Gif.
Now you can just grab all the free ram in the frame buffer in one go.
This fixes problems figuring out how many lines to alloc. Will update line
op code with this new info later.
Color gifs look very good for how bad you'd expect them to be with just
7 bits of color (rgb232) - quite amazing. Also, I hardened the gif
module to make it "user ready".
You can now get the color stats for an area in the image. The stats
function returns the mean, median, mode, min, max, st_dev,
lower_quartile, and upper_quartile.
This function allows you to automate binary and threshold functions
based on what's in the iamge.
The morph function lets you convolve the image with a kernel. It's
decently fast right now. But, in the future we'll have to optimize it by
a lot (unrolling loops, using SIMD instructions, etc.).
Anyway, along with morph I added an edge detection test script showing
how you can use a high pass filter on an image to get all the edges in
it. This is not as good as canny edge dection... but, it's about the
same and fast enough.
We'll need a Hough Transform system in the future to make edge dection
useful. Not sure how that will be implemented... so, that's going to be
far away for now.
The old code did not actually implement the errode anhd dilate kernels
correctly. However, it migh have been a little faster because it avoided
the boundary problem.
In the future we can optimize all the kernel code to have different loops
for doing the edges of image versus the center. But, for now, this is
good enough. QVGA color tracking with kernels will be slow, but, the
speed can be improved with QQVGA resolution. Using a 3x3 kernel is
plenty fast. Larger ones are slower.
I also added the ability for you to set the threshold for erode and
dialte. This lets you make the kenrel a little bit smarter so that it
won't errode or dilate a pixel unless the threshold is met. Meaning,
you'll be able to use erode to erode an image down to 1 pixel wide
lines.
All the work previously has been more or less leading up to supporting
this function. The line op function will open a file and execute a
function pointer on each line of the file opened to modify the frame
buffer.
It now figures out the file type from the file extension. If no file
extension is given it just saves the file as BMP if its not a JPEG image
or JPEG if it's a JPEG image. If you specify an extension and the file is
not of that type then it will give you an error.
The new test_save.py should run until you reach the JPEG image part
where it quits due to lack of JPEG support natively on OV7725 boards.
Maybe JPEG mode should be supoorted by just compressing pictures?
There's not a lot of actual functionality changes from the last commit.
However, switching the basic wrapper library to just long_jump on
failure and moving all the state info to structs required changes to all
the base functions in the last commit. The rest of the changes are to
link in the new functionality and to get the code to compile (usbdbg.c
edits).
Next I'll work on a function which abstracts the problem of opening an
image up and executing a line by line function op on it. I already
worked the code out for that. But, it's not in this commit to keep
things streamlined.
* With the new integral moving window we can support face detection,
keypoints and template matching on QVGA frames. However, it was only
implemented and tested for face detection.
* Increasing the max integral frame now for easier testing.
Fimrware will now automatically detect the appropriate file type and read
in that file type correctly.
Working on tying on of this stuff togheter next. It's getting a little
bit too complicated to deal with error cases. Need to add error message
function layer.