무인 차량의 색상 인식에 OpenCV의 HSV 색상 공간 적용

RGB는 누구에게나 가장 친숙한 3원색 공간에 속하며, 우리가 보는 모든 색은 3원색을 혼합하여 만들 수 있습니다. 그러나 색 공간에서 이미지의 효과적인 처리는 일반적으로 HSV 공간에서 이루어지며, HSV(Hue, Saturation, Brightness Value) 는 색상의 직관적인 특성에 따라 생성된 색 공간으로 육각형 원뿔 모델 이라고도 합니다 .

 

OpenCV에서 HSV 색공간의 값 범위 => H:[0, 180], S:[0, 255], V : [0, 255] H 색상이 작을수록 빨간색에 가깝고 높을수록 파란색에 가깝습니다. 크기 클수록 습니다 . 위의 그림에서 색상 변경을 확인하십시오!
HSV를 선택한 이유는 H로 표현되는 색상이 기본적으로 특정 색상을 결정할 수 있고 채도 및 밝기 정보와 결합하여 특정 임계값보다 큰 것으로 판단할 수 있기 때문입니다. RGB는 3가지 구성요소로 구성되어 있는데 각 구성요소의 기여도를 판단할 필요가 있다. HSV 공간의 인식 범위가 넓어져 사용이 더욱 편리해졌습니다.

1. 데모 예시

 예를 들어, 파란색 외부 포장이 있는 넓고 좁은 담배 한 갑을 가지고 색상을 식별하고 추적해 봅시다.

1.1 색상 인식 및 추적

데스크탑에 카메라가 설치되어 있지 않아서 여기서는 무인차량에 카메라를 사용합니다.

from jetbotmini import Camera
from jetbotmini import bgr8_to_jpeg
import cv2
import numpy as np
import traitlets
import ipywidgets.widgets as widgets
from IPython.display import display

# 目标颜色,这里设置为蓝色数组
color_lower = np.array([100,43,46])
color_upper = np.array([124, 255, 255])
# 相机实例
camera = Camera.instance(width=720, height=720)
# 显示控件(视频也是图片的连续帧)
color_image = widgets.Image(format='jpeg', width=500, height=400)
display(color_image)

# 实时识别颜色并反馈到上面的控件里
while 1:
    frame = camera.value # (720, 720, 3) (H,W,C)
    frame = cv2.resize(frame, (400, 400)) # (400, 400, 3)
    frame = cv2.GaussianBlur(frame,(5,5),0) # 高斯滤波(5, 5)表示高斯矩阵的长与宽都是5,标准差为0
    hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)#将BGR转成HSV
    mask=cv2.inRange(hsv,color_lower,color_upper)
    mask=cv2.erode(mask,None,iterations=2) # 进行腐蚀操作,去除边缘毛躁
    mask=cv2.dilate(mask,None,iterations=2) # 进行膨胀操作
    mask=cv2.GaussianBlur(mask,(3,3),0)
    cnts=cv2.findContours(mask.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2] # 轮廓
    if len(cnts)>0:
        cnt = max(cnts,key=cv2.contourArea) # 轮廓面积
        (color_x,color_y),color_radius=cv2.minEnclosingCircle(cnt) # 外接圆的位置信息
        if color_radius > 10:
            # 圆圈标注
            cv2.circle(frame,(int(color_x),int(color_y)),int(color_radius),(255,0,255),2)
    color_image.value = bgr8_to_jpeg(frame) # 转成图片传入Image组件

추적 화면에서 분홍색 원이 파란색인 것을 확인할 수 있습니다. 그 중 카메라 와 위젯을 활용하는 웹 인터랙티브 컴포넌트가 많이 있는데 , 관심 있는 분들은 다음을 참고할 수 있습니다.

이 코드의 의미는 상대적으로 명확합니다 . 카메라를 인스턴스화하고 , 다양한 색상의 HSV 색역 공간에 따라 이미지의 각 프레임을 분류 및 표시하고 , 먼저 cv2통해 BGR(여기서 읽은 그림은 RGB 대신 BGR임)을 HSV로 변환 합니다 . 주변 조명이 충분할 때 인식 효과가 이상적이지 않은 경우 무한 루프에서 일부 매개 변수 설정을 수동으로 변경할 수 있습니다.

1.2, cv2.inRange

다음으로 위 코드의 일부 함수에 대해 설명하면 마스크를
만들 mask=cv2.inRange(hsv,color_lower,color_upper)는 하한 경계 배열보다 아래의 값과 상한 경계 배열보다 높은 값이 0 검정이고 그 사이의 값이 255 흰색인 단일 채널에 속하는 것을 의미합니다. 직관적으로 이해하기 위해 코드를 살펴보겠습니다. 

import cv2
import matplotlib.pyplot as plt

img_cv2 = cv2.imread('test.jpg')
hsv = cv2.cvtColor(img_cv2, cv2.COLOR_BGR2HSV)
lowerb = np.array([20, 20, 20])
upperb = np.array([200, 200, 200])

# 黑白单通道(H,W)
mask = cv2.inRange(hsv, lowerb, upperb)
cv2.imshow('Display', mask)
cv2.waitKey(0)
cv2.destroyAllWindows()

plt.subplot(1,2,1); plt.imshow(img_cv2,aspect='auto');plt.axis('off');plt.title('BGR')
plt.subplot(1,2,2); plt.imshow(mask,aspect='auto');plt.axis('off');plt.title('mask')
plt.show()

 아래와 같이 BGR 및 마스크:

 

1.3, cv2.erode 및 cv2.dilate

Corrosion 연산은 image morphology에 속한다.그것은 문자 그대로의 의미와 같다.Corrosion이 수행된다.이 함수의 도움을 확인할 수 있다: erode( src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]]) -> dst 원본 src 이미지의 크기는 대상 이미지 dst 의 크기와 동일하며, 여기서 커널 코어의 크기는 침식의 크기를
결정
하는데 , 이는 선택적 이다 . 코드 테스트에서:

import cv2
import numpy as np

image = cv2.imread('test.jpg')
kernel = np.ones((5, 5), np.uint8)
image = cv2.erode(image, None)
cv2.imshow('erode', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

여기에서 image = cv2.erode(image, None) 두 번째 커널 매개변수를 지정하거나 지정하지 않을 수 있습니다. image = cv2.erode(image, kernel) 지정 후 커널 크기를 수정하여 효과를 확인하십시오.

컨볼루션 연산을 하는 것이 본질이며, 연산에서 1이 아닌 숫자는 0으로 하고, 모두 1일 때만 1로 한다. 이 기능은 가장자리의 일부 노이즈 결함을 제거하는 데 사용되는 코드의 주석과 같습니다. cv2.dilate 확장 함수는 부식 함수의 역동작으로 볼 수 있으며, 부식 후 검은 부분이 확장되고 확장 함수가 축소됩니다.

1.4、cv2.GaussianBlur

가우시안 블러는 노이즈 제거에도 사용됩니다.그림에 가우시안 노이즈를 추가한 다음 이 기능을 사용하여 주로 가우시안 노이즈에 대한 노이즈 제거 효과를 만들어 보겠습니다. 

import cv2  as cv
import numpy as np
 
def myShow(name,img):
    cv.imshow(name,img)
    cv.waitKey(0)
    cv.destroyAllWindows()
# 加高斯噪声
def addGauss(img,mean=0,val=0.01):
    img = img / 255
    gauss = np.random.normal(mean,val**0.05,img.shape)
    img = img + gauss
    return img

img = cv.imread('gauss.png')
img1 = addGauss(img)
myShow('img1',img1)

img2 = cv.GaussianBlur(img1,(3,3),0)
myShow('img2',img2)

여기서는 아래와 같이 원본 이미지와 가우시안 노이즈 및 노이즈 감소 처리가 적용된 세 개의 이미지를 함께 넣습니다.

1.5, cv2.findContours 및 drawContours 

윤곽 함수 findContours(image, mode, method[, contours[, hierarchy[, offset]]]) -> contours, hierarchy는 이진 이미지  에서 감지되므로 먼저 회색조 이미지로 변환하고 임계값을 통해 이진 이미지로 변환합니다 .
마지막으로 외곽선을 그리고 drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]] -> image를 사용합니다. 코드 구현을 살펴보겠습니다.

import cv2
import numpy as np

image = cv2.imread('test.jpg')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
image = cv2.drawContours(image,contours,-1,(255,0,0),2)

#cv2.imshow('erode',binary)
cv2.imshow('erode',image)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

cv2.RETR_EXTERNAL : 외부 윤곽을 감지하고 윤곽 내부의 구조를 무시합니다.
cv2.CHAIN_APPROX_SIMPLE : 가로, 세로, 대각선 방향의 요소를 압축하고 이 방향의 끝점 좌표만 유지합니다.
cv2.CHAIN_APPROX_NONE : 모든 윤곽점을 저장하고 인접한 두 점 사이의 픽셀 위치 차이는 1을 초과하지 않습니다.

1.6, HSV 색상 값 

여기서는 파란색을 예로 들었는데 다른 색을 원하면? 아래 그림과 같이 HSV의 값은 얼마입니까?

 

2. OpenCV 지식 포인트

여기에는 OpenCV 지식이 많이 사용되는데, 그림을 읽어서 회색 그림으로 변환하는 등 일반적으로 사용되는 것들에 익숙해지자. 

2.1, 사진 읽기 및 표시 

import cv2

img = cv2.imread('test.jpg', 0)
cv2.imshow("image",img)
# 如果注释下面的等待按键和释放窗口资源,会出现“窗口未响应”的状态,不能正常显示图片
cv2.waitKey()
cv2.destroyAllWindows()

 물론 이 시각적 라이브러리가 설치되어 있지 않으면 오류가 보고됩니다: ModuleNotFoundError: No module named 'cv2'

설치 명령: pip install opencv-python -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com 

이미지 cv2.imread() 를 읽을 때 두 번째 매개변수는 0으로 회색조 모드를 나타냅니다. 0 대신 cv2.IMREAD_GRAYSCALE을 사용할 수도 있습니다. 다른  숫자 다음
과 같습니다.
1은 컬러 이미지 cv2.IMREAD_GRAYSCALE 을 나타냅니다.

2.2, 사진 읽기 및 저장

img = cv2.imread('test.jpg', 0)
cv2.imwrite('new.jpg', img)

이런 식으로 회색조 모드에서 읽은 이미지는 cv2.imwrite 에 의해 새로운 이미지로 저장됩니다 .

2.3, 비디오 읽기 및 표시 

마찬가지로 비디오의 효과를 살펴보자 대중적인 이해는 비디오가 그 안의 각 프레임(그림)을 지속적으로 읽는다는 것입니다. 

import cv2

cap = cv2.VideoCapture('test.mp4')
print(cap.read()[1].shape) # (1080, 1920, 3)

 이렇게 동영상의 한 프레임을 읽어서 반환값이 튜플인데, 동영상 전체를 읽어들인다면? 한 번 보자:

import cv2

cap = cv2.VideoCapture('test.mp4')
if (cap.isOpened() == False):
    print('不能打开视频文件')
else:
    fps = cap.get(cv2.CAP_PROP_FPS) # 参数可直接使用5代替
    print('每帧速度:', fps,'FPS') # 每帧速度: 29.97002997002997 FPS
    f_count = cap.get(cv2.CAP_PROP_FRAME_COUNT) # 7
    print('总帧数: ', f_count) #总帧数:  3394.0

while(cap.isOpened()):
    ret, frame = cap.read()
    if ret == True:
        cv2.imshow('Frame',frame)
        key = cv2.waitKey(20)
        # 按q键退出
        if key == ord('q'):break
    else:
        break

# 注意释放资源
cap.release()
cv2.destroyAllWindows()

이런 식으로 비디오는 그림이 표시되는 것처럼 표시됩니다. 그 중 cv2.waitKey(20)는 연속된 프레임 사이에 20밀리초 동안 기다린다는 의미로, 값이 클수록 대기 시간이 길어지며 동영상 재생이 느려지는 것을 확인할 수 있습니다.
물론 더 중요한 것은 카메라 감시 영상을 확보하는 것이다.

# 参数 0 表示设备的默认摄像头,当设备有多个摄像头时可以改变参数选择
cap = cv2.VideoCapture(0)

물론 일부 외부 카메라의 ID는 0이 아닐 수 있으며 순회를 사용하여 가져올 수 있습니다.

import cv2
ID = 0
while(1):
    cap = cv2.VideoCapture(ID)
    ret, frame = cap.read()
    if ret == False:
        ID += 1
    else:
        print(ID)
        break

 2.4 여러 장의 사진을 동영상으로 합치기

디렉토리에 있는 사진을 비디오에 쓰는 방법은 이전 방법과 비슷하지만 컴포지트 비디오에 있는 사진의 크기가 같아야 한다는 점에 유의해야 합니다 .

import cv2
import os
path ='imgs'
size = (600,400) # (W,H)
fps = 1
#fourcc = cv2.VideoWriter_fourcc('X','V','I','D')
fourcc = cv2.VideoWriter_fourcc(*'XVID')
video = cv2.VideoWriter('hi.avi',fourcc,fps,size)

for item in os.listdir(path):
    if item.lower().endswith('.jpg'):
        img = cv2.imread(os.path.join(path,item))
        img1 = cv2.resize(img, size, interpolation=cv2.INTER_CUBIC)
        print(img1.shape) # (H,W,C)
        video.write(img1)
video.release()
cv2.destroyAllWindows()

보간 매개변수 로 지정된 보간 방법 :

INTER_NEAREST : 최근접 이웃 보간
INTER_LINEAR : 쌍선형 보간, 기본값
INTER_CUBIC : 4x4 픽셀 이웃 내 바이큐빅 보간
INTER_LANCZOS4 : 8x8 픽셀 이웃 내 Lanczos 보간

2.5, 직선, 직사각형, 원형 ​​및 기타 모양

실제 응용 프로그램에서 매우 일반적으로 사용되는 몇 가지 일반적인 모양은 다음과 같습니다.

2.5.1 직선

cv2.line(img, startPoint, endPoint, color, thickness)
startPoint	:起始位置像素坐标
endPoint:结束位置像素坐标
color:绘制的颜色
thickness:绘制的线条宽度

2.5.2, 원

cv2.circle(img, centerPoint, radius, color, thickness)
img:需要绘制的目标图像对象
centerPoint:绘制的圆的圆心位置像素坐标
radius:绘制的圆半径
color:绘制的颜色
thickness:绘制的线条宽度(thickness 是负数,表示圆被填充)

2.5.3 직사각형

cv2.rectangle(img, point1, point2, color, thickness)
img:需要绘制的目标图像对象
point1:左上顶点位置像素坐标
point2:右下顶点位置像素坐标
color:绘制的颜色
thickness:绘制的线条宽度

2.5.4 텍스트

cv2.putText(img, text, point, font, size, color, thickness)
img:需要绘制的目标图像对象
text:绘制的文字
point:左上顶点位置像素坐标
font:绘制的文字格式
size:绘制的文字大小
color:绘制使用的颜色
thickness:绘制的线条宽度

2.5.5 이미지 스케일링

cv2.resize(InputArray src, OutputArray dst, Size, fx, fy, interpolation)
InputArray src:输入图片
OutputArray dst:输出图片
Size:输出图片尺寸
fx, fy:沿x轴,y轴的缩放系数
interpolation:插值方法

3、bgr8_to_jpeg

마지막으로 이미지 컴포넌트에 이미지를 표시해야 하는 경우에는 각 프레임의 이미지를 이미지 컴포넌트로 피드백해야 하고 각 프레임의 이미지는 BGR, 이미지 컴포넌트의 포맷은 jpeg이므로 변환이 필요하다.
함수 bgr8_to_jpeg 는 이미지를 압축하는 imencode 함수의 본질적인 캡슐화인 메모리 버퍼에 이미지를 인코딩하는 것입니다 .
함수의 소스 코드는 다음과 같습니다.

def bgr8_to_jpeg(value, quality=75):
    return bytes(cv2.imencode('.jpg', value)[1])

내부의 imencode 함수는 다음과 같습니다.

imencode(ext, img[, params]) -> retval, buf
ext:定义输出格式的文件扩展名
img:要写入的图像
buf:输出缓冲区调整大小,以适应压缩图像

함수 소스 코드나 소스 파일 보기가 불편한 상황을 빠르게 보기:

import inspect
print(inspect.getsource(bgr8_to_jpeg))

Supongo que te gusta

Origin blog.csdn.net/weixin_41896770/article/details/131746841
Recomendado
Clasificación