Skocz do zawartości

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


Gieneq

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
Link do komentarza
Share on other sites

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.

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

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
Link do komentarza
Share on other sites

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
Link do komentarza
Share on other sites

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ę?

Link do komentarza
Share on other sites

@ethanak pewnie że tak można, to super metoda. Niedawno pisałem taki program w pracy. Ale jakoś na DIY nie chciało się przenosić 😅

Link do komentarza
Share on other sites

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...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.