{"id":878,"date":"2025-11-19T15:22:37","date_gmt":"2025-11-19T06:22:37","guid":{"rendered":"http:\/\/practical.kr\/?p=878"},"modified":"2025-11-19T15:22:37","modified_gmt":"2025-11-19T06:22:37","slug":"esp32%ec%97%90%ec%84%9c-opencv-%ed%9d%89%eb%82%b4%eb%82%b4%ea%b8%b0","status":"publish","type":"post","link":"http:\/\/practical.kr\/?p=878","title":{"rendered":"ESP32\uc5d0\uc11c OpenCV \ud749\ub0b4\ub0b4\uae30"},"content":{"rendered":"\n<p>ESP32 \ub9c8\uc774\ud06c\ub85c\ucee8\ud2b8\ub864\ub7ec\ub294 WiFi\/Bluetooth\ub97c \ub0b4\uc7a5\ud55c \uc800\uc804\ub825\u00b7\uc800\uac00\uaca9 \uce69\uc73c\ub85c, IoT \uce74\uba54\ub77c \ud504\ub85c\uc81d\ud2b8\uc5d0 \ub110\ub9ac \uc0ac\uc6a9\ub41c\ub2e4. \ud558\uc9c0\ub9cc \uc81c\ud55c\ub41c \uba54\ubaa8\ub9ac(SRAM 512KB)\uc640 \uc5f0\uc0b0 \ub2a5\ub825 \ub54c\ubb38\uc5d0 OpenCV \uac19\uc740 \ubcf8\uaca9\uc801\uc778 \ucef4\ud4e8\ud130 \ube44\uc804 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \ud3ec\ud305\ud558\uae30 \uc5b4\ub835\ub2e4. \ub300\ubd80\ubd84\uc758 ESP32 \uce74\uba54\ub77c \ud504\ub85c\uc81d\ud2b8\ub294 JPEG \uc2a4\ud2b8\ub9ac\ubc0d\uc774\ub098 \ub2e8\uc21c \ucea1\ucc98\uc5d0 \uadf8\uce58\uba70, \uc5e3\uc9c0\uc5d0\uc11c \uc2e4\uc2dc\uac04 \uc774\ubbf8\uc9c0 \ucc98\ub9ac\ub97c \uc218\ud589\ud558\ub824\uba74 \uc678\ubd80 \uc11c\ubc84\ub098 PC\ub85c \uc804\uc1a1 \ud6c4 \ucc98\ub9ac\ud558\ub294 \ubc29\uc2dd\uc744 \ud0dd\ud574\uc57c \ud588\ub2e4.<\/p>\n\n\n\n<p>\uadf8\ub7f0\ub370 \ucd5c\uadfc grayskull \uc624\ud508 \uc18c\uc2a4\ub97c \ubcf4\uace0 \uc5d0\uc9c0\uc5d0\uc11c\ub3c4 \uc774\ubbf8\uc9c0 \ud504\ub85c\uc138\uc2f1\uc774 \uac00\ub2a5\ud558\uaca0\ub2e4\ub294 \uc0dd\uac01\uc774 \ub4e4\uc5b4\uc11c \uc791\uc5c5\uc744 \ud55c\ubc88 \ud574\ubd24\ub2e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Grayskull: \uc784\ubca0\ub514\ub4dc\uc6a9 \ucef4\ud4e8\ud130 \ube44\uc804 \ub77c\uc774\ube0c\ub7ec\ub9ac<\/h2>\n\n\n\n<p><a href=\"https:\/\/github.com\/zserge\/grayskull\">Grayskull<\/a>\uc740 \ub9c8\uc774\ud06c\ub85c\ucee8\ud2b8\ub864\ub7ec\ub97c \ud0c0\uac9f\uc73c\ub85c \uc124\uacc4\ub41c \uacbd\ub7c9 \ucef4\ud4e8\ud130 \ube44\uc804 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub2e4. \uc8fc\uc694 \ud2b9\uc9d5\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\ub2e8\uc77c \ud5e4\ub354 \ud30c\uc77c<\/strong> (<code>grayskull.h<\/code>) &#8211; \uc758\uc874\uc131 \uc5c6\uc74c<\/li>\n\n\n\n<li><strong>C99 \ud45c\uc900<\/strong> &#8211; Arduino \ud658\uacbd \ud3ec\ud568 \ub300\ubd80\ubd84\uc758 \uc784\ubca0\ub514\ub4dc \ud50c\ub7ab\ud3fc \uc9c0\uc6d0<\/li>\n\n\n\n<li><strong>\ub3d9\uc801 \uba54\ubaa8\ub9ac \ud560\ub2f9 \ubd88\ud544\uc694<\/strong> &#8211; \uc0ac\uc6a9\uc790\uac00 \ubc84\ud37c \uad00\ub9ac<\/li>\n\n\n\n<li><strong>\uc815\uc218 \uc5f0\uc0b0 \uae30\ubc18<\/strong> &#8211; FPU \uc5c6\ub294 \ud658\uacbd\uc5d0 \ucd5c\uc801\ud654<\/li>\n\n\n\n<li><strong>1KLOC \ubbf8\ub9cc<\/strong> &#8211; \ucf54\ub4dc \ud06c\uae30 \uc57d 34KB<\/li>\n<\/ul>\n\n\n\n<p>\uc81c\uacf5\ud558\ub294 \uc8fc\uc694 \uc54c\uace0\ub9ac\uc998:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Blur (\ubc15\uc2a4 \ud544\ud130)<\/li>\n\n\n\n<li>Threshold (\uace0\uc815 \uc784\uacc4\uac12, Otsu \uc790\ub3d9 \uc784\uacc4\uac12, \uc801\uc751\ud615 \uc784\uacc4\uac12)<\/li>\n\n\n\n<li>Morphology (\uce68\uc2dd, \ud33d\ucc3d)<\/li>\n\n\n\n<li>Edge Detection (Sobel)<\/li>\n\n\n\n<li>Connected Components<\/li>\n\n\n\n<li>FAST\/ORB \ud2b9\uc9d5\uc810 \uac80\ucd9c<\/li>\n\n\n\n<li>LBP \uce90\uc2a4\ucf00\uc774\ub4dc (\uc5bc\uad74\/\uac1d\uccb4 \uac80\ucd9c)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">\ud558\ub4dc\uc6e8\uc5b4 \uad6c\uc131<\/h2>\n\n\n\n<p>\ud14c\uc2a4\ud2b8\uc5d0 \uc0ac\uc6a9\ud55c \ubcf4\ub4dc\ub294 <a href=\"https:\/\/www.dfrobot.com\/product-2676.html\">DFRobot FireBeetle 2 ESP32-S3<\/a> \uc640 OV2640 \uce74\uba54\ub77c\uc774\ub2e4.<\/p>\n\n\n\n<p><strong>\uc8fc\uc694 \uc2a4\ud399:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\uce69\uc14b<\/strong>: ESP32-S3 (Xtensa LX7 dual-core @ 240MHz)<\/li>\n\n\n\n<li><strong>\uba54\ubaa8\ub9ac<\/strong>: 16MB Flash, 8MB PSRAM<\/li>\n\n\n\n<li><strong>\uce74\uba54\ub77c<\/strong>: OV2640 (2MP, 1600\u00d71200 \ucd5c\ub300 \ud574\uc0c1\ub3c4)<\/li>\n\n\n\n<li><strong>\uc804\uc6d0 \uad00\ub9ac<\/strong>: AXP313A (Li-ion \ubc30\ud130\ub9ac \ucda9\uc804, \uce74\uba54\ub77c \ub3c5\ub9bd \uc804\uc6d0)<\/li>\n\n\n\n<li><strong>\ud1b5\uc2e0<\/strong>: WiFi 4 (802.11b\/g\/n), Bluetooth 5 LE<\/li>\n<\/ul>\n\n\n\n<p>PSRAM 8MB\uac00 \ud575\uc2ec\uc774\ub2e4. \uce74\uba54\ub77c \ud504\ub808\uc784 \ubc84\ud37c\uc640 \uc774\ubbf8\uc9c0 \ucc98\ub9ac \uc911\uac04 \ubc84\ud37c\ub97c PSRAM\uc5d0 \ud560\ub2f9\ud558\uba74 \ub0b4\ubd80 SRAM\uc744 \uc544\ub084 \uc218 \uc788\ub2e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\uad6c\ud604 \ubc29\ubc95<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. \uce74\uba54\ub77c \ucd08\uae30\ud654<\/h3>\n\n\n\n<p>ESP32-S3\uc758 esp_camera \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \uc0ac\uc6a9\ud574 OV2640\ub97c \ucd08\uae30\ud654\ud55c\ub2e4. \ud3ec\ub9f7\uc740 JPEG\ub97c \uae30\ubcf8\uc73c\ub85c \ud558\ub418, Grayskull \ucc98\ub9ac \uc694\uccad \uc2dc \ub7f0\ud0c0\uc784 \ubcc0\ud658\uc744 \uc218\ud589\ud55c\ub2e4.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">camera_config_t config;\nconfig.pixel_format = PIXFORMAT_JPEG;  \/\/ \uae30\ubcf8 JPEG\nconfig.frame_size = FRAMESIZE_HD;      \/\/ 1280\u00d7720\nconfig.fb_location = CAMERA_FB_IN_PSRAM;  \/\/ PSRAM \uc0ac\uc6a9\nconfig.fb_count = 2;\nesp_camera_init(&amp;config);<\/pre>\n\n\n\n<p>\uae30\ubcf8\uc801\uc73c\ub85c HD(1280 x 720)\uc5d0\uc11c \uc798\ub41c\ub2e4. \uadf8\ub807\uc9c0\ub9cc \uba54\ubaa8\ub9ac \ubd80\uc871 \uc2dc \ud574\uc0c1\ub3c4\ub97c VGA(640\u00d7480) \ub610\ub294 QVGA(320\u00d7240)\ub85c \ub0ae\ucd9c\uc218 \uc788\ub2e4.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. JPEG \u2192 \uadf8\ub808\uc774\uc2a4\ucf00\uc77c \ubcc0\ud658<\/h3>\n\n\n\n<p>Grayskull\uc740 \uadf8\ub808\uc774\uc2a4\ucf00\uc77c \uc774\ubbf8\uc9c0(<code>uint8_t<\/code> 1\ucc44\ub110)\ub9cc \ucc98\ub9ac\ud55c\ub2e4. JPEG \ud504\ub808\uc784\uc744 \ucc98\ub9ac\ud558\ub824\uba74 \ub2e4\uc74c \ub2e8\uacc4\uac00 \ud544\uc694\ud558\ub2e4.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>JPEG \ub514\ucf54\ub529<\/strong>: <code>fmt2rgb888()<\/code>\ub85c RGB888 \ubc84\ud37c \uc0dd\uc131<\/li>\n\n\n\n<li><strong>\uadf8\ub808\uc774\uc2a4\ucf00\uc77c \ubcc0\ud658<\/strong>: RGB\ub97c \uac00\uc911 \ud3c9\uade0\uc73c\ub85c \ubcc0\ud658 (Y = 0.299R + 0.587G + 0.114B)<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">bool convertFrameToGrayscale(const camera_fb_t* fb, gs_image&amp; gray) {\n  size_t rgbSize = fb->width * fb->height * 3;\n  uint8_t* rgbBuffer = (uint8_t*)ps_malloc(rgbSize);\n\n  if (!fmt2rgb888(fb->buf, fb->len, fb->format, rgbBuffer)) {\n    free(rgbBuffer);\n    return false;\n  }\n\n  gray = gs_alloc(fb->width, fb->height);\n  for (unsigned i = 0; i &lt; fb->width * fb->height; i++) {\n    uint8_t r = rgbBuffer[i * 3];\n    uint8_t g = rgbBuffer[i * 3 + 1];\n    uint8_t b = rgbBuffer[i * 3 + 2];\n    gray.data[i] = (299 * r + 587 * g + 114 * b) \/ 1000;\n  }\n\n  free(rgbBuffer);\n  return true;\n}<\/pre>\n\n\n\n<p>VGA \uae30\uc900 RGB888 \ubc84\ud37c\ub294 \uc57d 900KB\uc774\ubbc0\ub85c PSRAM(<code>ps_malloc<\/code>) \uc0ac\uc6a9\uc774 \ud544\uc218\ub2e4.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3. Grayskull \ucc98\ub9ac<\/h3>\n\n\n\n<p>\ubcc0\ud658\ub41c \uadf8\ub808\uc774\uc2a4\ucf00\uc77c \uc774\ubbf8\uc9c0\uc5d0 \uc6d0\ud558\ub294 \uc54c\uace0\ub9ac\uc998\uc744 \uc801\uc6a9\ud55c\ub2e4.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \ube14\ub7ec\nstruct gs_image blurred = gs_alloc(img.w, img.h);\ngs_blur(blurred, img, radius);\n\n\/\/ \uc784\uacc4\uac12\nstruct gs_image binary = gs_alloc(img.w, img.h);\ngs_copy(binary, img);\ngs_threshold(binary, threshold);\n\n\/\/ Otsu \uc790\ub3d9 \uc784\uacc4\uac12\nuint8_t otsu = gs_otsu_threshold(img);\ngs_threshold(binary, otsu);\n\n\/\/ \uc5e3\uc9c0 \uac80\ucd9c\nstruct gs_image edges = gs_alloc(img.w, img.h);\ngs_sobel(edges, img);<\/pre>\n\n\n\n<p>\uac01 \ud568\uc218\ub294 \uc778\ud50c\ub808\uc774\uc2a4 \ub610\ub294 \ubaa9\uc801\uc9c0 \ubc84\ud37c\ub97c \uc694\uad6c\ud55c\ub2e4. \uba54\ubaa8\ub9ac \ud574\uc81c\ub294 <code>gs_free()<\/code>\ub85c \uc218\ud589\ud55c\ub2e4.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4. \uacb0\uacfc JPEG \uc7ac\uc778\ucf54\ub529<\/h3>\n\n\n\n<p>\ucc98\ub9ac \uacb0\uacfc\ub97c \ub124\ud2b8\uc6cc\ud06c\ub85c \uc804\uc1a1\ud558\ub824\uba74 \ub2e4\uc2dc JPEG\ub85c \uc555\ucd95\ud55c\ub2e4. <code>fmt2jpg()<\/code>\ub294 \uadf8\ub808\uc774\uc2a4\ucf00\uc77c\uc744 JPEG\ub85c \ubcc0\ud658\ud55c\ub2e4.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">uint8_t* jpgBuffer = nullptr;\nsize_t jpgLength = 0;\n\nbool success = fmt2jpg(processedImage.data,\n                       processedImage.w * processedImage.h,\n                       processedImage.w,\n                       processedImage.h,\n                       PIXFORMAT_GRAYSCALE,\n                       quality,\n                       &amp;jpgBuffer,\n                       &amp;jpgLength);\n\nif (success) {\n  http.POST(jpgBuffer, jpgLength);\n  free(jpgBuffer);\n}<\/pre>\n\n\n\n<p>JPEG \ud488\uc9c8(0~63, \ub0ae\uc744\uc218\ub85d \uace0\ud488\uc9c8)\uc744 \uc870\uc808\ud574 \uc804\uc1a1 \uc18d\ub3c4\uc640 \uc774\ubbf8\uc9c0 \ud488\uc9c8 \uac04 \uade0\ud615\uc744 \ub9de\ucd98\ub2e4.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">5. \uc774\ubbf8\uc9c0 \uc800\uc7a5<\/h3>\n\n\n\n<p>\ucd2c\uc601\ud6c4 \ucc98\ub9ac\ud55c \uc774\ubbf8\uc9c0\ub294 http\ub97c \ud1b5\ud574 \uc11c\ubc84\ub85c \uc804\uc1a1\ud588\uace0, \uc11c\ubc84\ub294 FastAPI\ub85c \uad6c\ud604\ud588\uc73c\uba70, \ud0c0\uc784\uc2a4\ud0ec\ud504 \uae30\ubc18 \ud30c\uc77c\uba85\uc73c\ub85c \uc774\ubbf8\uc9c0\ub97c \uc800\uc7a5\ud55c\ub2e4.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@app.post(\"\/upload\")\nasync def upload_image(request: Request):\n    image_data = await request.body()\n    timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S_%f\")\n    filename = f\"capture_{timestamp}.jpg\"\n    with open(f\"data\/{filename}\", \"wb\") as f:\n        f.write(image_data)\n    return {\"status\": \"success\", \"filename\": filename}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\ucc98\ub9ac \uacb0\uacfc<\/h2>\n\n\n\n<p>\uc6d0\ubcf8 \uc774\ubbf8\uc9c0\uc640 \uac01 \uc54c\uace0\ub9ac\uc998 \uc801\uc6a9 \uacb0\uacfc\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n\n\n\n<p><strong>\uc6d0\ubcf8<\/strong><br><img decoding=\"async\" src=\"https:\/\/velog.velcdn.com\/images\/rtlink\/post\/d7dbeaee-6544-4d6f-8aa2-b8d1b6cf063c\/image.jpg\" alt=\"\"><\/p>\n\n\n\n<p><strong>Blur<\/strong><br><img decoding=\"async\" src=\"https:\/\/velog.velcdn.com\/images\/rtlink\/post\/bfec1cbe-8319-426c-b9ac-ba5f83caa5a6\/image.jpg\" alt=\"\"><\/p>\n\n\n\n<p><strong>Threshold<\/strong><br><img decoding=\"async\" src=\"https:\/\/velog.velcdn.com\/images\/rtlink\/post\/8afac598-9027-4410-b1bf-a735e89bf299\/image.jpg\" alt=\"\"><\/p>\n\n\n\n<p><strong>Otsu<\/strong><br><img decoding=\"async\" src=\"https:\/\/velog.velcdn.com\/images\/rtlink\/post\/a2feb80c-1785-45c4-8091-ea6402a1b46b\/image.jpg\" alt=\"\"><\/p>\n\n\n\n<p><strong>Edges<\/strong><br><img decoding=\"async\" src=\"https:\/\/velog.velcdn.com\/images\/rtlink\/post\/cc2ad54b-7b61-4599-a812-b776e765d95a\/image.jpg\" alt=\"\"><\/p>\n\n\n\n<p>\ucc98\ub9ac \uc2dc\uac04\uc740 VGA \uae30\uc900 \ube14\ub7ec \uc57d 200ms, Sobel \uc57d 150ms \uc815\ub3c4\ub2e4. \ud574\uc0c1\ub3c4\uc640 \uc54c\uace0\ub9ac\uc998 \ubcf5\uc7a1\ub3c4\uc5d0 \ube44\ub840\ud55c\ub2e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\uba54\ubaa8\ub9ac \uad00\ub9ac<\/h2>\n\n\n\n<p>ESP32-S3\uc758 \uba54\ubaa8\ub9ac \uad6c\uc131\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">[System] \ucd08\uae30 \uc0ac\uc6a9 \uac00\ub2a5\ud55c \uba54\ubaa8\ub9ac: 304696 bytes\n[System] \ucd5c\ub300 \uc5f0\uc18d \uba54\ubaa8\ub9ac \ube14\ub85d: 249844 bytes\n[System] PSRAM \ud06c\uae30: 8388608 bytes\n[System] \uc0ac\uc6a9 \uac00\ub2a5\ud55c PSRAM: 8386096 bytes<\/pre>\n\n\n\n<p>PSRAM \uc5c6\uc774\ub294 HD \ud574\uc0c1\ub3c4 \ucc98\ub9ac\uac00 \ubd88\uac00\ub2a5\ud558\ub2e4. PSRAM \ud560\ub2f9\uc740 <code>ps_malloc()<\/code>\uc744 \uc0ac\uc6a9\ud558\uba70, \uc2e4\ud328 \uc2dc \uc989\uc2dc \ubcf5\uad6c \ub85c\uc9c1\uc744 \uc2e4\ud589\ud574\uc57c \ud55c\ub2e4.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">uint8_t* buffer = (uint8_t*)ps_malloc(size);\nif (!buffer) {\n  Serial.println(\"PSRAM allocation failed\");\n  return false;\n}<\/pre>\n\n\n\n<p>\ucc98\ub9ac \uc644\ub8cc \ud6c4\uc5d0\ub294 \ubaa8\ub4e0 \uc784\uc2dc \ubc84\ud37c\ub97c \uba85\uc2dc\uc801\uc73c\ub85c \ud574\uc81c\ud55c\ub2e4.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">cleanup:\n  if (fb) esp_camera_fb_return(fb);\n  if (processedImage.data) gs_free(processedImage);\n  if (jpgBuffer) free(jpgBuffer);<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\uc81c\uc57d\uc0ac\ud56d<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>JPEG \ub514\ucf54\ub529 \uc624\ubc84\ud5e4\ub4dc<\/strong>: VGA \uae30\uc900 RGB \ubcc0\ud658\uc5d0 \uc57d 100~150ms \uc18c\uc694<\/li>\n\n\n\n<li><strong>\uba54\ubaa8\ub9ac \uc81c\uc57d<\/strong>: FHD(1920\u00d71080) \uc774\uc0c1\uc740 PSRAM 8MB\ub85c\ub3c4 \ubc84\uac70\uc6c0<\/li>\n\n\n\n<li><strong>\ucc98\ub9ac \uc18d\ub3c4<\/strong>: \uc2e4\uc2dc\uac04 \ube44\ub514\uc624 \ucc98\ub9ac(30fps)\ub294 \ubd88\uac00\ub2a5, \uc815\uc801 \uc774\ubbf8\uc9c0 \ubd84\uc11d\uc5d0 \uc801\ud569<\/li>\n\n\n\n<li><strong>\uc54c\uace0\ub9ac\uc998 \uc81c\ud55c<\/strong>: CNN \uac19\uc740 \ub525\ub7ec\ub2dd \ubaa8\ub378\uc740 \ubd88\uac00\ub2a5 (ESP32-S3\uc758 AI \uac00\uc18d\uae30\ub294 \ubcc4\ub3c4 \ud559\uc2b5 \ud544\uc694)<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">\uacb0\ub860<\/h2>\n\n\n\n<p>Grayskull\uc740 ESP32\uc5d0\uc11c OpenCV\uc758 \uae30\ubcf8 \uae30\ub2a5\uc744 \uad6c\ud604\ud560 \uc218 \uc788\uac8c \ud55c\ub2e4. \uc81c\ud55c\ub41c \uba54\ubaa8\ub9ac\uc640 \ucc98\ub9ac \ub2a5\ub825 \ub0b4\uc5d0\uc11c \ube14\ub7ec, \uc784\uacc4\uac12, \uc5e3\uc9c0 \uac80\ucd9c \uac19\uc740 \uc804\ud1b5\uc801\uc778 \ucef4\ud4e8\ud130 \ube44\uc804 \uc54c\uace0\ub9ac\uc998\uc744 \uc2e4\ud589 \uac00\ub2a5\ud558\uba70, \ud074\ub77c\uc6b0\ub4dc \uc758\uc874 \uc5c6\uc774 \uc5e3\uc9c0 \ub514\ubc14\uc774\uc2a4\uc5d0\uc11c \uc774\ubbf8\uc9c0 \ubd84\uc11d\uc744 \uc218\ud589\ud560 \uc218 \uc788\uc744\uac83 \uac19\ub2e4.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><strong>\ucc38\uace0 \uc790\ub8cc<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/zserge\/grayskull\">Grayskull GitHub<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.dfrobot.com\/product-2676.html\">FireBeetle 2 ESP32-S3 \uc81c\ud488 \ud398\uc774\uc9c0<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/espressif\/esp32-camera\">ESP32 Camera Driver<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>ESP32 \ub9c8\uc774\ud06c\ub85c\ucee8\ud2b8\ub864\ub7ec\ub294 WiFi\/Bluetooth\ub97c \ub0b4\uc7a5\ud55c \uc800\uc804\ub825\u00b7\uc800\uac00\uaca9 \uce69\uc73c\ub85c, IoT \uce74\uba54\ub77c \ud504\ub85c\uc81d\ud2b8\uc5d0 \ub110\ub9ac \uc0ac\uc6a9\ub41c\ub2e4. \ud558\uc9c0\ub9cc \uc81c\ud55c\ub41c \uba54\ubaa8\ub9ac(SRAM 512KB)\uc640 \uc5f0\uc0b0 \ub2a5\ub825 \ub54c\ubb38\uc5d0 OpenCV \uac19\uc740 \ubcf8\uaca9\uc801\uc778 \ucef4\ud4e8\ud130 \ube44\uc804 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \ud3ec\ud305\ud558\uae30 \uc5b4\ub835\ub2e4. \ub300\ubd80\ubd84\uc758 ESP32 \uce74\uba54\ub77c \ud504\ub85c\uc81d\ud2b8\ub294 JPEG \uc2a4\ud2b8\ub9ac\ubc0d\uc774\ub098 \ub2e8\uc21c \ucea1\ucc98\uc5d0 \uadf8\uce58\uba70, \uc5e3\uc9c0\uc5d0\uc11c \uc2e4\uc2dc\uac04 \uc774\ubbf8\uc9c0 \ucc98\ub9ac\ub97c \uc218\ud589\ud558\ub824\uba74 \uc678\ubd80 \uc11c\ubc84\ub098 PC\ub85c \uc804\uc1a1 \ud6c4 \ucc98\ub9ac\ud558\ub294 \ubc29\uc2dd\uc744 \ud0dd\ud574\uc57c \ud588\ub2e4. \uadf8\ub7f0\ub370 \ucd5c\uadfc grayskull \uc624\ud508 \uc18c\uc2a4\ub97c \ubcf4\uace0<\/p>\n<div class=\"more-link\">\n\t\t\t\t <a href=\"http:\/\/practical.kr\/?p=878\" class=\"link-btn theme-btn\"><span>Read More <\/span> <i class=\"fa fa-caret-right\"><\/i><\/a>\n\t\t\t<\/div>\n","protected":false},"author":1,"featured_media":879,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[31],"tags":[119,120,122],"class_list":["post-878","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-esp32","tag-grayskull","tag-opencv","tag-ov2640"],"jetpack_featured_media_url":"http:\/\/practical.kr\/wp-content\/uploads\/2025\/11\/1_color.jpg","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"http:\/\/practical.kr\/index.php?rest_route=\/wp\/v2\/posts\/878","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/practical.kr\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/practical.kr\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/practical.kr\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/practical.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=878"}],"version-history":[{"count":1,"href":"http:\/\/practical.kr\/index.php?rest_route=\/wp\/v2\/posts\/878\/revisions"}],"predecessor-version":[{"id":880,"href":"http:\/\/practical.kr\/index.php?rest_route=\/wp\/v2\/posts\/878\/revisions\/880"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/practical.kr\/index.php?rest_route=\/wp\/v2\/media\/879"}],"wp:attachment":[{"href":"http:\/\/practical.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=878"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/practical.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=878"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/practical.kr\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=878"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}