ov2640: support sharpness control

This commit is contained in:
Joo Aun Saw
2024-01-30 17:27:57 +11:00
committed by CaCO3
parent a8fb88a35d
commit 69f1a99b55
10 changed files with 276 additions and 49 deletions

View File

@@ -31,6 +31,8 @@
#include "driver/ledc.h"
#include "MainFlowControl.h"
#include "ov2640_sharpness.h"
#if (ESP_IDF_VERSION_MAJOR >= 5)
#include "soc/periph_defs.h"
#include "esp_private/periph_ctrl.h"
@@ -53,9 +55,9 @@ 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
// OV2640 Camera SDE Indirect Register Access
#define OV2640_IRA_BPADDR 0x7C
#define OV2640_IRA_BPDATA 0x7D
static camera_config_t camera_config = {
@@ -170,25 +172,49 @@ static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size
}
bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation, int _autoExposureLevel, bool _grayscale, bool _negative, bool _aec2)
bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation, int _autoExposureLevel, bool _grayscale, bool _negative, bool _aec2, int _sharpnessLevel)
{
_brightness = min(2, max(-2, _brightness));
_contrast = min(2, max(-2, _contrast));
_saturation = min(2, max(-2, _saturation));
_autoExposureLevel = min(2, max(-2, _autoExposureLevel));
bool _autoSharpness = false;
if (_sharpnessLevel <= -4)
_autoSharpness = true;
_sharpnessLevel = min(3, max(-3, _sharpnessLevel));
sensor_t * s = esp_camera_sensor_get();
if (s) {
// camera gives precedence to negative over grayscale, so it's easier to do negative ourselves.
// if (_negative) {
// s->set_special_effect(s, 1); // 0 - no effect, 1 - negative, 2 - grayscale, 3 - reddish, 4 - greenish, 5 - blue, 6 - retro
// }
if (_grayscale) {
s->set_special_effect(s, 2); // 0 - no effect, 1 - negative, 2 - grayscale, 3 - reddish, 4 - greenish, 5 - blue, 6 - retro
}
// auto exposure controls
s->set_aec2(s, _aec2 ? 1 : 0);
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
if (_autoSharpness) {
s->set_sharpness(s, 0); // auto-sharpness is not officially supported, default to 0
}
s->set_saturation(s, _saturation);
s->set_contrast(s, _contrast);
s->set_brightness(s, _brightness);
camera_sensor_info_t *sensor_info = esp_camera_sensor_get_info(&(s->id));
if (sensor_info != NULL) {
if (sensor_info->model == CAMERA_OV2640) {
if (_autoSharpness) {
ov2640_enable_auto_sharpness(s);
} else {
ov2640_set_sharpness(s, _sharpnessLevel);
}
/* Workaround - bug in cam library - enable bits are set without using bitwise OR logic -> only latest enable setting is used */
/* Library version: https://github.com/espressif/esp32-camera/commit/5c8349f4cf169c8a61283e0da9b8cff10994d3f3 */
/* Reference: https://esp32.com/viewtopic.php?f=19&t=14376#p93178 */
@@ -220,16 +246,19 @@ bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, in
if (_grayscale) {
indirectReg0 |= 0x18;
}
if (_negative) {
indirectReg0 |= 0x40;
}
// camera gives precedence to negative over grayscale, so it's easier to do negative ourselves.
// if (_negative) {
// indirectReg0 |= 0x40;
// }
// 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, indirectReg0);
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);
s->set_reg(s, OV2640_IRA_BPADDR, 0xFF, 0x00); // Address 0x00
s->set_reg(s, OV2640_IRA_BPDATA, 0xFF, indirectReg0);
s->set_reg(s, OV2640_IRA_BPADDR, 0xFF, 0x05); // Address 0x05
s->set_reg(s, OV2640_IRA_BPDATA, 0xFF, 0x80);
s->set_reg(s, OV2640_IRA_BPDATA, 0xFF, 0x80);
}
}
}
else {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetBrightnessContrastSaturation: Failed to get control structure");
@@ -245,6 +274,8 @@ bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, in
imageGrayscale = _grayscale;
imageNegative = _negative;
imageAec2 = _aec2;
imageAutoSharpness = _autoSharpness;
imageSharpnessLevel = _sharpnessLevel;
ESP_LOGD(TAG, "brightness %d, contrast: %d, saturation %d, autoExposureLevel %d, grayscale %d", brightness, contrast, saturation, autoExposureLevel, (int)imageGrayscale);
@@ -471,6 +502,10 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
return ESP_OK;
}
if (imageNegative) {
_zwImage->Negative();
}
stbi_uc* p_target;
stbi_uc* p_source;
int channels = 3;

View File

@@ -42,6 +42,8 @@ class CCamera {
int imageZoomOffsetY = 0;
bool imageNegative = false;
bool imageAec2 = false;
bool imageAutoSharpness = false;
int imageSharpnessLevel = 0;
#ifdef GRAYSCALE_AS_DEFAULT
bool imageGrayscale = true;
#else
@@ -56,7 +58,7 @@ class CCamera {
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 zoomEnabled, int zoomMode, int zoomOffsetX, int zoomOffsetY);
bool SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation, int _autoExposureLevel, bool _grayscale, bool _negative, bool _aec2);
bool SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation, int _autoExposureLevel, bool _grayscale, bool _negative, bool _aec2, int _sharpnessLevel);
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);

View File

@@ -0,0 +1,124 @@
#include <stdint.h>
#include "esp_camera.h"
#include "ov2640_sharpness.h"
#define OV2640_MAXLEVEL_SHARPNESS 6
const static uint8_t OV2640_SHARPNESS_AUTO[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0x20, 0x20,
0x00, 0x00, 0x00
};
const static uint8_t OV2640_SHARPNESS_MANUAL[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0x00, 0x20,
0x00, 0x00, 0x00
};
const static uint8_t OV2640_SHARPNESS_LEVEL0[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xc0, 0x1f,
0x00, 0x00, 0x00
};
const static uint8_t OV2640_SHARPNESS_LEVEL1[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xc1, 0x1f,
0x00, 0x00, 0x00
};
const static uint8_t OV2640_SHARPNESS_LEVEL2[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xc2, 0x1f,
0x00, 0x00, 0x00
};
const static uint8_t OV2640_SHARPNESS_LEVEL3[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xc4, 0x1f,
0x00, 0x00, 0x00
};
const static uint8_t OV2640_SHARPNESS_LEVEL4[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xc8, 0x1f,
0x00, 0x00, 0x00
};
const static uint8_t OV2640_SHARPNESS_LEVEL5[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xd0, 0x1f,
0x00, 0x00, 0x00
};
const static uint8_t OV2640_SHARPNESS_LEVEL6[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xdf, 0x1f,
0x00, 0x00, 0x00
};
const static uint8_t *OV2640_SETTING_SHARPNESS[]=
{
OV2640_SHARPNESS_LEVEL0, // -3 sharpness
OV2640_SHARPNESS_LEVEL1,
OV2640_SHARPNESS_LEVEL2,
OV2640_SHARPNESS_LEVEL3,
OV2640_SHARPNESS_LEVEL4,
OV2640_SHARPNESS_LEVEL5,
OV2640_SHARPNESS_LEVEL6 // +3 sharpness
};
static int table_mask_write(sensor_t *sensor, const uint8_t* ptab)
{
uint8_t address;
uint8_t value;
uint8_t orgval;
uint8_t mask;
const uint8_t *pdata = ptab;
if (pdata == NULL)
return -1;
while (1)
{
address = *pdata++;
value = *pdata++;
mask = *pdata++;
if ((address == 0) && (value == 0) && (mask == 0))
break;
sensor->set_reg(sensor, address, mask, value);
}
return 0;
}
int ov2640_enable_auto_sharpness(sensor_t *sensor)
{
table_mask_write(sensor, OV2640_SHARPNESS_AUTO);
return 0;
}
int ov2640_set_sharpness(sensor_t *sensor, int sharpness)
{
if ((sharpness < -3) || (sharpness > OV2640_MAXLEVEL_SHARPNESS - 3))
return -1;
table_mask_write(sensor, OV2640_SHARPNESS_MANUAL);
table_mask_write(sensor, OV2640_SETTING_SHARPNESS[sharpness + 3]);
return 0;
}

View File

@@ -0,0 +1,11 @@
#pragma once
#ifndef OV2640_SHARPNESS_H
#define OV2640_SHARPNESS_H
#include "esp_camera.h"
int ov2640_enable_auto_sharpness(sensor_t *sensor);
int ov2640_set_sharpness(sensor_t *sensor, int sharpness); // -3 to +3, -4 for auto-sharpness
#endif

View File

@@ -84,6 +84,7 @@ bool ClassFlowTakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
int _brightness = -100;
int _contrast = -100;
int _saturation = -100;
int _sharpness = 0;
int _autoExposureLevel = 0;
if (aktparamgraph.size() == 0)
@@ -178,6 +179,11 @@ bool ClassFlowTakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
_saturation = stoi(splitted[1]);
}
if ((toUpper(splitted[0]) == "SHARPNESS") && (splitted.size() > 1))
{
_sharpness = stoi(splitted[1]);
}
if ((toUpper(splitted[0]) == "FIXEDEXPOSURE") && (splitted.size() > 1))
{
if (toUpper(splitted[1]) == "TRUE")
@@ -199,7 +205,7 @@ bool ClassFlowTakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
}
}
Camera.SetBrightnessContrastSaturation(_brightness, _contrast, _saturation, _autoExposureLevel, ImageGrayscale, ImageNegative, ImageAec2);
Camera.SetBrightnessContrastSaturation(_brightness, _contrast, _saturation, _autoExposureLevel, ImageGrayscale, ImageNegative, ImageAec2, _sharpness);
Camera.SetQualitySize(ImageQuality, ImageSize, ZoomEnabled, ZoomMode, zoomOffsetX, zoomOffsetY);
image_width = Camera.image_width;

View File

@@ -703,6 +703,7 @@ esp_err_t handler_editflow(httpd_req_t *req)
bool zoom = false;
bool negative = false;
bool aec2 = false;
int sharpnessLevel = 0;
#ifdef GRAYSCALE_AS_DEFAULT
bool grayscale = true;
#else
@@ -732,6 +733,10 @@ esp_err_t handler_editflow(httpd_req_t *req)
std::string _ae = std::string(_valuechar);
aelevel = stoi(_ae);
}
if (httpd_query_key_value(_query, "sh", _valuechar, 30) == ESP_OK) {
std::string _sh = std::string(_valuechar);
sharpnessLevel = stoi(_sh);
}
if (httpd_query_key_value(_query, "gs", _valuechar, 30) == ESP_OK) {
std::string _gr = std::string(_valuechar);
if (stoi(_gr) != 0)
@@ -776,7 +781,7 @@ esp_err_t handler_editflow(httpd_req_t *req)
// ESP_LOGD(TAG, "Parameter host: %s", _host.c_str());
// string zwzw = "Do " + _task + " start\n"; ESP_LOGD(TAG, zwzw.c_str());
Camera.SetZoom(zoom, zoommode, zoomoffsetx, zoomoffsety);
Camera.SetBrightnessContrastSaturation(bri, con, sat, aelevel, grayscale, negative, aec2);
Camera.SetBrightnessContrastSaturation(bri, con, sat, aelevel, grayscale, negative, aec2, sharpnessLevel);
Camera.SetLEDIntensity(intens);
ESP_LOGD(TAG, "test_take - vor TakeImage");
std::string zw = flowctrl.doSingleStep("[TakeImage]", _host);

View File

@@ -636,6 +636,20 @@ CImageBasis::CImageBasis(string _name, uint8_t* _rgb_image, int _channels, int _
}
void CImageBasis::Negative(void)
{
RGBImageLock();
for (int i = 0; i < width * height * channels; i += channels) {
for (int c = 0; c < channels; c++) {
rgb_image[i+c] = 255 - rgb_image[i+c];
}
}
RGBImageRelease();
}
void CImageBasis::Contrast(float _contrast) //input range [-100..100]
{
stbi_uc* p_source;

View File

@@ -56,6 +56,7 @@ class CImageBasis
void drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int b, int thickness = 1);
void setPixelColor(int x, int y, int r, int g, int b);
void Negative(void);
void Contrast(float _contrast);
bool ImageOkay();
bool CopyFromMemory(uint8_t* _source, int _size);

View File

@@ -184,7 +184,7 @@
</tr>
<tr>
<td><label for="negative" id="labelnegative">Negative:</label></td>
<td><input type="checkbox" id="negative" name="negative" value="0"></td>
<td><input type="checkbox" id="negative" name="negative" value="0" onchange="drawRotated()"></td>
<td>
<class id="TakeImage_AutoExposureLevel_text" style="color:black;">Auto exposure:</class>
</td>
@@ -193,6 +193,17 @@
<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>
<tr>
<td></td>
<td></td>
<td>
<class id="TakeImage_Sharpness_text" style="color:black;">Sharpness:</class>
</td>
<td>
<input style="clear: both; width: 80%;vertical-align:middle" type="range" id="TakeImage_Sharpness_value1" size="13" value=0 min="-4" max="3" oninput="this.nextElementSibling.value = this.value">
<output id="TakeImage_Sharpness_value1_output" style="vertical-align:middle; min-width:15px; padding-right:5px; text-align:right; float:left">0</output>
</td>
</tr>
</table>
<table>
<colgroup>
@@ -257,11 +268,13 @@
_brightness = document.getElementById("TakeImage_Brightness_value1").value;
_contrast = document.getElementById("TakeImage_Contrast_value1").value;
_saturation = document.getElementById("TakeImage_Saturation_value1").value;
_sharpness = document.getElementById("TakeImage_Sharpness_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 + "&ae=" + _ae + "&gs=" + _grayscale + "&ne=" + _negative + "&a2=" + _aec2;
url = url + "&con=" + _contrast + "&sat=" + _saturation + "&sh=" + _sharpness + "&int=" + _intensity + "&gs=" + _grayscale + "&ne=" + _negative + "&z=" + _zoom;
if (_zoom != '0')
url = url + "&z=" + _zoom + "&zm=" + _zm + "&x=" + _x + "&y=" + _y;
url = url + "&zm=" + _zm + "&x=" + _x + "&y=" + _y;
url = url + "&ae=" + _ae + "&a2=" + _aec2;
}
else
{
@@ -320,6 +333,7 @@
document.getElementById("TakeImage_Brightness_value1").disabled = false;
document.getElementById("TakeImage_Contrast_value1").disabled = false;
document.getElementById("TakeImage_Saturation_value1").disabled = false;
document.getElementById("TakeImage_Sharpness_value1").disabled = false;
document.getElementById("TakeImage_LEDIntensity_value1").disabled = false;
}
else
@@ -374,6 +388,7 @@
document.getElementById("TakeImage_Brightness_value1").disabled = true;
document.getElementById("TakeImage_Saturation_value1").disabled = true;
document.getElementById("TakeImage_Contrast_value1").disabled = true;
document.getElementById("TakeImage_Sharpness_value1").disabled = true;
document.getElementById("TakeImage_LEDIntensity_value1").disabled = true;
document.getElementById("mirror").disabled = false;
document.getElementById("flip").disabled = false;
@@ -450,6 +465,7 @@
ReadParameter(param, "TakeImage", "Brightness", false);
ReadParameter(param, "TakeImage", "Contrast", false);
ReadParameter(param, "TakeImage", "Saturation", false);
ReadParameter(param, "TakeImage", "Sharpness", false);
ReadParameter(param, "TakeImage", "LEDIntensity", false);
ReadParameter(param, "TakeImage", "AutoExposureLevel", false);
}
@@ -523,6 +539,7 @@
param["TakeImage"]["Brightness"]["enabled"] = true;
param["TakeImage"]["Contrast"]["enabled"] = true;
param["TakeImage"]["Saturation"]["enabled"] = true;
param["TakeImage"]["Sharpness"]["enabled"] = true;
param["TakeImage"]["Grayscale"]["enabled"] = true;
param["TakeImage"]["Negative"]["enabled"] = true;
@@ -593,6 +610,11 @@
param["TakeImage"]["Saturation"]["found"] = true;
param["TakeImage"]["Saturation"]["value1"] = "0";
}
if (!param["TakeImage"]["Sharpness"]["found"])
{
param["TakeImage"]["Sharpness"]["found"] = true;
param["TakeImage"]["Sharpness"]["value1"] = "0";
}
UpdateInput();
showReference(param);
@@ -604,6 +626,7 @@
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", "Sharpness", false, true);
WriteParameter(param, category, "TakeImage", "LEDIntensity", false);
if (param["TakeImage"]["Grayscale"].value1 == "true") {
document.getElementById("grayscale").checked = true;
@@ -724,6 +747,11 @@
context.clearRect(0,0,canvas.width,canvas.height);
context.save();
negative = document.getElementById("negative").checked;
if (negative) {
context.filter = 'invert(1)';
}
if (isActReference)
{
context.drawImage(imageObj,0,0);

View File

@@ -117,6 +117,7 @@ function ParseConfig() {
ParamAddValue(param, catname, "Brightness");
ParamAddValue(param, catname, "Contrast");
ParamAddValue(param, catname, "Saturation");
ParamAddValue(param, catname, "Sharpness");
ParamAddValue(param, catname, "LEDIntensity");
ParamAddValue(param, catname, "ImageQuality");
ParamAddValue(param, catname, "ImageSize");