openpibo.vision_camera의 소스 코드

"""
영상처리, 인공지능 비전 기술을 사용합니다.

Class:
:obj:`~openpibo.vision_camera.Camera`
"""
import cv2,requests
import os
import numpy as np
from PIL import Image,ImageDraw,ImageFont
from picamera2 import Picamera2
#from libcamera import Transform
import openpibo_models
import logging

os.environ['LIBCAMERA_LOG_LEVELS'] = '3'

[문서] class Camera: """ Functions: :meth:`~openpibo.vision_camera.Camera.imread` :meth:`~openpibo.vision_camera.Camera.read` :meth:`~openpibo.vision_camera.Camera.create_matte` :meth:`~openpibo.vision_camera.Camera.imshow_to_ide` :meth:`~openpibo.vision_camera.Camera.resize` :meth:`~openpibo.vision_camera.Camera.rotate` :meth:`~openpibo.vision_camera.Camera.imwrite` :meth:`~openpibo.vision_camera.Camera.draw_bitmap` :meth:`~openpibo.vision_camera.Camera.rectangle` :meth:`~openpibo.vision_camera.Camera.circle` :meth:`~openpibo.vision_camera.Camera.line` :meth:`~openpibo.vision_camera.Camera.putTextPIL` :meth:`~openpibo.vision_camera.Camera.putText` :meth:`~openpibo.vision_camera.Camera.stylization` :meth:`~openpibo.vision_camera.Camera.detailEnhance` :meth:`~openpibo.vision_camera.Camera.pencilSketch` :meth:`~openpibo.vision_camera.Camera.edgePreservingFilter` :meth:`~openpibo.vision_camera.Camera.flip` 파이보의 카메라를 제어합니다. * 사진 촬영, 읽기, 쓰기 등 카메라 기본 기능을 사용할 수 있습니다. * 이미지에 도형/글자를 추가할 수 있습니다. * 이미지를 변환할 수 있습니다. example:: from openpibo.vision_camera import Camera camera = Camera() # 아래의 모든 예제 이전에 위 코드를 먼저 사용합니다. """ def __init__(self, cam=0, width=None, height=None): """ Camera 클래스를 초기화합니다. """ self.width, self.height = 640, 480 cv2.setUseOptimized(True) cv2.setNumThreads(cv2.getNumberOfCPUs()) cap = Picamera2() config = cap.create_still_configuration( main={ 'size': (1280, 960), 'format': 'RGB888' }, #transform=Transform(hflip=False, vflip=False), buffer_count=2 ) cap.configure(config) cap.set_controls({'AwbEnable': True, 'AwbMode': 0}) cap.start() self.cap = cap
[문서] def release(self): if self.cap is not None: self.cap.stop() self.cap.close() self.cap = None
[문서] def imread(self, filename): """ 이미지 파일을 읽습니다. :param str filename: 사용할 이미지 파일 :returns: ``numpy.ndarray`` 타입 이미지 객체 """ return cv2.imread(filename)
[문서] def read(self): """ 카메라를 통해 이미지를 촬영합니다. 해상도 변경 시 이미지가 깨질 수 있으므로, 기본 해상도를 권장합니다. :returns: ``numpy.ndarray`` 타입 이미지 객체 """ return cv2.resize(cv2.flip(self.cap.capture_array(), -1), (self.width, self.height))
# return cv2.rotate(cv2.resize(self.cap.capture_array(), (self.height, self.width)),cv2.ROTATE_90_COUNTERCLOCKWISE) # return cv2.rotate(self.cap.capture_array(),cv2.ROTATE_90_COUNTERCLOCKWISE)
[문서] def create_matte(self, colors=(255,255,255)): if type(colors) is str: colors = (int(colors[5:7], 16), int(colors[3:5], 16), int(colors[1:3], 16)) if type(colors) is not tuple: raise Exception(f'"{colors}" must be tuple type') if len(colors) != 3: raise Exception(f'len({colors}) must be 3') return np.full((self.height, self.width, 3), colors, dtype=np.uint8)
[문서] def imshow(self, img, ratio=1.0): """ 이미지 파일을 Web IDE에 출력합니다. :param numpy.ndarray img: 이미지 객체 :param float ratio: 이미지 사이즈 변환 비율 (다수 동시 사용시, 네트워크 부하) """ self.imshow_to_ide(img, ratio)
[문서] def imshow_to_ide(self, img, ratio=0.25): """ 이미지 파일을 Web IDE에 출력합니다. :param numpy.ndarray img: 이미지 객체 :param float ratio: 이미지 사이즈 변환 비율 (다수 동시 사용시, 네트워크 부하) """ if not type(img) is np.ndarray: raise Exception('"img" must be image data from opencv') img = cv2.resize(img, (0, 0), fx=ratio, fy=ratio, interpolation=cv2.INTER_AREA) # curl -X 'POST' -so /dev/null 'http://0.0.0.0/show' -H 'accept: application/json' -H 'Content-Type: multipart/form-data' -F 'data=@{filename};type=image/png' requests.post('http://0.0.0.0/show', headers={'accept': 'application/json'}, files={'data': ('filename', cv2.imencode('.jpg', img)[1].tobytes(), 'image/png')})
[문서] def resize (self, img, w, h): """ Opencv 이미지의 크기를 변환합니다. :param numpy.ndarray img: 이미지 객체 :param int w: 변환될 이미지의 가로 크기입니다. (픽셀 단위) :param int h: 변환될 이미지의 세로 크기입니다. (픽셀 단위) :returns: 크기 변환 후의 ``numpy.ndarray`` 이미지 객체 """ return cv2.resize(img, (w, h))
[문서] def rotate(self, img, degree=10, ratio=0.9): """ 이미지를 회전시킵니다. :param numpy.ndarray img: 이미지 객체 :param int degree: 회전할 각도 :param float ratio: 축소 또는 확대할 비율 :returns: 회전한 ``numpy.ndarray`` 이미지 객체 """ if not type(img) is np.ndarray: raise Exception('"img" must be image data from opencv') if type(degree) is not int or abs(degree) >= 360: raise Exception(f'{degree} must be integer type and -360~360') if type(ratio) is not float or ratio >= 1.0: raise Exception(f'"{ratio} must be float type and 0~1.0') rows, cols = img.shape[0:2] op = cv2.getRotationMatrix2D((cols/2,rows/2), degree, ratio) return cv2.warpAffine(img, op, (cols,rows))
[문서] def imwrite(self, filename, img): """ 이미지를 파일로 저장합니다. :param str filename: 저장할 파일 경로(jpg, png) :param numpy.ndarray img: 저장할 이미지 객체 """ return cv2.imwrite(filename, img)
[문서] def draw_bitmap(self, w, h, val, background=(255,255,255), pixel=(0,0,0)): """ 비트맨 데이터를 이미지로 생성성합니다. :param int w: bitmap 가로 길이 :param int h: bitmap 세로 길이 :param tuple background: 배경 색상 :param tuple pixel: 비트 색상 :returns: ``numpy.ndarray`` 이미지 객체 """ # 1. 문자열을 정수 리스트로 변환 try: values = [int(v.strip()) for v in val.split(',')] except ValueError: raise ValueError("val 인자는 쉼표로 구분된 정수 문자열이어야 합니다.") # 2. w*h 크기에 맞는 값인지 확인 if len(values) != w * h: raise ValueError(f"val의 길이가 w*h와 일치하지 않습니다: {len(values)} != {w * h}") # 3. 1차원 리스트를 2차원 배열로 변환 (행: h, 열: w) bitmap = np.array(values).reshape((h, w)) # 4. 작은 크기의 이미지 생성 (배경색으로 채움) small_img = np.full((h, w, 3), background, dtype=np.uint8) small_img[bitmap == 1] = pixel # bitmap 값이 1인 부분에 pixel 색상 적용 # 5. 각 픽셀을 복제할 배수 계산 (정수 배수) scale_y = self.width // h # 세로 복제 횟수 scale_x = self.height // w # 가로 복제 횟수 # 6. np.repeat를 이용하여 픽셀 복제 (각 픽셀을 scale_y x scale_x 블록으로 확장) replicated_img = np.repeat(np.repeat(small_img, scale_y, axis=0), scale_x, axis=1) # 7. 복제 결과가 정확히 480x640이 아닐 수 있으므로, 부족한 부분은 배경색으로 채우고 # 넘치는 부분은 자른다. rep_h, rep_w = replicated_img.shape[:2] if rep_h < self.width or rep_w < self.height: pad_bottom = self.width - rep_h pad_right = self.height - rep_w final_img = cv2.copyMakeBorder(replicated_img, 0, pad_bottom, 0, pad_right, cv2.BORDER_CONSTANT, value=background) else: final_img = replicated_img[:self.width, :self.height] return final_img
[문서] def rectangle(self, img, p1, p2, colors=(255,255,255), tickness=1): """ 이미지에 직사각형을 그립니다. :param numpy.ndarray img: 이미지 객체 :param tuple(int, int) p1: 좌측상단 좌표 (x, y) :param tuple(int, int) p2: 우측하단 좌표 (x, y) :param tuple(int, int, int) colors: RGB 값 (r, g, b) or 16진수 값 '#ffffff' :param int tickness: 사각형 모서리의 두께 (픽셀 단위) -1 은 채움 """ if not type(img) is np.ndarray: raise Exception('"img" must be image data from opencv') if type(p1) is not tuple: raise Exception(f'"{p1}" must be tuple type') if len(p1) != 2: raise Exception(f'len({p1}) must be 2') if type(p2) is not tuple: raise Exception(f'"{p2}" must be tuple type') if len(p2) != 2: raise Exception(f'len({p2}) must be 2') if type(colors) is str: colors = (int(colors[5:7], 16), int(colors[3:5], 16), int(colors[1:3], 16)) if type(colors) is not tuple: raise Exception(f'"{colors}" must be tuple type') if len(colors) != 3: raise Exception(f'len({colors}) must be 3') return cv2.rectangle(img, p1, p2, colors, tickness)
[문서] def circle(self, img, p, r, colors=(255,255,255), tickness=1): """ 이미지에 원을을 그립니다. :param numpy.ndarray img: 이미지 객체 :param tuple(int, int) p: 좌측상단 좌표 (x, y) :param int r: 반지름 :param tuple(int, int, int) colors: RGB 값 (r, g, b) or 16진수 값 '#ffffff' :param int tickness: 사각형 모서리의 두께 (픽셀 단위) -1은 채움 """ if not type(img) is np.ndarray: raise Exception('"img" must be image data from opencv') if type(p) is not tuple: raise Exception(f'"{p}" must be tuple type') if len(p) != 2: raise Exception(f'len({p}) must be 2') if type(r) is not int: raise Exception(f'len({r}) must be Integer type') if type(colors) is str: colors = (int(colors[5:7], 16), int(colors[3:5], 16), int(colors[1:3], 16)) if type(colors) is not tuple: raise Exception(f'"{colors}" must be tuple type') if len(colors) != 3: raise Exception(f'len({colors}) must be 3') return cv2.circle(img, p, r, colors, tickness)
[문서] def line(self, img, p1, p2, colors=(255,255,255), tickness=1): """ 이미지에 직선을 그립니다. :param numpy.ndarray img: 이미지 객체 :param tuple(int, int) p1: 시작 좌표 (x, y) :param tuple(int, int) p2: 끝 좌표 (x, y) :param tuple(int, int, int) colors: RGB 값 (r, g, b) or 16진수 값 '#ffffff' :param int tickness: 선의 두께 (픽셀 단위) """ if not type(img) is np.ndarray: raise Exception('"img" must be image data from opencv') if type(p1) is not tuple: raise Exception(f'"{p1}" must be tuple type') if len(p1) != 2: raise Exception(f'len({p1}) must be 2') if type(p2) is not tuple: raise Exception(f'"{p2}" must be tuple type') if len(p2) != 2: raise Exception(f'len({p2}) must be 2') if type(colors) is str: colors = (int(colors[5:7], 16), int(colors[3:5], 16), int(colors[1:3], 16)) if type(colors) is not tuple: raise Exception(f'"{colors}" must be tuple type') if len(colors) != 3: raise Exception(f'len({colors}) must be 3') return cv2.line(img, p1, p2, colors, tickness)
[문서] def putTextPIL(self, img, text, points, size=30, colors=(255,255,255)): """ 이미지에 문자를 입력합니다. (한/영 가능 - pillow 이용) :param numpy.ndarray img: 이미지 객체 :param str text: 표시할 문자열 :param tuple(int, int) points: 텍스트 블록 좌측상단 좌표 (x, y) :param int size: 표시할 글자의 크기 :param tuple(int, int, int) colors: 글자 색깔 RGB 값 (b, g, r) or 16진수 값 '#ffffff' """ if not type(img) is np.ndarray: raise Exception('"img" must be image data from opencv') if type(points) is not tuple: raise Exception(f'"{points}" must be tuple type') if len(points) != 2: raise Exception(f'len({points}) must be 2') if type(colors) is str: colors = (int(colors[5:7], 16), int(colors[3:5], 16), int(colors[1:3], 16)) if type(colors) is not tuple: raise Exception(f'"{colors}" must be tuple type') if len(colors) != 3: raise Exception(f'len({colors}) must be 3') font = ImageFont.truetype(openpibo_models.filepath("KDL.ttf"), size) pil = Image.fromarray(img) # CV to PIL ImageDraw.Draw(pil).text(points, text, font=font, fill=colors) # putText img[:] = np.array(pil) # PIL to CV return img
[문서] def putText(self, img, text, points, size=1, colors=(255,255,255), tickness=1): """ 이미지에 문자를 입력합니다. (영어만 가능) :param numpy.ndarray img: 이미지 객체 :param str text: 표시할 문자열 :param tuple(int, int) points: 텍스트 블록 좌측하단 좌표 (x, y) :param int size: 표시할 글자의 크기 :param tuple(int, int, int) colors: 글자 색깔 RGB 값 (r, g, b) or 16진수 값 '#ffffff' :param int tickness: 글자 두께 """ if not type(img) is np.ndarray: raise Exception('"img" must be image data from opencv') if type(points) is not tuple: raise Exception(f'"{points}" must be tuple type') if len(points) != 2: raise Exception(f'len({points}) must be 2') if type(colors) is str: colors = (int(colors[5:7], 16), int(colors[3:5], 16), int(colors[1:3], 16)) if type(colors) is not tuple: raise Exception(f'"{colors}" must be tuple type') if len(colors) != 3: raise Exception(f'len({colors}) must be 3') return cv2.putText(img, text, points, cv2.FONT_HERSHEY_SIMPLEX, size, colors, tickness)
[문서] def stylization(self, img, sigma_s=100, sigma_r=0.5): """ 만화 이미지로 변환합니다. (opencv api) low speed :param numpy.ndarray img: 이미지 객체 :param float sigma_s: 이미지의 blur 보존 정도 (1-200) :param float sigma_r: 이미지의 Edge 적용 정도 (0-1) :returns: ``numpy.ndarray`` 이미지 객체 """ if not type(img) is np.ndarray: raise Exception('"img" must be image data from opencv') return cv2.stylization(img, sigma_s=sigma_s, sigma_r=sigma_r)
[문서] def detailEnhance(self, img, sigma_s=100, sigma_r=0.05): """ 만화 이미지로 변환합니다. (opencv api) :param numpy.ndarray img: 이미지 객체 :param float sigma_s: 이미지의 blur 보존 정도 (1-200) :param float sigma_r: 이미지의 Edge 적용 정도 (0-1) :returns: ``numpy.ndarray`` 이미지 객체 """ if not type(img) is np.ndarray: raise Exception('"img" must be image data from opencv') return cv2.detailEnhance(img, sigma_s=sigma_s, sigma_r=sigma_r)
[문서] def pencilSketch(self, img, sigma_s=100, sigma_r=0.2, shade_factor=0.018): """ 스케치 이미지로 변환합니다. example:: img = camera.read() camera.sketchize(img) :param numpy.ndarray img: 이미지 객체 :param float sigma_s: 이미지의 blur 보존 정도 (1-200) :param float sigma_r: 이미지의 Edge 적용 정도 (0-1) :param float shade_factor: 이미지의 밝기 정도 (0-0.1) :returns: ``numpy.ndarray`` 이미지 객체 / grayscale, bgr """ if not type(img) is np.ndarray: raise Exception('"img" must be image data from opencv') return cv2.pencilSketch(img, sigma_s=sigma_s, sigma_r=sigma_r, shade_factor=shade_factor)
[문서] def edgePreservingFilter(self, img, flags=1, sigma_s=60, sigma_r=0.4): """ 흐림 이미지로 변환합니다. :param numpy.ndarray img: 이미지 객체 :param int flags: 필터 종류 1 (RECURS_FILTER) or 2 (NORMCONV_FILTER) :param float sigma_s: 이미지의 blur 보존 정도 (1-200) :param float sigma_r: 이미지의 Edge 적용 정도 (0-1) :returns: ``numpy.ndarray`` 이미지 객체 """ if not type(img) is np.ndarray: raise Exception('"img" must be image data from opencv') return cv2.edgePreservingFilter(img, flags=flags, sigma_s=sigma_s, sigma_r=sigma_r)
[문서] def flip(self, img, flags=1): """ 상하/좌우 대칭 이미지로 변환합니다 :param numpy.ndarray img: 이미지 객체 :param int flags: 0: 상하 대칭, 1: 좌우 대칭, -1: 상하/좌우 대칭 :returns: ``numpy.ndarray`` 이미지 객체 """ if not type(img) is np.ndarray: raise Exception('"img" must be image data from opencv') return cv2.flip(img, flags)