| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796 |
- #include "defines.h"
- #include "CImageBasis.h"
- #include "Helper.h"
- #include "psram.h"
- #include "ClassLogFile.h"
- #include "server_ota.h"
- #include <esp_log.h>
- #include "esp_system.h"
- #include <cstring>
- #include <math.h>
- #include <algorithm>
- using namespace std;
- static const char *TAG = "C IMG BASIS";
- bool jpgFileTooLarge = false; // JPG creation verfication
- uint8_t *CImageBasis::RGBImageLock(int _waitmaxsec)
- {
- if (islocked)
- {
- TickType_t xDelay;
- xDelay = 1000 / portTICK_PERIOD_MS;
- for (int i = 0; i <= _waitmaxsec; ++i)
- {
- vTaskDelay(xDelay);
- if (!islocked)
- {
- break;
- }
- }
- }
- if (islocked)
- {
- return NULL;
- }
- return rgb_image;
- }
- void CImageBasis::RGBImageRelease()
- {
- islocked = false;
- }
- uint8_t *CImageBasis::RGBImageGet()
- {
- return rgb_image;
- }
- void writejpghelp(void *context, void *data, int size)
- {
- // ESP_LOGD(TAG, "Size all: %d, size %d", ((ImageData*)context)->size, size);
- ImageData *_zw = (ImageData *)context;
- uint8_t *voidstart = _zw->data;
- uint8_t *datastart = (uint8_t *)data;
- if ((_zw->size < MAX_JPG_SIZE))
- {
- // Abort copy to prevent buffer overflow
- voidstart += _zw->size;
- for (int i = 0; i < size; ++i)
- {
- *(voidstart + i) = *(datastart + i);
- }
- _zw->size += size;
- }
- else
- {
- jpgFileTooLarge = true;
- }
- }
- ImageData *CImageBasis::writeToMemoryAsJPG(const int quality)
- {
- ImageData *ii = new ImageData;
- RGBImageLock();
- stbi_write_jpg_to_func(writejpghelp, ii, width, height, channels, rgb_image, quality);
- RGBImageRelease();
- if (jpgFileTooLarge)
- {
- jpgFileTooLarge = false;
- LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "writeToMemoryAsJPG: Creation aborted! JPG size > preallocated buffer: " + std::to_string(MAX_JPG_SIZE));
- }
- return ii;
- }
- void CImageBasis::writeToMemoryAsJPG(ImageData *i, const int quality)
- {
- ImageData *ii = new ImageData;
- RGBImageLock();
- stbi_write_jpg_to_func(writejpghelp, ii, width, height, channels, rgb_image, quality);
- RGBImageRelease();
- if (jpgFileTooLarge)
- {
- jpgFileTooLarge = false;
- LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "writeToMemoryAsJPG: Creation aborted! JPG size > preallocated buffer: " + std::to_string(MAX_JPG_SIZE));
- }
- memCopy((uint8_t *)ii, (uint8_t *)i, sizeof(ImageData));
- delete ii;
- }
- struct SendJPGHTTP
- {
- httpd_req_t *req;
- esp_err_t res;
- char buf[HTTP_BUFFER_SENT];
- int size = 0;
- };
- inline void writejpgtohttphelp(void *context, void *data, int size)
- {
- SendJPGHTTP *_send = (SendJPGHTTP *)context;
- // data no longer fits in buffer
- if ((_send->size + size) >= HTTP_BUFFER_SENT)
- {
- if (httpd_resp_send_chunk(_send->req, _send->buf, _send->size) != ESP_OK)
- {
- ESP_LOGE(TAG, "File sending failed!");
- _send->res = ESP_FAIL;
- }
- _send->size = 0;
- }
- std::memcpy((void *)(&(_send->buf[0]) + _send->size), data, size);
- _send->size += size;
- }
- esp_err_t CImageBasis::SendJPGtoHTTP(httpd_req_t *_req, const int quality)
- {
- SendJPGHTTP ii;
- ii.req = _req;
- ii.res = ESP_OK;
- ii.size = 0;
- RGBImageLock();
- stbi_write_jpg_to_func(writejpgtohttphelp, &ii, width, height, channels, rgb_image, quality);
- if (ii.size > 0)
- {
- // still send the rest
- if (httpd_resp_send_chunk(_req, (char *)ii.buf, ii.size) != ESP_OK)
- {
- ESP_LOGE(TAG, "File sending failed!");
- ii.res = ESP_FAIL;
- }
- }
- RGBImageRelease();
- return ii.res;
- }
- bool CImageBasis::CopyFromMemory(uint8_t *_source, int _size)
- {
- int gr = height * width * channels;
- // Size does not fit
- if (gr != _size)
- {
- ESP_LOGE(TAG, "Cannot copy image from memory - sizes do not match: should be %d, but is %d", _size, gr);
- return false;
- }
- RGBImageLock();
- memCopy(_source, rgb_image, _size);
- RGBImageRelease();
- return true;
- }
- uint8_t CImageBasis::GetPixelColor(int x, int y, int ch)
- {
- stbi_uc *p_source;
- p_source = rgb_image + (channels * (y * width + x));
- return p_source[ch];
- }
- void CImageBasis::memCopy(uint8_t *_source, uint8_t *_target, int _size)
- {
- #if CONFIG_SPIRAM
- for (int i = 0; i < _size; ++i)
- {
- *(_target + i) = *(_source + i);
- }
- #else
- memcpy(_target, _source, _size);
- #endif
- }
- bool CImageBasis::isInImage(int x, int y)
- {
- if ((x < 0) || (x > width - 1))
- {
- return false;
- }
- if ((y < 0) || (y > height - 1))
- {
- return false;
- }
- return true;
- }
- void CImageBasis::setPixelColor(int x, int y, int r, int g, int b)
- {
- stbi_uc *p_source;
- RGBImageLock();
- p_source = rgb_image + (channels * (y * width + x));
- p_source[0] = r;
- if (channels > 2)
- {
- p_source[1] = g;
- p_source[2] = b;
- }
- RGBImageRelease();
- }
- void CImageBasis::drawRect(int x, int y, int dx, int dy, int r, int g, int b, int thickness)
- {
- int zwx1, zwx2, zwy1, zwy2;
- int _x, _y, _thick;
- zwx1 = x - thickness + 1;
- zwx2 = x + dx + thickness - 1;
- zwy1 = y;
- zwy2 = y;
- RGBImageLock();
- for (_thick = 0; _thick < thickness; _thick++)
- {
- for (_x = zwx1; _x <= zwx2; ++_x)
- {
- for (_y = zwy1; _y <= zwy2; _y++)
- {
- if (isInImage(_x, _y))
- {
- setPixelColor(_x, _y - _thick, r, g, b);
- }
- }
- }
- }
- zwx1 = x - thickness + 1;
- zwx2 = x + dx + thickness - 1;
- zwy1 = y + dy;
- zwy2 = y + dy;
- for (_thick = 0; _thick < thickness; _thick++)
- {
- for (_x = zwx1; _x <= zwx2; ++_x)
- {
- for (_y = zwy1; _y <= zwy2; _y++)
- {
- if (isInImage(_x, _y))
- {
- setPixelColor(_x, _y + _thick, r, g, b);
- }
- }
- }
- }
- zwx1 = x;
- zwx2 = x;
- zwy1 = y;
- zwy2 = y + dy;
- for (_thick = 0; _thick < thickness; _thick++)
- {
- for (_x = zwx1; _x <= zwx2; ++_x)
- {
- for (_y = zwy1; _y <= zwy2; _y++)
- {
- if (isInImage(_x, _y))
- {
- setPixelColor(_x - _thick, _y, r, g, b);
- }
- }
- }
- }
- zwx1 = x + dx;
- zwx2 = x + dx;
- zwy1 = y;
- zwy2 = y + dy;
- for (_thick = 0; _thick < thickness; _thick++)
- {
- for (_x = zwx1; _x <= zwx2; ++_x)
- {
- for (_y = zwy1; _y <= zwy2; _y++)
- {
- if (isInImage(_x, _y))
- {
- setPixelColor(_x + _thick, _y, r, g, b);
- }
- }
- }
- }
- RGBImageRelease();
- }
- void CImageBasis::drawLine(int x1, int y1, int x2, int y2, int r, int g, int b, int thickness)
- {
- int _x, _y, _thick;
- int _zwy1, _zwy2;
- thickness = (thickness - 1) / 2;
- RGBImageLock();
- for (_thick = 0; _thick <= thickness; ++_thick)
- {
- for (_x = x1 - _thick; _x <= x2 + _thick; ++_x)
- {
- if (x2 == x1)
- {
- _zwy1 = y1;
- _zwy2 = y2;
- }
- else
- {
- _zwy1 = (y2 - y1) * (float)(_x - x1) / (float)(x2 - x1) + y1;
- _zwy2 = (y2 - y1) * (float)(_x + 1 - x1) / (float)(x2 - x1) + y1;
- }
- for (_y = _zwy1 - _thick; _y <= _zwy2 + _thick; _y++)
- {
- if (isInImage(_x, _y))
- {
- setPixelColor(_x, _y, r, g, b);
- }
- }
- }
- }
- RGBImageRelease();
- }
- void CImageBasis::drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int b, int thickness)
- {
- float deltarad, aktrad;
- int _thick, _x, _y;
- int rad = radx;
- if (rady > radx)
- {
- rad = rady;
- }
- deltarad = 1 / (4 * M_PI * (rad + thickness - 1));
- RGBImageLock();
- for (aktrad = 0; aktrad <= (2 * M_PI); aktrad += deltarad)
- {
- for (_thick = 0; _thick < thickness; ++_thick)
- {
- _x = sin(aktrad) * (radx + _thick) + x1;
- _y = cos(aktrad) * (rady + _thick) + y1;
- if (isInImage(_x, _y))
- {
- setPixelColor(_x, _y, r, g, b);
- }
- }
- }
- RGBImageRelease();
- }
- void CImageBasis::drawCircle(int x1, int y1, int rad, int r, int g, int b, int thickness)
- {
- float deltarad, aktrad;
- int _thick, _x, _y;
- deltarad = 1 / (4 * M_PI * (rad + thickness - 1));
- RGBImageLock();
- for (aktrad = 0; aktrad <= (2 * M_PI); aktrad += deltarad)
- {
- for (_thick = 0; _thick < thickness; ++_thick)
- {
- _x = sin(aktrad) * (rad + _thick) + x1;
- _y = cos(aktrad) * (rad + _thick) + y1;
- if (isInImage(_x, _y))
- {
- setPixelColor(_x, _y, r, g, b);
- }
- }
- }
- RGBImageRelease();
- }
- CImageBasis::CImageBasis(string _name)
- {
- name = _name;
- externalImage = false;
- rgb_image = NULL;
- width = 0;
- height = 0;
- channels = 0;
- islocked = false;
- }
- void CImageBasis::CreateEmptyImage(int _width, int _height, int _channels)
- {
- bpp = _channels;
- width = _width;
- height = _height;
- channels = _channels;
- RGBImageLock();
- memsize = width * height * channels;
- if (name == "TempImage")
- {
- rgb_image = (unsigned char *)psram_reserve_shared_tmp_image_memory();
- }
- else
- {
- rgb_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
- }
- if (rgb_image == NULL)
- {
- LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CreateEmptyImage: Can't allocate enough memory: " + std::to_string(memsize));
- LogFile.WriteHeapInfo("CreateEmptyImage");
- RGBImageRelease();
- return;
- }
- stbi_uc *p_source;
- for (int x = 0; x < width; ++x)
- {
- for (int y = 0; y < height; ++y)
- {
- p_source = rgb_image + (channels * (y * width + x));
- for (int _channels = 0; _channels < channels; ++_channels)
- {
- p_source[_channels] = (uint8_t)0;
- }
- }
- }
- RGBImageRelease();
- }
- void CImageBasis::EmptyImage()
- {
- stbi_uc *p_source;
- RGBImageLock();
- for (int x = 0; x < width; ++x)
- {
- for (int y = 0; y < height; ++y)
- {
- p_source = rgb_image + (channels * (y * width + x));
- for (int _channels = 0; _channels < channels; ++_channels)
- {
- p_source[_channels] = (uint8_t)0;
- }
- }
- }
- RGBImageRelease();
- }
- void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len)
- {
- RGBImageLock();
- if (rgb_image != NULL)
- {
- stbi_image_free(rgb_image);
- }
- 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);
- if ((width * height * channels) == 0)
- {
- LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Image with size 0 loaded --> reboot to be done! "
- "Check that your camera module is working and connected properly.");
- LogFile.WriteHeapInfo("LoadFromMemory");
- doReboot();
- }
- RGBImageRelease();
- }
- 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;
- islocked = false;
- externalImage = false;
- channels = _copyfrom->channels;
- width = _copyfrom->width;
- height = _copyfrom->height;
- bpp = _copyfrom->bpp;
- RGBImageLock();
- memsize = width * height * channels;
- if (name == "TempImage")
- {
- rgb_image = (unsigned char *)psram_reserve_shared_tmp_image_memory();
- }
- else
- {
- rgb_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
- }
- if (rgb_image == NULL)
- {
- LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CImageBasis-Copyfrom: Can't allocate enough memory: " + std::to_string(memsize));
- LogFile.WriteHeapInfo("CImageBasis-Copyfrom");
- RGBImageRelease();
- return;
- }
- memCopy(_copyfrom->rgb_image, rgb_image, memsize);
- RGBImageRelease();
- }
- CImageBasis::CImageBasis(string _name, int _width, int _height, int _channels)
- {
- name = _name;
- islocked = false;
- externalImage = false;
- channels = _channels;
- width = _width;
- height = _height;
- bpp = _channels;
- RGBImageLock();
- memsize = width * height * channels;
- if (name == "TempImage")
- {
- rgb_image = (unsigned char *)psram_reserve_shared_tmp_image_memory();
- }
- else
- {
- rgb_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
- }
- if (rgb_image == NULL)
- {
- LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CImageBasis-width,height,ch: Can't allocate enough memory: " + std::to_string(memsize));
- LogFile.WriteHeapInfo("CImageBasis-width,height,ch");
- RGBImageRelease();
- return;
- }
- RGBImageRelease();
- }
- CImageBasis::CImageBasis(string _name, std::string _image)
- {
- name = _name;
- islocked = false;
- channels = 3;
- externalImage = false;
- filename = _image;
- if (file_size(_image.c_str()) == 0)
- {
- LogFile.WriteToFile(ESP_LOG_ERROR, TAG, _image + " is empty!");
- return;
- }
- RGBImageLock();
- rgb_image = stbi_load(_image.c_str(), &width, &height, &bpp, channels);
- if (rgb_image == NULL)
- {
- LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CImageBasis-image: Failed to load " + _image + "! Is it corrupted?");
- LogFile.WriteHeapInfo("CImageBasis-image");
- RGBImageRelease();
- return;
- }
- RGBImageRelease();
- }
- bool CImageBasis::ImageOkay()
- {
- return rgb_image != NULL;
- }
- CImageBasis::CImageBasis(string _name, uint8_t *_rgb_image, int _channels, int _width, int _height, int _bpp)
- {
- name = _name;
- islocked = false;
- rgb_image = _rgb_image;
- channels = _channels;
- width = _width;
- height = _height;
- bpp = _bpp;
- externalImage = true;
- }
- 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();
- }
- // input range [-100..100]
- void CImageBasis::Contrast(float _contrast)
- {
- stbi_uc *p_source;
- float contrast = (_contrast / 100) + 1; // convert to decimal & shift range: [0..2]
- float intercept = 128 * (1 - contrast);
- RGBImageLock();
- for (int x = 0; x < width; ++x)
- {
- for (int y = 0; y < height; ++y)
- {
- p_source = rgb_image + (channels * (y * width + x));
- for (int _channels = 0; _channels < channels; ++_channels)
- {
- p_source[_channels] = (uint8_t)std::min(255, std::max(0, (int)(p_source[_channels] * contrast + intercept)));
- }
- }
- }
- RGBImageRelease();
- }
- CImageBasis::~CImageBasis()
- {
- RGBImageLock();
- if (!externalImage)
- {
- if (name == "TempImage")
- {
- // This image should be placed in the shared part of PSRAM
- psram_free_shared_temp_image_memory();
- }
- else
- {
- // All other images are much smaller and can go into the normal PSRAM region
- if (memsize == 0)
- {
- LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Not freeing (" + name + " as there was never PSRAM allocated for it)");
- }
- else
- {
- free_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ", " + to_string(memsize) + ")", rgb_image);
- }
- }
- }
- RGBImageRelease();
- }
- void CImageBasis::SaveToFile(std::string _imageout)
- {
- string typ = get_file_type(_imageout);
- RGBImageLock();
- if ((typ == "jpg") || (typ == "JPG"))
- {
- // CAUTION PROBLEMATIC IN ESP32
- stbi_write_jpg(_imageout.c_str(), width, height, channels, rgb_image, 0);
- }
- #ifndef STBI_ONLY_JPEG
- if ((typ == "bmp") || (typ == "BMP"))
- {
- stbi_write_bmp(_imageout.c_str(), width, height, channels, rgb_image);
- }
- #endif
- RGBImageRelease();
- }
- void CImageBasis::Resize(int _new_dx, int _new_dy)
- {
- memsize = _new_dx * _new_dy * channels;
- uint8_t *temp_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->temp_image", memsize, MALLOC_CAP_SPIRAM);
- RGBImageLock();
- stbir_resize_uint8(rgb_image, width, height, 0, temp_image, _new_dx, _new_dy, 0, channels);
- rgb_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->CImageBasis Resize (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
- memCopy(temp_image, rgb_image, memsize);
- width = _new_dx;
- height = _new_dy;
- free_psram_heap(std::string(TAG) + "->temp_image", temp_image);
- RGBImageRelease();
- }
- void CImageBasis::Resize(int _new_dx, int _new_dy, CImageBasis *_target)
- {
- if ((_target->height != _new_dy) || (_target->width != _new_dx) || (_target->channels != channels))
- {
- ESP_LOGE(TAG, "Resize - Target image size does not fit!");
- return;
- }
- RGBImageLock();
- uint8_t *temp_image = _target->rgb_image;
- stbir_resize_uint8(rgb_image, width, height, 0, temp_image, _new_dx, _new_dy, 0, channels);
- RGBImageRelease();
- }
|