v5.0.1#

v5.0.1은 v5.0.0 위에 올라간 유지 보수 릴리스입니다 — 레거시 sensor 모듈이 csi 위의 Python 호환 계층이 되고, 프레임 버퍼에 불러온 이미지가 이제 카메라 프레임처럼 미리보기에 표시되며, JPEG 인코더의 1 MB 출력 제한이 사라지고 오버플로가 올바르게 처리되며, 카메라, 프로토콜, 네트워킹, ML 라이브러리 수정이 다수 포함되었습니다. csi를 기준으로 작성된 스크립트에 대해서는 호환성을 깨는 API 변경이 없습니다. 몇 가지 동작 변경은 호환성을 깨는 API 변경 아래에 정리되어 있습니다.

주요 내용#

  • 이제 sensor 모듈은 csi 위에 구축된 프로즌 Python 모듈입니다. 임포트 시 사용 중단 경고를 출력하며, csi 모듈이 지원할 수 없는 몇몇 함수는 이제 OSError를 발생시킵니다. sensor 모듈 변경 사항을 참고하세요.

  • 불러온 이미지가 카메라 프레임처럼 미리보기에 표시됩니다. copy_to_fb=True로 프레임 버퍼에 놓인 이미지(image.Image, image.ImageIO.read(), fir.snapshot(), tof.snapshot())는 즉시가 아니라 다음 이미지가 프레임 버퍼를 덮어쓸 때 — 그 위에 그린 내용과 함께 — IDE로 플러시됩니다. 미리보기 변경 사항을 참고하세요.

  • JPEG 인코더 정비. 1 MB 출력 버퍼 상한이 사라졌고(버퍼 크기는 여유 메모리에 따라 결정됨), 소프트웨어 및 하드웨어 경로 모두에서 오버플로가 잘린 파일을 쓰는 대신 제대로 처리되며, 압축된 이미지를 다른 압축 확장자로 저장하면 쓰레기 데이터를 쓰는 대신 예외가 발생합니다.

  • 새로운 protocol.poll() 및 protocol.CBORChannel을 위한 여섯 가지 새 CBOR 위젯(text, lineedit, pushbutton, spinbox, radio, waveform).

  • YOLO Pro 후처리기, FLIR Lepton 3.1R / UW 지원, IMU 버스트 읽기, OpenMV N6의 CAN, 그리고 네트워킹이 가능한 시뮬레이터 타깃.

새로운 기능#

  • YoloPro — Edge Impulse YOLO Pro 객체 검출 모델을 위한 새로운 후처리기와 yolo_pro_detector.py 예제. (f3f150ebd, ea7bbb398)

  • protocol.poll() — USB 인터럽트나 poll_ms 타이머로 구동되지 않는 사용자 정의 전송 방식을 위해, 필요할 때 프로토콜 태스크를 한 번 실행합니다. protocol.init()을 다시 호출해 스택을 재초기화할 수도 있습니다. (c00c215f9, f849ffb1b)

  • 더 많은 CBOR 채널 위젯 — protocol.CBORChannel.add()에 text(정적 문자열), lineedit(쓰기 가능한 문자열), pushbutton(순간 동작), spinbox(단계별 숫자 입력), radio(옵션 그룹), waveform(sample_rate, series, typecode 인자를 갖는 1D 시계열. 인터리브된 샘플 바이트를 할당하면 스트리밍됨)이 추가되었습니다. (8016d29ba)

  • csi.CUSTOM — 사용자 정의 (w, h) 해상도가 설정된 후 csi.CSI.framesize()가 이름 없는 정수 대신 사용 가능한 상수를 반환합니다. (af8841654)

  • IMU 버스트 읽기 — imu.__read_reg()가 선택적 쓰기 가능 버퍼를 받아 연속된 레지스터에서 한 번의 트랜잭션으로 채웁니다. (244ca480a)

  • FLIR Lepton 3.1R 및 Lepton UW를 Lepton 드라이버가 인식하며, 이들의 방사 측정 게인 모드도 포함됩니다. (0fc356632)

  • 시뮬레이터 네트워킹 — MPS2_AN500 QEMU 타깃에 이제 lwIP와 사용자 모드 NIC가 있으며, requests, mqtt, sensor가 프로즌으로 포함되고 네트워킹 단위 테스트가 추가되었습니다. (23546dfb1, 61d187305, 15096c3a2)

  • OpenAI 호환 API 예제 — openai_compatible.py는 JPEG을 스냅샷하여 장면 설명을 위해 chat-completions 엔드포인트로 전송합니다. (79033c6aa)

  • SX126x LoRa 예제 — micropython-lib의 lora-sx126x 드라이버(lora-sync / lora-async 포함)가 OpenMV RT1062 펌웨어에 프로즌으로 포함되었으며, SX1261 또는 SX1262 모듈을 SPI 버스에 연결하기 위한 송신기/수신기/핑퐁/이미지 스트림 예제가 함께 제공됩니다. 이는 커뮤니티가 기여한 지원이며, OpenMV는 LoRa 하드웨어를 판매하지 않습니다. (19576e1b1)

기타 변경 및 개선 사항#

  • requests 라이브러리 재작성 — 청크 전송 응답이 디코딩되고, timeout 인자(기본값 5초)가 노출되며, JSON 및 문자열 본문이 올바르게 직렬화/인코딩되고, 헤더는 대소문자를 구분하지 않고 상태 줄은 위치 기준으로 파싱되며, 호출자의 headers 딕셔너리가 복사되고(따라서 auth가 더 이상 이후 요청으로 새어 나가지 않음), 본문은 블록 단위로 bytearray에 읽혀 들어옵니다. (889f0b096)

  • mqtt가 ssl을 지연 임포트하므로, SSL을 지원하지 않는 빌드에서도 모듈과 일반(비 TLS) 연결이 동작합니다. (c2e0899e8)

  • JPEG 출력 버퍼 크기를 여유 메모리에 따라 결정 — 고정 1 MB 상한 대신 여유 메모리에 맞춰 크기가 정해지며, 인코더가 이미지를 기록하기 전에 버퍼를 인코딩된 크기로 잘라 내므로 대형 센서의 고품질 캡처가 더 이상 실패하지 않습니다. (aa1ca6776)

  • ML 후처리 정리 — Darknet, Edge Impulse, Ultralytics 후처리기 전반에서 바운딩 박스 생성이 표준화되었고, YOLOv8 출력은 임계값 적용 후 전치되어 다른 모델과 마찬가지로 검출당 한 행으로 저장됩니다. (b5c7853f2, c45f99cad)

  • PAG7936 안전 종료 — 새로운 센서 정지(halt) 플래그가 RESET/PWDN 토글 전에 PAG7936을 절전 상태로 전환하여, 센서를 연장 케이블로 사용할 때 발생하던 멈춤 현상을 수정합니다. 또한 드라이버는 MIPI CSI-2 또는 병렬 레지스터 세트를 빌드 시점이 아니라 런타임에 센서의 I2C 주소로부터 선택합니다. (10026530c, f6acf14ec, 5f0251c74)

  • ImageIO 재생 페이싱이 이제 프레임 간 간격을 기다리는 동안 WFI로 잠드는 대신 대기 중인 이벤트를 처리합니다. (a54790193)

  • MicroPython 서브모듈 업데이트 — QEMU 포트의 완전한 네트워킹 지원, 백업 SRAM/BKP 레지스터를 통한 STM32의 machine.mem_backup, STM32F4에서 SDIO 하드웨어 흐름 제어 비활성 유지, 비캐시 영역에 대한 MPU 속성 재사용. (31ada5626, ab61d27a6, 06bddf13a)

  • differential-translation.py가 사용 중단된 sensor 모듈 대신 자신의 csi.CSI 객체를 통해 캡처하며, 예제의 import 간격이 정규화되었습니다. (68c866e7f, 9ceff4513)

버그 수정#

카메라 및 센서:

  • 사용자 정의 (w, h) 해상도를 연달아 두 번 설정하면 센서는 첫 번째 크기에 머무는데 csi.CSI.width() / height()는 두 번째 크기를 보고했습니다. 이제 “변경 없음” 빠른 경로에서 사용자 정의 크기는 제외됩니다. (cfbb70156)

  • csi.CSI.lens_correction()이 radi와 coef를 불리언으로 파싱하여, 드라이버에 전달되는 값이 지정한 값과 달랐습니다. (ab44b3a77)

  • PAG7936: 노출과 게인을 연달아 설정하면 서로를 덮어썼습니다(둘은 하나의 수동 모드를 공유하고 읽기 레지스터가 한 프레임 지연됨). 이제 드라이버는 수동 모드에 진입할 때만 수동 레지스터를 초기화하고, 설정 중인 컨트롤만 기록합니다. (e7a18c0a0)

  • OpenMV N6에서 PAG7936 및 PS5520 BAYER 캡처가 전체 해상도가 아닌 크기에서는 “Failed to initialize the CSI interface” 오류로 실패했습니다. N6 ISP가 원시 Bayer를 리스케일할 수 없기 때문입니다. 이제 BAYER의 경우 센서가 요청된 크기에 맞는 네이티브 모드로 동작합니다(PS5520: VGA, HD, SXGA-M, FHD, QHD, WQXGA2. QVGA는 네이티브 모드가 없어 지원되지 않음). (fe6790055, b7622b245)

이미지 인코딩 및 저장:

  • 소프트웨어 JPEG 인코더: realloc 경로가 비활성화되어 있어 출력 버퍼 오버플로가 항상 치명적이었습니다. 이제 버퍼를 소유한 경우 버퍼를 (한 번에 절반 크기씩) 늘리고, 메모리가 부족하면 깔끔하게 되돌리며, 결과를 잘라 냅니다. jpeg_write / mjpeg_write는 인코딩 실패 후 더 이상 버퍼를 기록하지 않습니다. (28e1110be)

  • 하드웨어 JPEG 인코더(STM32): 오버플로 시 할당 크기와 함께 부분적으로 인코딩된 이미지를 반환하여, EOI 마커가 없는 잘린 파일이 만들어졌습니다(예: 품질 100의 5 MP 캡처에서 아래쪽 절반이 비어 있음). 이제 오버플로를 보고하고, 실패 시 버퍼를 해제하며, 성공 시 버퍼를 잘라 냅니다. (22ce9619c)

  • VC8000 인코더가 모든 요청에 대해 조용히 4:2:0 크로마를 생성했습니다. 이제 4:2:0이 아닌 서브샘플링은 이를 지원하는 HW 코덱으로 대체됩니다. (71f2be7f2)

  • PNG 이미지를 .jpg로 image.Image.save()하면 최대 1 MB의 초기화되지 않은 힙이 기록되었고, JPEG 이미지를 .png로 저장하면 빈 파일이 기록되었습니다. 이제 둘 다 예외를 발생시키며, 이미 대상 형식인 이미지는 여전히 그대로 복사됩니다. (6c1fdf822)

  • png_write가 이제 압축기의 반환값을 확인하고 파일을 만들기 전에 압축하므로, 실패해도 더 이상 빈 파일이 남지 않습니다. (557f52d66)

Image 모듈:

  • BAYER 및 YUV 이미지에서 rgbtuple=True로 image.Image.get_pixel()을 호출하면 변환된 픽셀이 호출자가 제어하는 오프셋으로 스택 지역 변수 너머에 기록되었습니다. (13f2a17b6)

  • gif.Gif.add_frame()에서 도달 불가능한 Bayer/YUV 변환 분기를 제거했습니다. Python API가 이미 강제하던 대로 GRAYSCALE 및 RGB565 이미지만 허용됩니다. (f20d99d96)

프로토콜:

  • 모든 슬롯이 이미 사용 중인 상태에서 동적 채널을 등록하면 범위를 벗어난 쓰기가 발생했습니다. 이제 채널 ID를 먼저 검사하며, 예약된 채널 ID가 채널 테이블에 들어맞는지 정적으로 단언합니다. (68f635d1f)

  • 동적 채널 객체가 루트 포인터에 남아 소프트 리셋 후에도 살아남았고, 그 결과 GC가 이전 실행의 오래된 힙 블록을 계속 스캔했습니다. 이제 초기화 시 테이블이 비워집니다. (ef0ebb03b, e23641145)

포트:

  • i.MX RT: 소프트 리셋 시 누락되어 있던 CAN 및 Encoder 해제(de-initialization)를 추가했습니다. (7cb9d3dc2)

하드웨어 및 보드 지원#

  • OpenMV N6 — 이제 CAN이 활성화되었습니다(machine.CAN). (294f0febd, 91e1c9573)

  • OpenMV AE3 — AE3 배터리 실드의 microSD 소켓을 위해 sdcard SPI 드라이버가 프로즌으로 포함되었습니다. (a258f4814)

  • OpenMV RT1062 — lora-sx126x / lora-sync / lora-async가 프로즌으로 포함되었습니다(새로운 기능 참고).

  • FLIR Lepton 3.1R / UW — Lepton을 지원하는 모든 보드에서 Lepton 드라이버가 인식합니다.

  • PAG7936 — 안전 종료 정지, 런타임 인터페이스 선택, 노출/게인 수정, N6에서의 BAYER 캡처.

  • PS5520 — N6에서 네이티브 크기의 BAYER 캡처.

  • 시뮬레이터(MPS2_AN500) — 네트워킹.

  • 모든 보드 — sensor 호환 모듈이 모든 보드의 매니페스트에 프로즌으로 포함되었습니다.

호환성을 깨는 API 변경#

v5.0.1은 csi 또는 image API에 호환성을 깨는 변경을 도입하지 않습니다. v5.0.0에서 실행되던 스크립트는 다음 동작 변경만 제외하면 수정 없이 실행됩니다.

sensor 모듈 (behavior)#

네이티브 sensor 모듈(modules/py_csi.c)이 제거되고, csi 위에 동일한 API를 구현하는 프로즌 Python 모듈로 대체되었습니다. sensor를 참고하세요.

커밋: 5e3d40631

  • import sensor는 터미널에 WARNING: The sensor module is deprecated and will be removed in a future release. Please use the csi module instead.를 출력합니다.

  • 숨겨진 csi.CSI 객체가 임포트 시 생성되므로, 카메라가 없으면 이제 sensor.reset()이 아니라 import sensor에서 오류가 발생합니다.

  • 다음 함수들은 csi에 대응하는 기능이 없으므로 이제 OSError를 발생시킵니다: get_fb(), alloc_extra_fb(), dealloc_extra_fb(), set_auto_rotation(), get_auto_rotation(), disable_delays(), disable_full_flush(). (두 *_extra_fb 함수는 v5.0.0에서 이미 예외를 발생시켰습니다.) 마지막 두 함수 대신 csi.CSI(delays=False) / csi.CSI(fflush=False)를 사용하세요.

  • csi가 내보내지 않는 프레임 크기(QQCIF, QQSIF, QQQQVGA, HQQQQVGA, HQQQVGA, HQQVGA, B64X32, B64X64, B128X64, B128X128, B160X160, B320X320, LCD, QQVGA2)는 이제 사용자 정의 해상도로 전달되는 (w, h) 튜플입니다. get_framesize()는 카메라가 아직 그 크기에 있는 동안 설정된 튜플을 반환하므로, 상수와의 동등 비교가 계속 동작합니다.

  • ioctl()은 요청 뒤의 인자를 최대 네 개까지만 전달합니다.

  • sleep() / shutdown()은 이전과 마찬가지로 실패 시 OSError를 발생시킵니다(csi의 RuntimeError가 변환됨).

csi로 마이그레이션하세요 — v5.0.0 마이그레이션 표를 참고하세요.

프레임 버퍼 이미지 로딩 (behavior)#

프레임 버퍼에 이미지를 불러오는 함수들 — copy_to_fb=True를 지정한 image.Image, copy_to_fb=True를 지정한 image.Image.copy() / image.Image.scale() 및 to_* 변환, image.ImageIO.read(), copy_to_fb=True를 지정한 fir.snapshot() 및 tof.snapshot() — 는 예전에는 새 이미지를 IDE 미리보기로 즉시 전송했기 때문에 반환된 이미지에 그린 내용을 전혀 볼 수 없었습니다. 이제 이들은 프레임 버퍼를 대기 중으로 표시하고, 다음 이미지가 덮어쓰기 직전에 그 위에 그린 모든 내용과 함께 플러시합니다 — csi.CSI.snapshot()과 같은 동작입니다.

커밋: 87b6db5ee, 7b1a35c2f, f773c740d

이미지 스트림을 읽는 루프는 수정할 필요가 없으며 이제 그린 내용이 표시됩니다. 단일 이미지를 불러오고 멈추는 스크립트는 명시적으로 플러시해야 합니다:

img = image.Image("example.bmp", copy_to_fb=True)
img.flush()          # new: send it to the IDE preview now
time.sleep_ms(1000)

ML 라이브러리 (behavior)#

제공되는 후처리기를 서브클래싱하거나 복사한 코드에만 영향을 줍니다. YoloV8은 이제 임계값 적용 후 모델 출력을 전치하므로, 살아남은 검출이 Darknet 및 Edge Impulse 후처리기와 마찬가지로 박스당 한 열(bb[4:, :])이 아니라 박스당 한 행(bb[:, 4:])으로 저장됩니다. 공개 ((x, y, w, h), score) 결과는 변경되지 않았습니다.

커밋: c45f99cad, b5c7853f2

protocol 모듈 (minor)#

protocol.poll()이 돌아왔습니다. v5.0.0에서 프로토콜 태스크가 타이머로 옮겨가면서 제거되었으나, 사용자 정의 전송 방식에서 태스크를 명시적으로 구동하는 수단으로 다시 추가되었습니다. 그 외에는 변경된 것이 없으며, 이 함수를 호출한 적이 없는 스크립트는 영향을 받지 않습니다.

커밋: c00c215f9

빌드 / 툴링 (tooling)#

이 중 어느 것도 MicroPython 스크립트에는 영향을 주지 않습니다. py_image.c가 분할되어, 디스크립터 객체(cascade, keypoints, LBP, keypoint match)는 py_image_descriptor.c로, 통계 객체(similarity, statistics, percentile, threshold, histogram)는 py_image_stats.c로 옮겨졌습니다. imlib 인코더 API가 imlib의 나머지 부분과 일관되게 정리되었습니다: jpeg_compress / png_compress는 성공 시 0, 오류 시 -1을 반환하며, jpeg_compress의 realloc 인자는 사라졌습니다(크기 조정은 버퍼 소유 여부에 따라 결정됨). omv_protocol_channel.h에서 OMV_PROTOCOL_MAX_CHANNELS가 OMV_PROTOCOL_CHANNEL_MAX로 바뀌었고, 포트는 이제 초기화 시 py_protocol_init0()을 호출합니다. OMV_PAG7936_MIPI_CSI2 보드 정의는 사라졌습니다(인터페이스는 런타임에 선택됨). CI 의존성 업데이트와 빈 릴리스 체인지로그 수정으로 마무리됩니다. 포크와 관련된 세부 사항은 펌웨어 저장소 이력을 참고하세요.

마이그레이션 체크리스트#

  • 아직 import sensor를 사용 중인가요? 경고와 함께 동작합니다. csi로 옮기고(v5.0.0 마이그레이션), disable_delays / disable_full_flush를 csi.CSI() 생성자 인자로 교체하고, get_fb / 자동 회전 호출은 제거하세요.

  • copy_to_fb=True로 단일 이미지를 불러오고 그것이 보이길 기대하나요? 불러온 후 img.flush()를 추가하세요.

  • YOLOv8 후처리기를 복사했나요? 임계값 적용된 출력을 열이 아니라 행(bb[:, 4:])으로 인덱싱하세요.

  • 튜플을 설정한 후 csi0.framesize()를 비교하나요? 이제 csi.CUSTOM을 반환합니다.

  • 그 외 모든 것: 다시 플래시하고 실행하세요.