Quellcode durchsuchen

Support crop image on sensor, grayscale, auto exposure level

Joo Aun Saw vor 2 Jahren
Ursprung
Commit
ac4f823cbf

+ 217 - 50
code/components/jomjol_controlcamera/ClassControllCamera.cpp

@@ -53,6 +53,10 @@ static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary="
 static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
 static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
 
+// OV Camera SDE Indirect Register Access
+#define OV_IRA_BPADDR               0x7C
+#define OV_IRA_BPDATA               0x7D
+
 
 static camera_config_t camera_config = {
     .pin_pwdn = CAM_PIN_PWDN,
@@ -81,8 +85,8 @@ static camera_config_t camera_config = {
 
     .pixel_format = PIXFORMAT_JPEG, //YUV422,GRAYSCALE,RGB565,JPEG
     .frame_size = FRAMESIZE_VGA,    //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
-//    .frame_size = FRAMESIZE_UXGA,    //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
-    .jpeg_quality = 12, //0-63 lower number means higher quality
+    // .frame_size = FRAMESIZE_UXGA,    //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
+    .jpeg_quality = 4, //0-63 lower number means higher quality
     .fb_count = 1,       //if more than one, i2s runs in continuous mode. Use only with JPEG
     .fb_location = CAMERA_FB_IN_PSRAM, /*!< The location where the frame buffer will be allocated */
     .grab_mode = CAMERA_GRAB_LATEST,      // only from new esp32cam version
@@ -166,7 +170,7 @@ static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size
 }
 
 
-bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation)
+bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation, int _autoExposureLevel, bool _grayscale)
 {
     _brightness = min(2, max(-2, _brightness));
     _contrast = min(2, max(-2, _contrast));
@@ -174,6 +178,11 @@ bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, in
 
     sensor_t * s = esp_camera_sensor_get();
     if (s) {
+        // auto exposure controls
+        s->set_ae_level(s, _autoExposureLevel); // -2 to 2
+        s->set_gainceiling(s, GAINCEILING_2X); // GAINCEILING_2X 4X 8X 16X 32X 64X 128X
+
+        // post processing
         s->set_saturation(s, _saturation);
         s->set_contrast(s, _contrast);
         s->set_brightness(s, _brightness);
@@ -205,9 +214,23 @@ bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, in
 
         //s->set_reg(s, 0x7C, 0xFF, 2); // Optional feature - hue setting: Select byte 2 in register 0x7C to set hue value
         //s->set_reg(s, 0x7D, 0xFF, 0); // Optional feature - hue setting: Hue value 0 - 255
-        s->set_reg(s, 0xFF, 0x01, 0); // Select DSP bank
-        s->set_reg(s, 0x7C, 0xFF, 0); // Select byte 0 in register 0x7C
-        s->set_reg(s, 0x7D, 7, 7); // Set bit 0, 1, 2 in register 0x7D to enable saturation, contrast, brightness and hue control
+        if (_grayscale) {
+            // Indirect register access
+            s->set_reg(s, 0xFF, 0x01, 0); // Select DSP bank
+            s->set_reg(s, OV_IRA_BPADDR, 0xFF, 0x00); // Address 0x00
+            s->set_reg(s, OV_IRA_BPDATA, 0xFF, 0x1F); // Set bit 0, 1, 2 to enable saturation, contrast, brightness and hue control
+            s->set_reg(s, OV_IRA_BPADDR, 0xFF, 0x05); // Address 0x05
+            s->set_reg(s, OV_IRA_BPDATA, 0xFF, 0x80);
+            s->set_reg(s, OV_IRA_BPDATA, 0xFF, 0x80);
+        } else {
+            // Indirect register access
+            s->set_reg(s, 0xFF, 0x01, 0); // Select DSP bank
+            s->set_reg(s, OV_IRA_BPADDR, 0xFF, 0x00); // Address 0x00
+            s->set_reg(s, OV_IRA_BPDATA, 0xFF, 7); // Set bit 0, 1, 2 to enable saturation, contrast, brightness and hue control
+            s->set_reg(s, OV_IRA_BPADDR, 0xFF, 0x05); // Address 0x05
+            s->set_reg(s, OV_IRA_BPDATA, 0xFF, 0x80);
+            s->set_reg(s, OV_IRA_BPDATA, 0xFF, 0x80);
+        }
     }
     else {
         LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetBrightnessContrastSaturation: Failed to get control structure");
@@ -219,38 +242,123 @@ bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, in
     brightness = _brightness;
     contrast = _contrast;
     saturation = _saturation;
+    autoExposureLevel = _autoExposureLevel;
+    imageGrayscale = _grayscale;
 
-    ESP_LOGD(TAG, "brightness %d, contrast: %d, saturation %d", brightness, contrast, saturation);
+    ESP_LOGD(TAG, "brightness %d, contrast: %d, saturation %d, autoExposureLevel %d, grayscale %d", brightness, contrast, saturation, autoExposureLevel, (int)imageGrayscale);
 
     return true;
 }
 
 
-void CCamera::SetQualitySize(int qual, framesize_t resol)
+/*
+* resolution = 0 \\ 1600 x 1200
+* resolution = 1 \\  800 x  600
+* resolution = 2 \\  400 x  296
+*/
+void CCamera::SetCamWindow(sensor_t *s, int resolution, int xOffset, int yOffset, int xLength, int yLength)
 {
-    qual = min(63, max(8, qual)); // Limit quality from 8..63 (values lower than 8 tent to be unstable)
-    
-    sensor_t * s = esp_camera_sensor_get();
-    if (s) {
-        s->set_quality(s, qual);    
-        s->set_framesize(s, resol);
-    }
-    else {
-        LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetQualitySize: Failed to get control structure");
-    }
+    s->set_res_raw(s, resolution, 0, 0, 0, xOffset, yOffset, xLength, yLength, xLength, yLength, false, false);
+}
 
-    ActualResolution = resol;
-    ActualQuality = qual;
 
+void CCamera::SetImageWidthHeightFromResolution(framesize_t resol)
+{
     if (resol == FRAMESIZE_QVGA)
     {
         image_height = 240;
-        image_width = 320;             
+        image_width = 320;
     }
     else if (resol == FRAMESIZE_VGA)
     {
         image_height = 480;
-        image_width = 640;             
+        image_width = 640;
+    }
+    else if (resol == FRAMESIZE_SVGA)
+    {
+        image_height = 600;
+        image_width = 800;
+    }
+    else if (resol == FRAMESIZE_XGA)
+    {
+        image_height = 768;
+        image_width = 1024;
+    }
+    else if (resol == FRAMESIZE_HD)
+    {
+        image_height = 720;
+        image_width = 1280;
+    }
+    else if (resol == FRAMESIZE_SXGA)
+    {
+        image_height = 1024;
+        image_width = 1280;
+    }
+    else if (resol == FRAMESIZE_UXGA)
+    {
+        image_height = 1200;
+        image_width = 1600;
+    }
+}
+
+
+void CCamera::SetZoom(bool zoomEnabled, int zoomMode, int zoomOffsetX, int zoomOffsetY)
+{
+    imageZoomEnabled = zoomEnabled;
+    imageZoomMode = zoomMode;
+    imageZoomOffsetX = zoomOffsetX;
+    imageZoomOffsetY = zoomOffsetY;
+
+    sensor_t *s = esp_camera_sensor_get();
+    if (s) {
+        if (imageZoomEnabled) {
+            int z = imageZoomMode;
+            int x = imageZoomOffsetX;
+            int y = imageZoomOffsetY;
+            if (z > 1)
+                z = 1;
+            if (image_width >= 800 || image_height >= 600) {
+                z = 0;
+            }
+            int maxX = 1600 - image_width;
+            int maxY = 1200 - image_height;
+            if (z == 1) {
+                maxX = 800 - image_width;
+                maxY = 600 - image_height;
+            }
+            if (x > maxX)
+                x = maxX;
+            if (y > maxY)
+                y = maxY;
+            SetCamWindow(s, z, x, y, image_width, image_height);
+        } else {
+            s->set_framesize(s, ActualResolution);
+        }
+    }
+}
+
+
+void CCamera::SetQualitySize(int qual, framesize_t resol, bool zoomEnabled, int zoomMode, int zoomOffsetX, int zoomOffsetY)
+{
+    qual = min(63, max(8, qual)); // Limit quality from 8..63 (values lower than 8 tent to be unstable)
+    
+    ActualResolution = resol;
+    ActualQuality = qual;
+
+    imageZoomEnabled = zoomEnabled;
+    imageZoomMode = zoomMode;
+    imageZoomOffsetX = zoomOffsetX;
+    imageZoomOffsetY = zoomOffsetY;
+
+    SetImageWidthHeightFromResolution(resol);
+
+    sensor_t * s = esp_camera_sensor_get();
+    if (s) {
+        s->set_quality(s, qual);
+        SetZoom(zoomEnabled, zoomMode, zoomOffsetX, zoomOffsetY);
+    }
+    else {
+        LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetQualitySize: Failed to get control structure");
     }
 }
 
@@ -315,7 +423,7 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
 	#endif
 
     camera_fb_t * fb = esp_camera_fb_get();
-    esp_camera_fb_return(fb);        
+    esp_camera_fb_return(fb);
     fb = esp_camera_fb_get();
     if (!fb) {
         LEDOnOff(false);
@@ -340,24 +448,44 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
     else {
         LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToBasisImage: Can't allocate _zwImage");
     }
-    esp_camera_fb_return(fb);        
+    esp_camera_fb_return(fb);
 
     #ifdef DEBUG_DETAIL_ON
         LogFile.WriteHeapInfo("CaptureToBasisImage - After fb_get");
     #endif
 
-    LEDOnOff(false);  
+    LEDOnOff(false);
 
     if (delay > 0) 
         LightOnOff(false);
  
-//    TickType_t xDelay = 1000 / portTICK_PERIOD_MS;     
+//    TickType_t xDelay = 1000 / portTICK_PERIOD_MS;
 //    vTaskDelay( xDelay );  // wait for power to recover
     
     #ifdef DEBUG_DETAIL_ON
         LogFile.WriteHeapInfo("CaptureToBasisImage - After LoadFromMemory");
     #endif
 
+    if (_zwImage == NULL) {
+        return ESP_OK;
+    }
+
+    if (_zwImage->getWidth() > image_width || _zwImage->getHeight() > image_height) {
+        int cropLeft = (_zwImage->getWidth() - image_width) / 2;
+        if (cropLeft < 0)
+            cropLeft = 0;
+        int cropRight = _zwImage->getWidth() - cropLeft - image_width;
+        if (cropRight < 0)
+            cropRight = 0;
+        int cropTop = (_zwImage->getHeight() - image_height) / 2;
+        if (cropTop < 0)
+            cropTop = 0;
+        int cropBottom = _zwImage->getHeight() - cropTop - image_height;
+        if (cropBottom < 0)
+            cropBottom = 0;
+        _zwImage->crop_image(cropLeft, cropRight, cropTop, cropBottom);
+    }
+
     stbi_uc* p_target;
     stbi_uc* p_source;    
     int channels = 3;
@@ -375,9 +503,9 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
         {
             p_target = _Image->rgb_image + (channels * (y * width + x));
             p_source = _zwImage->rgb_image + (channels * (y * width + x));
-            p_target[0] = p_source[0];
-            p_target[1] = p_source[1];
-            p_target[2] = p_source[2];
+            for (int c = 0; c < channels; c++) {
+                p_target[c] = p_source[c];
+            }
         }
 
     delete _zwImage;
@@ -386,7 +514,7 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
         LogFile.WriteHeapInfo("CaptureToBasisImage - Done");
     #endif
 
-    return ESP_OK;    
+    return ESP_OK;
 }
 
 
@@ -394,7 +522,7 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
 {
     string ftype;
 
-     LEDOnOff(true);              // Switched off to save power !
+    LEDOnOff(true);              // Switched off to save power !
 
     if (delay > 0) {
         LightOnOff(true);
@@ -414,7 +542,7 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
 
         return ESP_FAIL;
     }
-    LEDOnOff(false);    
+    LEDOnOff(false);
 
     #ifdef DEBUG_DETAIL_ON    
         ESP_LOGD(TAG, "w %d, h %d, size %d", fb->width, fb->height, fb->len);
@@ -504,8 +632,7 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
         return ESP_FAIL;
     }
 
-    LEDOnOff(false); 
-    
+    LEDOnOff(false);
     res = httpd_resp_set_type(req, "image/jpeg");
     if(res == ESP_OK){
         res = httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=raw.jpg");
@@ -662,49 +789,89 @@ void CCamera::LEDOnOff(bool status)
 }
 
 
-void CCamera::GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol)
+void CCamera::GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol, bool &zoomEnabled, int &zoomMode, int &zoomOffsetX, int &zoomOffsetY)
 {
     char _query[100];
-    char _qual[10];
-    char _size[10];
+    char _value[10];
 
     resol = ActualResolution;
     qual = ActualQuality;
-
+    zoomEnabled = imageZoomEnabled;
+    zoomMode = imageZoomMode;
+    zoomOffsetX = imageZoomOffsetX;
+    zoomOffsetY = imageZoomOffsetY;
 
     if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
     {
         ESP_LOGD(TAG, "Query: %s", _query);
-        if (httpd_query_key_value(_query, "size", _size, 10) == ESP_OK)
+        if (httpd_query_key_value(_query, "size", _value, sizeof(_value)) == ESP_OK)
         {
-            #ifdef DEBUG_DETAIL_ON   
+            #ifdef DEBUG_DETAIL_ON
             ESP_LOGD(TAG, "Size: %s", _size);
             #endif
-            if (strcmp(_size, "QVGA") == 0)
+            if (strcmp(_value, "QVGA") == 0)
                 resol = FRAMESIZE_QVGA;       // 320x240
-            else if (strcmp(_size, "VGA") == 0)
+            else if (strcmp(_value, "VGA") == 0)
                 resol = FRAMESIZE_VGA;      // 640x480
-            else if (strcmp(_size, "SVGA") == 0)
+            else if (strcmp(_value, "SVGA") == 0)
                 resol = FRAMESIZE_SVGA;     // 800x600
-            else if (strcmp(_size, "XGA") == 0)
+            else if (strcmp(_value, "XGA") == 0)
                 resol = FRAMESIZE_XGA;      // 1024x768
-            else if (strcmp(_size, "SXGA") == 0)
+            else if (strcmp(_value, "SXGA") == 0)
                 resol = FRAMESIZE_SXGA;     // 1280x1024
-            else if (strcmp(_size, "UXGA") == 0)
+            else if (strcmp(_value, "UXGA") == 0)
                  resol = FRAMESIZE_UXGA;     // 1600x1200   
         }
-        if (httpd_query_key_value(_query, "quality", _qual, 10) == ESP_OK)
+        if (httpd_query_key_value(_query, "quality", _value, sizeof(_value)) == ESP_OK)
         {
-            #ifdef DEBUG_DETAIL_ON   
+            #ifdef DEBUG_DETAIL_ON
             ESP_LOGD(TAG, "Quality: %s", _qual);
             #endif
-            qual = atoi(_qual);
-                
+            qual = atoi(_value);
             if (qual > 63)      // Limit to max. 63
                 qual = 63;
             else if (qual < 8)  // Limit to min. 8
                 qual = 8;
         }
+        if (httpd_query_key_value(_query, "z", _value, sizeof(_value)) == ESP_OK)
+        {
+            #ifdef DEBUG_DETAIL_ON
+            ESP_LOGD(TAG, "Zoom: %s", _value);
+            #endif
+            if (atoi(_value) != 0)
+                zoomEnabled = true;
+            else
+                zoomEnabled = false;
+        }
+        if (httpd_query_key_value(_query, "zm", _value, sizeof(_value)) == ESP_OK)
+        {
+            #ifdef DEBUG_DETAIL_ON
+            ESP_LOGD(TAG, "Zoom mode: %s", _value);
+            #endif
+            zoomMode = atoi(_value);
+            if (zoomMode > 2)
+                zoomMode = 2;
+            else if (zoomMode < 0)
+                zoomMode = 0;
+        }
+        if (httpd_query_key_value(_query, "x", _value, sizeof(_value)) == ESP_OK)
+        {
+            #ifdef DEBUG_DETAIL_ON
+            ESP_LOGD(TAG, "X offset: %s", _value);
+            #endif
+            zoomOffsetX = atoi(_value);
+            if (zoomOffsetX < 0)
+                zoomOffsetX = 0;
+        }
+        if (httpd_query_key_value(_query, "y", _value, sizeof(_value)) == ESP_OK)
+        {
+            #ifdef DEBUG_DETAIL_ON
+            ESP_LOGD(TAG, "Y offset: %s", _value);
+            #endif
+            zoomOffsetY = atoi(_value);
+            if (zoomOffsetY < 0)
+                zoomOffsetY = 0;
+        }
     }
 }
 

+ 17 - 4
code/components/jomjol_controlcamera/ClassControllCamera.h

@@ -19,7 +19,7 @@ class CCamera {
     protected:
         int ActualQuality;
         framesize_t ActualResolution;
-        int brightness, contrast, saturation;
+        int brightness, contrast, saturation, autoExposureLevel;
         bool isFixedExposure;
         int waitbeforepicture_org;
         int led_intensity = 4095;
@@ -31,8 +31,20 @@ class CCamera {
         bool loadNextDemoImage(camera_fb_t *fb);
         long GetFileSize(std::string filename);
 
+        void SetCamWindow(sensor_t *s, int resolution, int xOffset, int yOffset, int xLength, int yLength);
+        void SetImageWidthHeightFromResolution(framesize_t resol);
+
     public:
         int image_height, image_width;
+        bool imageZoomEnabled = false;
+        int imageZoomMode = 0;
+        int imageZoomOffsetX = 0;
+        int imageZoomOffsetY = 0;
+    #ifdef GRAYSCALE_AS_DEFAULT
+        bool imageGrayscale = true;
+    #else
+        bool imageGrayscale = false;
+    #endif
         
         CCamera();
         esp_err_t InitCam();
@@ -41,9 +53,10 @@ class CCamera {
         void LEDOnOff(bool status);
         esp_err_t CaptureToHTTP(httpd_req_t *req, int delay = 0);
         esp_err_t CaptureToStream(httpd_req_t *req, bool FlashlightOn);
-        void SetQualitySize(int qual, framesize_t resol);
-        bool SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation);
-        void GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol);
+        void SetQualitySize(int qual, framesize_t resol, bool zoomEnabled, int zoomMode, int zoomOffsetX, int zoomOffsetY);
+        bool SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation, int _autoExposureLevel, bool _grayscale);
+        void SetZoom(bool zoomEnabled, int zoomMode, int zoomOffsetX, int zoomOffsetY);
+        void GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol, bool &zoomEnabled, int &zoomMode, int &zoomOffsetX, int &zoomOffsetY);
         void SetLEDIntensity(float _intrel);
         bool testCamera(void);
         void EnableAutoExposure(int flash_duration);

+ 20 - 8
code/components/jomjol_controlcamera/server_camera.cpp

@@ -97,14 +97,18 @@ esp_err_t handler_capture(httpd_req_t *req)
     {
         int quality;
         framesize_t res;
+        bool zoomEnabled;
+        int zoomMode;
+        int zoomOffsetX;
+        int zoomOffsetY;
 
-        Camera.GetCameraParameter(req, quality, res);
+        Camera.GetCameraParameter(req, quality, res, zoomEnabled, zoomMode, zoomOffsetX, zoomOffsetY);
 
-        #ifdef DEBUG_DETAIL_ON   
+        #ifdef DEBUG_DETAIL_ON
             ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
         #endif
 
-        Camera.SetQualitySize(quality, res);
+        Camera.SetQualitySize(quality, res, zoomEnabled, zoomMode, zoomOffsetX, zoomOffsetY);
 
         esp_err_t result;
         result = Camera.CaptureToHTTP(req);
@@ -136,6 +140,10 @@ esp_err_t handler_capture_with_light(httpd_req_t *req)
 
         int quality;
         framesize_t res;    
+        bool zoomEnabled;
+        int zoomMode;
+        int zoomOffsetX;
+        int zoomOffsetY;
         int delay = 2500;
 
         if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
@@ -153,13 +161,13 @@ esp_err_t handler_capture_with_light(httpd_req_t *req)
             }
         }
 
-        Camera.GetCameraParameter(req, quality, res);
+        Camera.GetCameraParameter(req, quality, res, zoomEnabled, zoomMode, zoomOffsetX, zoomOffsetY);
 
-        #ifdef DEBUG_DETAIL_ON   
+        #ifdef DEBUG_DETAIL_ON
             ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
         #endif
 
-        Camera.SetQualitySize(quality, res);
+        Camera.SetQualitySize(quality, res, zoomEnabled, zoomMode, zoomOffsetX, zoomOffsetY);
         Camera.LightOnOff(true);
         const TickType_t xDelay = delay / portTICK_PERIOD_MS;
         vTaskDelay( xDelay );
@@ -200,6 +208,10 @@ esp_err_t handler_capture_save_to_file(httpd_req_t *req)
 
         int quality;
         framesize_t res;    
+        bool zoomEnabled;
+        int zoomMode;
+        int zoomOffsetX;
+        int zoomOffsetY;
 
         if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
         {
@@ -228,11 +240,11 @@ esp_err_t handler_capture_save_to_file(httpd_req_t *req)
         else
             fn.append("noname.jpg");
 
-        Camera.GetCameraParameter(req, quality, res);
+        Camera.GetCameraParameter(req, quality, res, zoomEnabled, zoomMode, zoomOffsetX, zoomOffsetY);
         #ifdef DEBUG_DETAIL_ON   
             ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
         #endif
-        Camera.SetQualitySize(quality, res);
+        Camera.SetQualitySize(quality, res, zoomEnabled, zoomMode, zoomOffsetX, zoomOffsetY);
 
         esp_err_t result;
         result = Camera.CaptureToFile(fn, delay);  

+ 1 - 1
code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp

@@ -31,7 +31,7 @@ void ClassFlowAlignment::SetInitialParameter(void)
     AlignAndCutImage = NULL;
     ImageBasis = NULL;
     ImageTMP = NULL;
-    #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG 
+    #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
     AlgROI = (ImageData*)malloc_psram_heap(std::string(TAG) + "->AlgROI", sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
     #endif
     previousElement = NULL;

+ 35 - 2
code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp

@@ -50,6 +50,15 @@ void ClassFlowTakeImage::SetInitialParameter(void)
     ImageQuality = 5;
     rawImage = NULL;
     ImageSize = FRAMESIZE_VGA;
+    ZoomEnabled = false;
+    ZoomMode = 0;
+    zoomOffsetX = 0;
+    zoomOffsetY = 0;
+#ifdef GRAYSCALE_AS_DEFAULT
+    ImageGrayscale = true;
+#else
+    ImageGrayscale = false;
+#endif
     SaveAllFiles = false;
     disabled = false;
     FixedExposure = false;
@@ -73,6 +82,7 @@ bool ClassFlowTakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
     int _brightness = -100;
     int _contrast = -100;
     int _saturation = -100;
+    int _autoExposureLevel = 0;
 
     if (aktparamgraph.size() == 0)
         if (!this->GetNextParagraph(pfile, aktparamgraph))
@@ -92,6 +102,29 @@ bool ClassFlowTakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
         if ((toUpper(splitted[0]) == "IMAGEQUALITY") && (splitted.size() > 1))
             ImageQuality = std::stod(splitted[1]);
 
+        if ((toUpper(splitted[0]) == "ZOOM") && (splitted.size() > 1))
+        {
+            if (toUpper(splitted[1]) == "TRUE")
+                ZoomEnabled = true;
+            else if (toUpper(splitted[1]) == "FALSE")
+                ZoomEnabled = false;
+        }
+        if ((toUpper(splitted[0]) == "ZOOMMODE") && (splitted.size() > 1))
+            ZoomMode = std::stod(splitted[1]);
+        if ((toUpper(splitted[0]) == "ZOOMOFFSETX") && (splitted.size() > 1))
+            zoomOffsetX = std::stod(splitted[1]);
+        if ((toUpper(splitted[0]) == "ZOOMOFFSETY") && (splitted.size() > 1))
+            zoomOffsetY = std::stod(splitted[1]);
+        if ((toUpper(splitted[0]) == "GRAYSCALE") && (splitted.size() > 1))
+        {
+            if (toUpper(splitted[1]) == "TRUE")
+                ImageGrayscale = true;
+            else if (toUpper(splitted[1]) == "FALSE")
+                ImageGrayscale = false;
+        }
+        if ((toUpper(splitted[0]) == "AUTOEXPOSURELEVEL") && (splitted.size() > 1))
+            _autoExposureLevel = std::stod(splitted[1]);
+
         if ((toUpper(splitted[0]) == "IMAGESIZE") && (splitted.size() > 1))
         {
             ImageSize = Camera.TextToFramesize(splitted[1].c_str());
@@ -150,8 +183,8 @@ bool ClassFlowTakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
         }
     }
 
-    Camera.SetBrightnessContrastSaturation(_brightness, _contrast, _saturation);
-    Camera.SetQualitySize(ImageQuality, ImageSize);
+    Camera.SetBrightnessContrastSaturation(_brightness, _contrast, _saturation, _autoExposureLevel, ImageGrayscale);
+    Camera.SetQualitySize(ImageQuality, ImageSize, ZoomEnabled, ZoomMode, zoomOffsetX, zoomOffsetY);
 
     image_width = Camera.image_width;
     image_height = Camera.image_height;

+ 5 - 0
code/components/jomjol_flowcontroll/ClassFlowTakeImage.h

@@ -17,6 +17,11 @@ protected:
     float waitbeforepicture_store;
     framesize_t ImageSize;
     bool isImageSize;
+    bool ZoomEnabled = false;
+    int ZoomMode = 0;
+    int zoomOffsetX = 0;
+    int zoomOffsetY = 0;
+    bool ImageGrayscale;
     int ImageQuality;
     time_t TimeImageTaken;
     string namerawimage;

+ 46 - 10
code/components/jomjol_flowcontroll/MainFlowControl.cpp

@@ -692,39 +692,75 @@ esp_err_t handler_editflow(httpd_req_t *req)
     if (_task.compare("test_take") == 0)
     {
         std::string _host = "";
-        std::string _bri = "";
-        std::string _con = "";
-        std::string _sat = "";
-        std::string _int = "";
         int bri = -100;
         int sat = -100;
         int con = -100;
         int intens = -100;
+        int aelevel = 0;
+        int zoommode = 0;
+        int zoomoffsetx = 0;
+        int zoomoffsety = 0;
+        bool zoom = false;
+    #ifdef GRAYSCALE_AS_DEFAULT
+        bool grayscale = true;
+    #else
+        bool grayscale = false;
+    #endif
 
         if (httpd_query_key_value(_query, "host", _valuechar, 30) == ESP_OK) {
             _host = std::string(_valuechar);
         }
         if (httpd_query_key_value(_query, "int", _valuechar, 30) == ESP_OK) {
-            _int = std::string(_valuechar);
+            std::string _int = std::string(_valuechar);
             intens = stoi(_int);
         }
         if (httpd_query_key_value(_query, "bri", _valuechar, 30) == ESP_OK) {
-            _bri = std::string(_valuechar);
+            std::string _bri = std::string(_valuechar);
             bri = stoi(_bri);
         }
         if (httpd_query_key_value(_query, "con", _valuechar, 30) == ESP_OK) {
-            _con = std::string(_valuechar);
+            std::string _con = std::string(_valuechar);
             con = stoi(_con);
         }
         if (httpd_query_key_value(_query, "sat", _valuechar, 30) == ESP_OK) {
-            _sat = std::string(_valuechar);
+            std::string _sat = std::string(_valuechar);
             sat = stoi(_sat);
         }
-
+        if (httpd_query_key_value(_query, "ae", _valuechar, 30) == ESP_OK) {
+            std::string _ae = std::string(_valuechar);
+            aelevel = stoi(_ae);
+        }
+        if (httpd_query_key_value(_query, "gs", _valuechar, 30) == ESP_OK) {
+            std::string _gr = std::string(_valuechar);
+            if (stoi(_gr) != 0)
+                grayscale = true;
+            else
+                grayscale = false;
+        }
+        if (httpd_query_key_value(_query, "z", _valuechar, 30) == ESP_OK) {
+            std::string _zoom = std::string(_valuechar);
+            if (stoi(_zoom) != 0)
+                zoom = true;
+            else
+                zoom = false;
+        }
+        if (httpd_query_key_value(_query, "zm", _valuechar, 30) == ESP_OK) {
+            std::string _zm = std::string(_valuechar);
+            zoommode = stoi(_zm);
+        }
+        if (httpd_query_key_value(_query, "x", _valuechar, 30) == ESP_OK) {
+            std::string _x = std::string(_valuechar);
+            zoomoffsetx = stoi(_x);
+        }
+        if (httpd_query_key_value(_query, "y", _valuechar, 30) == ESP_OK) {
+            std::string _y = std::string(_valuechar);
+            zoomoffsety = stoi(_y);
+        }
 
 //        ESP_LOGD(TAG, "Parameter host: %s", _host.c_str());
 //        string zwzw = "Do " + _task + " start\n"; ESP_LOGD(TAG, zwzw.c_str());
-        Camera.SetBrightnessContrastSaturation(bri, con, sat);
+        Camera.SetZoom(zoom, zoommode, zoomoffsetx, zoomoffsety);
+        Camera.SetBrightnessContrastSaturation(bri, con, sat, aelevel, grayscale);
         Camera.SetLEDIntensity(intens);
         ESP_LOGD(TAG, "test_take - vor TakeImage");
         std::string zw = flowctrl.doSingleStep("[TakeImage]", _host);

+ 39 - 1
code/components/jomjol_image_proc/CImageBasis.cpp

@@ -443,7 +443,7 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len)
         //free_psram_heap(std::string(TAG) + "->rgb_image (LoadFromMemory)", rgb_image);
     }
 
-    rgb_image = stbi_load_from_memory(_buffer, len, &width, &height, &channels, 3);
+    rgb_image = stbi_load_from_memory(_buffer, len, &width, &height, &channels, STBI_rgb);
     bpp = channels;
     ESP_LOGD(TAG, "Image loaded from memory: %d, %d, %d", width, height, channels);
     
@@ -459,6 +459,44 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len)
 }
 
 
+void CImageBasis::crop_image(unsigned short cropLeft, unsigned short cropRight, unsigned short cropTop, unsigned short cropBottom)
+{
+    unsigned int maxTopIndex = cropTop * width * channels;
+    unsigned int minBottomIndex = ((width*height) - (cropBottom * width)) * channels;
+    unsigned short maxX = width - cropRight; // In pixels
+    unsigned short newWidth = width - cropLeft - cropRight;
+    unsigned short newHeight = height - cropTop - cropBottom;
+
+    unsigned int writeIndex = 0;
+    // Loop over all bytes
+    for (int i = 0; i < width * height * channels; i += channels) {
+        // Calculate current X, Y pixel position
+        int x = (i/channels) % width;
+
+        // Crop from the top
+        if (i < maxTopIndex) { continue; }
+
+        // Crop from the bottom
+        if (i > minBottomIndex) { continue; }
+
+        // Crop from the left
+        if (x <= cropLeft) { continue; }
+
+        // Crop from the right
+        if (x > maxX) { continue; }
+
+        // If we get here, keep the pixels
+        for (int c = 0; c < channels; c++) {
+            rgb_image[writeIndex++] = rgb_image[i+c];
+        }
+    }
+
+    // Set the new dimensions of the framebuffer for further use.
+    width = newWidth;
+    height = newHeight;
+}
+
+
 CImageBasis::CImageBasis(string _name, CImageBasis *_copyfrom) 
 {
     name = _name;

+ 1 - 0
code/components/jomjol_image_proc/CImageBasis.h

@@ -74,6 +74,7 @@ class CImageBasis
 
         void Resize(int _new_dx, int _new_dy);        
         void Resize(int _new_dx, int _new_dy, CImageBasis *_target);        
+        void crop_image(unsigned short cropLeft, unsigned short cropRight, unsigned short cropTop, unsigned short cropBottom);
 
         void LoadFromMemory(stbi_uc *_buffer, int len);
 

+ 1 - 0
code/include/defines.h

@@ -66,6 +66,7 @@
     #define FLASH_GPIO GPIO_NUM_4               // PIN for flashlight LED
     #define USE_PWM_LEDFLASH                    // if __LEDGLOBAL is defined, a global variable is used for LED control, otherwise locally and each time a new
     #define CAM_LIVESTREAM_REFRESHRATE 500      // Camera livestream feature: Waiting time in milliseconds to refresh image
+    // #define GRAYSCALE_AS_DEFAULT
 
 
     //ClassControllCamera + ClassFlowTakeImage

+ 135 - 10
sd-card/html/edit_reference.html

@@ -99,6 +99,30 @@
             <col span="1" style="width: 18%;">
             <col span="1" style="width: 22%;">
         </colgroup>
+        <tr>
+            <td><label for="zoom" id="labelzoom">Enable zoom:</label></td>
+            <td><input type="checkbox" id="zoom" name="zoom" value="0"></td>
+            <td><label for="zoomoffsetx">Zoom offset X:</label></td>
+            <td>
+                <input required type="number" id="zoomoffsetx" name="zoomoffsetx" value="0" min="0" max="1280" step="1"
+                        oninput="(!validity.rangeOverflow||(value=1280)) && (!validity.rangeUnderflow||(value=0)) && 
+                            (!validity.stepMismatch||(value=parseInt(this.value)));">
+            </td>
+        </tr>
+        <tr>
+            <td><label for="zoommode" id="labelzoommode">Zoom mode:</label></td>
+            <td>
+                <input required type="number" id="zoommode" name="zoommode" value="0" min="0" max="1" step="1"
+                        oninput="(!validity.rangeOverflow||(value=1)) && (!validity.rangeUnderflow||(value=0)) && 
+                            (!validity.stepMismatch||(value=parseInt(this.value)));">
+            </td>
+            <td><label for="zoomoffsety">Zoom offset Y:</label></td>
+            <td>
+                <input required type="number" id="zoomoffsety" name="zoomoffsety" value="0" min="0" max="960" step="1"
+                        oninput="(!validity.rangeOverflow||(value=960)) && (!validity.rangeUnderflow||(value=0)) && 
+                            (!validity.stepMismatch||(value=parseInt(this.value)));">
+            </td>
+        </tr>
         <tr>
             <td><label for="mirror" id="labelmirror">Mirror image:</label></td>
             <td><input type="checkbox" id="mirror" name="mirror" value="1" onchange="drawRotated()"></td>
@@ -123,7 +147,7 @@
             </td>
         </tr>
         <tr>
-            <td><label for="prerotateangle">Rotation angle:</label></td>	  
+            <td><label for="prerotateangle">Rotation angle:</label></td>
             <td>
                 <input required type="number" id="prerotateangle" name="prerotateangle" value="0" min="-360" max="360" onchange="drawRotated()"
                         oninput="(!validity.rangeOverflow||(value=360)) && (!validity.rangeUnderflow||(value=-360)) && 
@@ -138,7 +162,7 @@
             </td>
         </tr>
         <tr>
-            <td><label for="finerotate">Rotation angle (Fine-tune):</label></td>	
+            <td><label for="finerotate">Rotation angle (Fine-tune):</label></td>
             <td>
                 <input required type="number" id="finerotate" name="finerotate" value=0.0 min="-1" max="1" step="0.1" onchange="drawRotated()"
                         oninput="(!validity.rangeOverflow||(value=1)) && (!validity.rangeUnderflow||(value=-1)) && 
@@ -152,6 +176,17 @@
                 <output id="TakeImage_Saturation_value1_output" style="vertical-align:middle; min-width:15px; padding-right:5px; text-align:right; float:left">0</output>
             </td>
         </tr>
+        <tr>
+            <td><label for="grayscale" id="labelgrayscale">Grayscale:</label></td>
+            <td><input type="checkbox" id="grayscale" name="grayscale" value="0"></td>
+            <td>
+                <class id="TakeImage_AutoExposureLevel_text" style="color:black;">Auto exposure:</class>
+            </td>
+            <td>
+                <input  style="clear: both; width: 80%;vertical-align:middle" type="range" id="TakeImage_AutoExposureLevel_value1" size="13" value=0 min="-2" max="2" oninput="this.nextElementSibling.value = this.value">
+                <output id="TakeImage_AutoExposureLevel_value1_output" style="vertical-align:middle; min-width:15px; padding-right:5px; text-align:right; float:left">0</output>
+            </td>
+        </tr>
     </table>
     <table>
         <colgroup>
@@ -201,13 +236,24 @@
             var xhttp = new XMLHttpRequest();
             if (param["TakeImage"]["Brightness"].found && param["TakeImage"]["Brightness"].enabled)
             {
+                _grayscale = document.getElementById("grayscale").checked ? "1" : "0";
+                _zoom = document.getElementById("zoom").checked ? "1" : "0";
+                _zm = document.getElementById("zoommode").value;
+                if (_zm == "") _zm = "0";
+                _x = document.getElementById("zoomoffsetx").value;
+                if (_x == "") _x = "0";
+                _y = document.getElementById("zoomoffsety").value;
+                if (_y == "") _y = "0";
                 _intensity = document.getElementById("TakeImage_LEDIntensity_value1").value;
                 if (_intensity == "") _intensity = "50";
                 _brightness = document.getElementById("TakeImage_Brightness_value1").value;
                 _contrast = document.getElementById("TakeImage_Contrast_value1").value;
                 _saturation = document.getElementById("TakeImage_Saturation_value1").value;
+                _ae = document.getElementById("TakeImage_AutoExposureLevel_value1").value;
                 url = getDomainname() + "/editflow?task=test_take&bri=" + _brightness;
-                url = url + "&con=" + _contrast + "&sat=" + _saturation + "&int=" + _intensity;
+                url = url + "&con=" + _contrast + "&sat=" + _saturation + "&int=" + _intensity + "&ae=" + _ae + "&gs=" + _grayscale;
+                if (_zoom != '0')
+                    url = url + "&z=" + _zoom + "&zm=" + _zm + "&x=" + _x + "&y=" + _y;
             }
             else
             {
@@ -235,8 +281,14 @@
                 doTake();
             }
 
+            document.getElementById("grayscale").disabled = false;
+            document.getElementById("zoom").disabled = false;
+            document.getElementById("zoommode").disabled = false;
+            document.getElementById("zoomoffsetx").disabled = false;
+            document.getElementById("zoomoffsety").disabled = false;
+            document.getElementById("TakeImage_AutoExposureLevel_value1").disabled = false;
             document.getElementById("finerotate").disabled = false;
-            document.getElementById("prerotateangle").disabled = false;  
+            document.getElementById("prerotateangle").disabled = false;
             document.getElementById("startreference").disabled = true;
             document.getElementById("take").disabled = false;
             if (param["Alignment"]["InitialMirror"].found)
@@ -296,6 +348,12 @@
             if (_param["Alignment"]["FlipImageSize"].found && (_param["Alignment"]["FlipImageSize"].value1 == "true"))
                 document.getElementById("flip").checked = true;
 
+            document.getElementById("grayscale").disabled = true;
+            document.getElementById("zoom").disabled = true;
+            document.getElementById("zoommode").disabled = true;
+            document.getElementById("zoomoffsetx").disabled = true;
+            document.getElementById("zoomoffsety").disabled = true;
+            document.getElementById("TakeImage_AutoExposureLevel_value1").disabled = true;
             document.getElementById("finerotate").disabled = true;
             document.getElementById("prerotateangle").disabled = true; 
             document.getElementById("updatereferenceimage").disabled = true;
@@ -351,12 +409,27 @@
                 else
                     param["Alignment"]["FlipImageSize"].value1 = "false";
 
+                if ((param["TakeImage"]["Grayscale"].found == true) && (document.getElementById("grayscale").checked))
+                    param["TakeImage"]["Grayscale"].value1 = "true";
+                else
+                    param["TakeImage"]["Grayscale"].value1 = "false";
+
+                if ((param["TakeImage"]["Zoom"].found == true) && (document.getElementById("zoom").checked))
+                    param["TakeImage"]["Zoom"].value1 = "true";
+                else
+                    param["TakeImage"]["Zoom"].value1 = "false";
+
+                param["TakeImage"]["ZoomMode"].value1 = document.getElementById("zoommode").value;
+                param["TakeImage"]["ZoomOffsetX"].value1 = document.getElementById("zoomoffsetx").value;
+                param["TakeImage"]["ZoomOffsetY"].value1 = document.getElementById("zoomoffsety").value;
+
                 if (param["TakeImage"]["Brightness"].found && param["TakeImage"]["Brightness"].enabled)
                 {
-                    ReadParameter(param, "TakeImage", "Brightness", false);		
-                	ReadParameter(param, "TakeImage", "Contrast", false);
+                    ReadParameter(param, "TakeImage", "Brightness", false);
+                    ReadParameter(param, "TakeImage", "Contrast", false);
                     ReadParameter(param, "TakeImage", "Saturation", false);
                     ReadParameter(param, "TakeImage", "LEDIntensity", false);
+                    ReadParameter(param, "TakeImage", "AutoExposureLevel", false);
                 }
 
                 var canvas = document.getElementById("canvas");
@@ -423,11 +496,49 @@
             ParseConfig();
             param = getConfigParameters();
 
+            param["TakeImage"]["AutoExposureLevel"]["enabled"] = true;
             param["TakeImage"]["LEDIntensity"]["enabled"] = true;
             param["TakeImage"]["Brightness"]["enabled"] = true;
             param["TakeImage"]["Contrast"]["enabled"] = true;
             param["TakeImage"]["Saturation"]["enabled"] = true;
+            
+            param["TakeImage"]["Grayscale"]["enabled"] = true;
+            param["TakeImage"]["Zoom"]["enabled"] = true;
+            param["TakeImage"]["ZoomMode"]["enabled"] = true;
+            param["TakeImage"]["ZoomOffsetX"]["enabled"] = true;
+            param["TakeImage"]["ZoomOffsetY"]["enabled"] = true;
+
+            if (!param["TakeImage"]["Grayscale"]["found"])
+            {
+                param["TakeImage"]["Grayscale"]["found"] = true;
+                param["TakeImage"]["Grayscale"].value1 = "false";
+            }
+            if (!param["TakeImage"]["Zoom"]["found"])
+            {
+                param["TakeImage"]["Zoom"]["found"] = true;
+                param["TakeImage"]["Zoom"].value1 = "false";
+            }
+            if (!param["TakeImage"]["ZoomMode"]["found"])
+            {
+                param["TakeImage"]["ZoomMode"]["found"] = true;
+                param["TakeImage"]["ZoomMode"].value1 = "0";
+            }
+            if (!param["TakeImage"]["ZoomOffsetX"]["found"])
+            {
+                param["TakeImage"]["ZoomOffsetX"]["found"] = true;
+                param["TakeImage"]["ZoomOffsetX"].value1 = "0";
+            }
+            if (!param["TakeImage"]["ZoomOffsetY"]["found"])
+            {
+                param["TakeImage"]["ZoomOffsetY"]["found"] = true;
+                param["TakeImage"]["ZoomOffsetY"].value1 = "0";
+            }
 
+            if (!param["TakeImage"]["AutoExposureLevel"]["found"])
+            {
+                param["TakeImage"]["AutoExposureLevel"]["found"] = true;
+                param["TakeImage"]["AutoExposureLevel"]["value1"] = "0";
+            }
             if (!param["TakeImage"]["LEDIntensity"]["found"])
             {
                 param["TakeImage"]["LEDIntensity"]["found"] = true;
@@ -455,10 +566,24 @@
 
 
         function UpdateInput() {
-            WriteParameter(param, category, "TakeImage", "Brightness", false, true);		
-            WriteParameter(param, category, "TakeImage", "Contrast", false, true);		
-            WriteParameter(param, category, "TakeImage", "Saturation", false, true);		
-            WriteParameter(param, category, "TakeImage", "LEDIntensity", false);		
+            WriteParameter(param, category, "TakeImage", "AutoExposureLevel", false, true);
+            WriteParameter(param, category, "TakeImage", "Brightness", false, true);
+            WriteParameter(param, category, "TakeImage", "Contrast", false, true);
+            WriteParameter(param, category, "TakeImage", "Saturation", false, true);
+            WriteParameter(param, category, "TakeImage", "LEDIntensity", false);
+            if (param["TakeImage"]["Grayscale"].value1 == "true") {
+                document.getElementById("grayscale").checked = true;
+            } else {
+                document.getElementById("grayscale").checked = false;
+            }
+            if (param["TakeImage"]["Zoom"].value1 == "true") {
+                document.getElementById("zoom").checked = true;
+            } else {
+                document.getElementById("zoom").checked = false;
+            }
+            document.getElementById("zoommode").value = param["TakeImage"]["ZoomMode"].value1;
+            document.getElementById("zoomoffsetx").value = param["TakeImage"]["ZoomOffsetX"].value1;
+            document.getElementById("zoomoffsety").value = param["TakeImage"]["ZoomOffsetY"].value1;
         }
 
 

+ 8 - 2
sd-card/html/readconfigparam.js

@@ -119,8 +119,14 @@ function ParseConfig() {
      ParamAddValue(param, catname, "Saturation");
      ParamAddValue(param, catname, "LEDIntensity");
      ParamAddValue(param, catname, "ImageQuality");
-     ParamAddValue(param, catname, "ImageSize");     
-     ParamAddValue(param, catname, "FixedExposure");     
+     ParamAddValue(param, catname, "ImageSize");
+     ParamAddValue(param, catname, "Zoom");
+     ParamAddValue(param, catname, "ZoomMode");
+     ParamAddValue(param, catname, "ZoomOffsetX");
+     ParamAddValue(param, catname, "ZoomOffsetY");
+     ParamAddValue(param, catname, "Grayscale");
+     ParamAddValue(param, catname, "AutoExposureLevel");
+     ParamAddValue(param, catname, "FixedExposure");
 
      var catname = "Alignment";
      category[catname] = new Object();