Skip to content

Commit e864648

Browse files
authoredMay 26, 2023
Merge pull request #689 from arduino/zoom-flip-mirror
[AE-2] Add zoom, flip, and mirror functionality for GC2145
2 parents 26804b6 + cb7ec81 commit e864648

File tree

15 files changed

+477
-14
lines changed

15 files changed

+477
-14
lines changed
 

‎.github/workflows/compile-examples.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ jobs:
7676
- libraries/doom
7777
- libraries/KernelDebug
7878
- libraries/MCUboot
79-
- libraries/Camera/examples
79+
- libraries/Camera/examples/CameraCaptureRawBytes
80+
- libraries/Camera/examples/CameraMotionDetect
8081
- libraries/Portenta_lvgl/examples/Portenta_lvgl
8182
- libraries/Portenta_SDCARD
8283
- libraries/Portenta_SDRAM
@@ -115,6 +116,7 @@ jobs:
115116
additional-sketch-paths: |
116117
- libraries/PDM
117118
- libraries/Camera/examples/CameraCaptureRawBytes
119+
- libraries/Camera/examples/CameraCaptureZoomPan
118120
- libraries/SE05X
119121
- libraries/STM32H747_System
120122
- libraries/ThreadDebug

‎libraries/Camera/examples/CameraCaptureRawBytes/CameraCaptureRawBytes.ino

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@
66
Camera cam(galaxyCore);
77
#define IMAGE_MODE CAMERA_RGB565
88
#elif defined(ARDUINO_PORTENTA_H7_M7)
9+
// uncomment the correct camera in use
910
#include "hm0360.h"
1011
HM0360 himax;
12+
13+
// #include "himax.h"
14+
// HM01B0 himax;
15+
// Camera cam(himax);
16+
1117
Camera cam(himax);
1218
#define IMAGE_MODE CAMERA_GRAYSCALE
1319
#elif defined(ARDUINO_GIGA)
@@ -60,7 +66,7 @@ void setup() {
6066

6167
void loop() {
6268
if(!Serial) {
63-
Serial.begin(921600);
69+
Serial.begin(115200);
6470
while(!Serial);
6571
}
6672

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* This example shows how to use the Nicla Vision to capture images from the camera
3+
* with a zoom window and send them over the serial port.
4+
* The zoom window will move from left to right and top to bottom
5+
* in the predefined steps of pixels (ZOOM_X_STEP and ZOOM_Y_STEP).
6+
*
7+
* Whenever the board sends a frame over the serial port, the blue LED will blink.
8+
*
9+
* Instructions:
10+
* 1. Upload this sketch to Nicla Vision.
11+
* 2. Open the CameraRawBytesVisualizer.pde Processing sketch and change `useGrayScale` to `false`.
12+
* 3. Adjust the serial port in the Processing sketch to match the one used by Nicla Vision.
13+
* 4. Run the Processing sketch.
14+
*
15+
* Initial author: Sebastian Romero @sebromero
16+
*/
17+
18+
#include "camera.h"
19+
20+
#ifndef ARDUINO_NICLA_VISION
21+
#error "This sketch only works on Nicla Vision."
22+
#endif
23+
24+
#include "gc2145.h"
25+
GC2145 galaxyCore;
26+
Camera cam(galaxyCore);
27+
#define IMAGE_MODE CAMERA_RGB565
28+
29+
#define CHUNK_SIZE 512 // Size of chunks in bytes
30+
#define RESOLUTION CAMERA_R1600x1200 // Zoom in from the highest supported resolution
31+
#define ZOOM_WINDOW_RESOLUTION CAMERA_R320x240
32+
33+
constexpr uint16_t ZOOM_WINDOW_WIDTH = 320;
34+
constexpr uint16_t ZOOM_WINDOW_HEIGHT = 240;
35+
constexpr uint16_t ZOOM_X_STEP = 100;
36+
constexpr uint16_t ZOOM_Y_STEP = 100;
37+
38+
FrameBuffer frameBuffer;
39+
uint32_t currentZoomX = 0;
40+
uint32_t currentZoomY = 0;
41+
uint32_t maxZoomX = 0; // Will be calculated in setup()
42+
uint32_t maxZoomY = 0; // Will be calculated in setup()
43+
44+
45+
void blinkLED(uint32_t count = 0xFFFFFFFF)
46+
{
47+
pinMode(LED_BUILTIN, OUTPUT);
48+
49+
while (count--) {
50+
digitalWrite(LED_BUILTIN, LOW); // turn the LED on (HIGH is the voltage level)
51+
delay(50); // wait for a second
52+
digitalWrite(LED_BUILTIN, HIGH); // turn the LED off by making the voltage LOW
53+
delay(50); // wait for a second
54+
}
55+
}
56+
57+
void setup() {
58+
// Init the cam QVGA, 30FPS
59+
if (!cam.begin(RESOLUTION, IMAGE_MODE, 30)) {
60+
blinkLED();
61+
}
62+
63+
blinkLED(5);
64+
65+
pinMode(LEDB, OUTPUT);
66+
digitalWrite(LEDB, HIGH);
67+
68+
// Flips the image vertically
69+
cam.setVerticalFlip(true);
70+
71+
// Mirrors the image horizontally
72+
cam.setHorizontalMirror(true);
73+
74+
// Calculate the max zoom window position
75+
maxZoomX = cam.getResolutionWidth() - ZOOM_WINDOW_WIDTH;
76+
maxZoomY = cam.getResolutionHeight() - ZOOM_WINDOW_HEIGHT;
77+
78+
// Set the zoom window to 0,0
79+
cam.zoomTo(ZOOM_WINDOW_RESOLUTION, currentZoomX, currentZoomY);
80+
}
81+
82+
void sendFrame(){
83+
// Grab frame and write to serial
84+
if (cam.grabFrame(frameBuffer, 3000) == 0) {
85+
byte* buffer = frameBuffer.getBuffer();
86+
size_t bufferSize = cam.frameSize();
87+
digitalWrite(LEDB, LOW);
88+
89+
// Split buffer into chunks
90+
for(size_t i = 0; i < bufferSize; i += CHUNK_SIZE) {
91+
size_t chunkSize = min(bufferSize - i, CHUNK_SIZE);
92+
Serial.write(buffer + i, chunkSize);
93+
Serial.flush();
94+
delay(1); // Small delay to allow the receiver to process the data
95+
}
96+
97+
digitalWrite(LEDB, HIGH);
98+
} else {
99+
blinkLED(20);
100+
}
101+
}
102+
103+
void loop() {
104+
if(!Serial) {
105+
Serial.begin(115200);
106+
while(!Serial);
107+
}
108+
109+
if(!Serial.available()) return;
110+
byte request = Serial.read();
111+
112+
if(request == 1){
113+
sendFrame();
114+
currentZoomX += ZOOM_X_STEP;
115+
116+
if(currentZoomX > maxZoomX){
117+
currentZoomX = 0;
118+
currentZoomY += ZOOM_Y_STEP;
119+
if(currentZoomY > maxZoomY){
120+
currentZoomY = 0;
121+
}
122+
}
123+
cam.zoomTo(ZOOM_WINDOW_RESOLUTION, currentZoomX, currentZoomY);
124+
}
125+
126+
}

‎libraries/Camera/examples/CameraMotionDetect/CameraMotionDetect.ino

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
#include "camera.h"
2-
#include "himax.h"
3-
HM01B0 himax;
2+
3+
// uncomment the correct camera in use
4+
#include "hm0360.h"
5+
HM0360 himax;
6+
7+
// #include "himax.h"
8+
// HM01B0 himax;
9+
410
Camera cam(himax);
511

612
#ifdef ARDUINO_NICLA_VISION

‎libraries/Camera/extras/CameraRawBytesVisualizer/CameraRawBytesVisualizer.pde

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ final int cameraHeight = 240;
2020
final boolean useGrayScale = true;
2121

2222
// Must match the baud rate in the Arduino sketch
23-
final int baudRate = 921600;
23+
final int baudRate = 115200;
2424

2525
final int cameraBytesPerPixel = useGrayScale ? 1 : 2;
2626
final int cameraPixelCount = cameraWidth * cameraHeight;

‎libraries/Camera/src/camera.cpp

+90-5
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ const uint32_t restab[CAMERA_RMAX][2] = {
137137
{320, 240 }, // QVGA
138138
{320, 320 },
139139
{640, 480 }, // VGA
140+
{0, 0 }, // Empty entry because there's a jump in the resolution enum initializers
140141
{800, 600 }, // SVGA
141142
{1600, 1200}, // UXGA
142143
};
@@ -504,36 +505,120 @@ int Camera::setFrameRate(int32_t framerate)
504505
return -1;
505506
}
506507

507-
int Camera::setResolution(int32_t resolution)
508+
int Camera::setResolutionWithZoom(int32_t resolution, int32_t zoom_resolution, int32_t zoom_x, int32_t zoom_y)
508509
{
509510
if (this->sensor == NULL || resolution >= CAMERA_RMAX
510511
|| pixformat >= CAMERA_PMAX || pixformat == -1) {
511512
return -1;
512513
}
513514

515+
// resolution = the full resolution to set the camera to
516+
// zoom_resolution = the resolution to crop to when zooming (set equal to resolution for no zoom)
517+
// final_resolution = the resolution to crop to (depends on zoom or not)
518+
int32_t final_resolution;
519+
// Check if zooming is asked for
520+
if (resolution != zoom_resolution)
521+
{
522+
// Can't zoom into a larger window than the original
523+
if (zoom_resolution > resolution)
524+
{
525+
return -1;
526+
}
527+
final_resolution = zoom_resolution;
528+
}
529+
else
530+
{
531+
final_resolution = resolution;
532+
}
533+
514534
/*
515535
* @param X0 DCMI window X offset
516536
* @param Y0 DCMI window Y offset
517537
* @param XSize DCMI Pixel per line
518538
* @param YSize DCMI Line number
519539
*/
520540
HAL_DCMI_EnableCROP(&hdcmi);
521-
uint32_t bpl = restab[resolution][0];
541+
uint32_t bpl = restab[final_resolution][0];
522542
if (pixformat == CAMERA_RGB565 ||
523543
(pixformat == CAMERA_GRAYSCALE && !this->sensor->getMono())) {
524544
// If the pixel format is Grayscale and sensor is Not monochrome,
525545
// the actual pixel format will be YUV (i.e 2 bytes per pixel).
526546
bpl *= 2;
527547
}
528-
HAL_DCMI_ConfigCROP(&hdcmi, 0, 0, bpl - 1, restab[resolution][1] - 1);
548+
HAL_DCMI_ConfigCROP(&hdcmi, 0, 0, bpl - 1, restab[final_resolution][1] - 1);
529549

530-
if (this->sensor->setResolution(resolution) == 0) {
531-
this->resolution = resolution;
550+
if (this->sensor->setResolutionWithZoom(resolution, zoom_resolution, zoom_x, zoom_y) == 0) {
551+
this->resolution = final_resolution;
532552
return 0;
533553
}
534554
return -1;
535555
}
536556

557+
int Camera::setResolution(int32_t resolution)
558+
{
559+
// Check for resolutions that would cause out-of-bounds indexing of restab
560+
// This check is here because original_resolution will be trusted in all other code
561+
if ((resolution < 0) || (resolution >= CAMERA_RMAX))
562+
{
563+
return -1;
564+
}
565+
original_resolution = resolution;
566+
return setResolutionWithZoom(resolution, resolution, 0, 0);
567+
}
568+
569+
int Camera::zoomTo(int32_t zoom_resolution, uint32_t zoom_x, uint32_t zoom_y)
570+
{
571+
// Check for zoom resolutions that would cause out-of-bounds indexing of restab
572+
if ((zoom_resolution < 0) || (zoom_resolution >= CAMERA_RMAX))
573+
{
574+
return -1;
575+
}
576+
// Check if the zoom window goes outside the frame on the x axis
577+
// Notice that this form prevents uint32_t wraparound, so don't change it
578+
if (zoom_x >= (restab[this->original_resolution][0]) - (restab[zoom_resolution][0]))
579+
{
580+
return -1;
581+
}
582+
// Check if the zoom window goes outside the frame on the y axis
583+
// Notice that this form prevents uint32_t wraparound, so don't change it
584+
if (zoom_y >= (restab[this->original_resolution][1]) - (restab[zoom_resolution][1]))
585+
{
586+
return -1;
587+
}
588+
return setResolutionWithZoom(this->original_resolution, zoom_resolution, zoom_x, zoom_y);
589+
}
590+
591+
int Camera::zoomToCenter(int32_t zoom_resolution)
592+
{
593+
// Check for zoom resolutions that would cause out-of-bounds indexing of restab
594+
if ((zoom_resolution < 0) || (zoom_resolution >= CAMERA_RMAX))
595+
{
596+
return -1;
597+
}
598+
uint32_t zoom_x = (restab[this->original_resolution][0] - restab[zoom_resolution][0]) / 2;
599+
uint32_t zoom_y = (restab[this->original_resolution][1] - restab[zoom_resolution][1]) / 2;
600+
return setResolutionWithZoom(this->original_resolution, zoom_resolution, zoom_x, zoom_y);
601+
}
602+
603+
int Camera::setVerticalFlip(bool flip_enable)
604+
{
605+
return (this->sensor->setVerticalFlip(flip_enable));
606+
}
607+
608+
int Camera::setHorizontalMirror(bool mirror_enable)
609+
{
610+
return (this->sensor->setHorizontalMirror(mirror_enable));
611+
}
612+
613+
uint32_t Camera::getResolutionWidth()
614+
{
615+
return (restab[this->original_resolution][0]);
616+
}
617+
uint32_t Camera::getResolutionHeight()
618+
{
619+
return (restab[this->original_resolution][1]);
620+
}
621+
537622
int Camera::setPixelFormat(int32_t pixformat)
538623
{
539624
if (this->sensor == NULL || pixformat >= CAMERA_PMAX) {

0 commit comments

Comments
 (0)
Please sign in to comment.