Skocz do zawartości

Ramka na e-paper i ESP32C3 - strona do ditheringu zdjęć


Pomocna odpowiedź

20 minut temu, Gieneq napisał:

Skalowanie i dithering w JS wykonywanym na urządzeniu użytkownika, żeby mógł sobie zrobić podgląd zdjęcia.

Uhm... OpenCV? Bez problemu sobie z tym radzi (i to bardzo szybko).

  • Lubię! 1
  • Pomogłeś! 1

Zaskoczyłeś mnie, OpenCV jest w JS. Ale to chyba za dużo, tu jest coś tylko do tego https://github.com/danielepiccone/ditherjs

Ale w tym momencie bardziej nie rozumiem tych spraw webowych. Jak sobie wyobrazić że użytkownik wybiera obrazek ze swojego systemu plików, to się robi w przegląarce wykonującej JS i na koniec skąś bierze się plik obrazka .BMP który trafia na serwer.

Proof of concept jest 😄 

Skorzystałem z przykładu esp-idf/protocols/http_server/simple i file_server. Z file_server można dojść w jaki sposób wystawić endpoint z plikiem np najprostszy favico.ico czyli ikonka w zakładce. Jest to bardzo proste:

static esp_err_t favicon_get_handler(httpd_req_t *req)
{
    extern const unsigned char favicon_ico_start[] asm("_binary_favicon_ico_start");
    extern const unsigned char favicon_ico_end[]   asm("_binary_favicon_ico_end");
    const size_t favicon_ico_size = (favicon_ico_end - favicon_ico_start);
    httpd_resp_set_type(req, "image/x-icon");
    httpd_resp_send(req, (const char *)favicon_ico_start, favicon_ico_size);
    ESP_LOGW(TAG, ">> Got favicon.ico [%uB]", favicon_ico_size);
    return ESP_OK;
}

static const httpd_uri_t favicon = {
    .uri       = "/favicon.ico",
    .method    = HTTP_GET,
    .handler   = favicon_get_handler,
    .user_ctx  = NULL
};

Trzeba to jeszcze zarejestrować:

    if (httpd_start(&server, &config) == ESP_OK) {
        // Set URI handlers
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &hello);
        httpd_register_uri_handler(server, &echo);
        httpd_register_uri_handler(server, &ctrl);
        httpd_register_uri_handler(server, &favicon);
        httpd_register_uri_handler(server, &main_index);
        httpd_register_uri_handler(server, &main_script);
        httpd_register_uri_handler(server, &main_styles);

        return server;
    }

W podobny sposób client będzie dopytywał się o pliki html, js, css więc dla nich też robię podobne endpointy. Pliki są zaciągane tak samo, dodając je w CMakeLists.txt:

idf_component_register(SRCS "main.c"
                    INCLUDE_DIRS "."
                    EMBED_FILES "favicon.ico" "index.html" "script.js" "styles.css"
                    REQUIRES ${requires})

Dalej zostaje napisanie kodu strony - ten można przetestować poza ESP.

Wspomniana biblioteka niezabardzo działa, jest tam problem z jakimś "CORS" - nie wnikam co to, jak będzie trzeba to się nad tym pochylę. Na jakimś stacku znalazłem coś lepszego - przykład jak wciągnąć obrazek z pliku, zmodyfikować go, narysować na canvasie i pobrać. Pozostało dodać własny algorytm i zamiast pobierania wrzucić na endpoint.

Z wysłaniem na endpoint też prosta sprawa - jest do tego funkcja pobierająca treść body, która buforem pobiera aż do końca. Tu przykład dla endpointa z metodą POST:

    char buf[100];
    int ret, remaining = req->content_len;

    while (remaining > 0) {
        /* Read the data for the request */
        if ((ret = httpd_req_recv(req, buf,
                        MIN(remaining, sizeof(buf)))) <= 0) {
            if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
                /* Retry receiving if timeout occurred */
                continue;
            }
            return ESP_FAIL;
        }

        /* Send back the same data */
        httpd_resp_send_chunk(req, buf, ret);
        remaining -= ret;

        /* Log data received */
        ESP_LOGI(TAG, "=========== RECEIVED DATA ==========");
        ESP_LOGI(TAG, "%.*s", ret, buf);
        ESP_LOGI(TAG, "====================================");
    }

Jak widać POST do endpointa /echo działa:

image.thumb.png.59dd57bad47efad5733b7c7730d666c5.png

I (636201) example: =========== RECEIVED DATA ==========
I (636201) example: Tu bedzie obrazek
I (636201) example: ====================================

Dorzucam jeszcze ten wstępny kod strony:

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <title>Imagesadsfdgfdsa Upload and Trim</title>
    <link rel="stylesheet" href="styles.css">
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="ditherjs.dist.js"></script>
</head>
<body>
    <p> Frame image uploader </p>
    <input type="file" id="imageInput">
    <canvas id="canvas"></canvas>
    <button onClick="brightness(1.5)">Lighter</button>
    <button onClick="brightness(0.5)">Darker</button>
    <button onClick="save()">Save</button>
    <script src="script.js"></script>
</body>
</html>

CSS pominę - nic ciekawego, ale JS może się komuś przyda:

const imageInput = document.getElementById('imageInput');
const canvas = document.getElementById('canvas');
context = canvas.getContext('2d');

imageInput.addEventListener('change', function(event) {
    const file = event.target.files[0];
    
    if (file) {
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = function(e) {
            const img = new Image();
            img.src = e.target.result;
            img.onload = function() {
                context.clearRect(0, 0, canvas.width, canvas.height);
                context.drawImage(img, 0, 0, canvas.width, canvas.height);
            };
        };
    }
});

function saveCanvasAsImage(canvas, filename) {
    const dataURL = canvas.toDataURL('image/png');
    const link = document.createElement('a');
    link.href = dataURL;
    link.download = filename || 'canvas-image.png';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}
const img = new Image();
img.src = `data:image/jpeg;base64,${getImageFromDb()}`;
img.onload = () =>
{
  canvas.width = img.width;
  canvas.height = img.height;
  const imgX = canvas.width/2 - img.width/2;
  const imgY = canvas.height/2 - img.height/2;
  context.drawImage(img, imgX, imgY);
}

function brightness(multiplier) {
  const imgX = canvas.width/2 - img.width/2;
  const imgY = canvas.height/2 - img.height/2;
  const imageData = context.getImageData(imgX, imgY, img.width, img.height);
  const dataArray = imageData.data;
  for(let i = 0; i < dataArray.length; i += 4){
    var red = dataArray[i];
    var green = dataArray[i + 1];
    var blue = dataArray[i + 2];
    dataArray[i] = multiplier * red;
    dataArray[i + 1] = multiplier * green;
    dataArray[i + 2] = multiplier * blue;
  }
  context.putImageData(imageData, imgX, imgY);
};

function save() {
  const dataURL = canvas.toDataURL().replace('data:', '').replace(/^.+,/, '');
  console.log(dataURL); 
  saveCanvasAsImage(canvas, 'my-image.png');
}
function getImageFromDb() {
  return "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";
}

Autor wpisu postanowił dać zakodowany obrazek prosto w kodzie funkcji 😄 

image.thumb.png.c35f00b4e1310913631c6101c3dfd761.png

  • Lubię! 1

Jest dalszy progres w tym temacie. Z pomocą chata GPT dodałem algorytm ditheringu Floyd-Steinberg.

Działa podgląd, obrót obrazka, kursorem myszy można przesunąć obrazek a suwakiem przeskalować. Gamma do ewentualnej poprawy szczegółów.

2 przyciski:

  • po prawej pobiera plik PNG na urządzenie.
  • po lewej gdy strona jest w trybie deweloperskim pobiera plik BMP 1bit per pixel, gdy jest shosytowana zamienia binarkę na stringa i wysyła na endpoint z metodą POST.

Pliki BMP składam generując nagłówek i dodając binarkę na końcu:

function createBMP(monochromeData, width, height) {
  const datalen = monochromeData.length;
  const fileSize = 62 + datalen;
  const header = new Uint8Array([
      /*00*/ 0x42, 0x4D, // BM
      /*02*/ fileSize & 0xff, (fileSize >> 8) & 0xff, (fileSize >> 16) & 0xff, (fileSize >> 24) & 0xff,
      /*06*/ 0x00, 0x00, 0x00, 0x00, // Reserved
      /*0a*/ 0x3e, 0x00, 0x00, 0x00, // Offset to pixel data
      /*0e*/ 0x28, 0x00, 0x00, 0x00, // Info Header size
      /*12*/ width & 0xff, (width >> 8) & 0xff, 0x00, 0x00, // Width
      /*16*/ height & 0xff, (height >> 8) & 0xff, 0x00, 0x00, // Height
      /*1a*/ 0x01, 0x00, // Planes
      /*1c*/ 0x01, 0x00, // Bits per pixel (1-bit)
      /*1e*/ 0x00, 0x00, 0x00, 0x00, // Compression
      /*22*/ datalen & 0xff, (datalen >> 8) & 0xff, (datalen >> 16) & 0xff, (datalen >> 24) & 0xff, // Image size
      /*--*/ 0x00, 0x00, 0x00, 0x00, // X pixels per meter
      /*--*/ 0x00, 0x00, 0x00, 0x00, // Y pixels per meter
      /*--*/ 0x00, 0x00, 0x00, 0x00,
      /*--*/ 0x00, 0x00, 0x00, 0x00, 
      /*--*/ 0x00, 0x00, 0x00, 0x00,
      /*3a*/ 0xff, 0xff, 0xff, 0x00,
  ]);

  const bmpData = new Uint8Array(header.length + monochromeData.length);
  bmpData.set(header, 0);
  bmpData.set(monochromeData, header.length);

  return bmpData;
}

Ramka narazie odczytuje pliki BMP z pamięci, ale to nie problem dodać partycję i zapisywać tam pobrane BMPy.

Kod strony z obrazkami na ikony itp liczy z 25kB, gdyby zminimalizować treść to będzie jeszcze lepiej. Działą ogólnie sprawnie. Na desktopie wygląa ok, ale na smartphonie tak sobie. Później się z tym uporam, ale to może umiesczę w osobnym temacie DIY.

image.thumb.png.7e97ffed1cdcd20fc605ef47536fdf8d.png

  • Lubię! 2
  • 1 miesiąc później...

Jak mniemam rozwiązanie które stosuję u siebie w czytaku (próbuje połączyć się z siecią domową, jak mu nie wyjdzie to próbuje mojego telefonu bo może mam router włączony, a jak i to nie wyjdzie odpala ap) nie wchodzi w grę?

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...