728x90

SSD 얼굴 검출

import sys
import numpy as np
import cv2


model = 'opencv_face_detector/res10_300x300_ssd_iter_140000_fp16.caffemodel'
config = 'opencv_face_detector/deploy.prototxt'
# model = 'opencv_face_detector/opencv_face_detector_uint8.pb'
# config = 'opencv_face_detector/opencv_face_detector.pbtxt'

# cap = cv2.VideoCapture('utub.mp4')
cap = cv2.VideoCapture(0)

if not cap.isOpened():
    print('Camera open failed!')
    sys.exit()

net = cv2.dnn.readNet(model, config)

if net.empty():
    print('Net open failed!')
    sys.exit()

while True:
    ret, frame = cap.read()

    if not ret:
        break
    
    #1: 픽셀 0~255
    #resize 크기(300,300)
    #BGR 평균 (104,177,123) 
    blob = cv2.dnn.blobFromImage(frame, 1, (300, 300), (104, 177, 123))
    net.setInput(blob)
    out = net.forward() #(1,1,200,7) 에서 (200,7)만 필요함  *꼭 200이 아닐수 있음
    
    detect = out[0, 0, :, :] #detect 이차원 행렬을 분석한다.
    (h, w) = frame.shape[:2]

    for i in range(detect.shape[0]): #전체 행만큼 순회
        confidence = detect[i, 2] #confidence: 얼굴일 확률
        if confidence < 0.5:
            break
        # print('detect[i, 3]',detect[i, 3])
        #좌표가0~1로 정규화 되어 있으므로 크기를 직접 곱해서 좌표를 구한다    
        #x1,y1:좌측상단       
        x1 = int(detect[i, 3] * w)
        y1 = int(detect[i, 4] * h)
        
        # x2,y2: 우측하단
        x2 = int(detect[i, 5] * w)
        y2 = int(detect[i, 6] * h)

        cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0))

        label = f'Face: {confidence:4.2f}'
        # (x1, y1-1): text 위치 지정
        cv2.putText(frame, label, (x1, y1-1), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 1, cv2.LINE_AA)

    cv2.imshow('frame', frame)

    if cv2.waitKey(1) == 27:
        break

cv2.destroyAllWindows()

 

yolo_v3 객체 검출

import sys
import numpy as np
import cv2


# 모델 & 설정 파일
model = 'yolo_v3/yolov3.weights'
config = 'yolo_v3/yolov3.cfg'
class_labels = 'yolo_v3/coco.names'
confThreshold = 0.5
nmsThreshold = 0.4

# 테스트 이미지 파일
img_files = ['person.jpg', 'sheep.jpg', 'kite.jpg']

# 네트워크 생성
net = cv2.dnn.readNet(model, config)

if net.empty():
    print('Net open failed!')
    sys.exit()

# 클래스 이름 불러오기

classes = [] #총 80개가 들어옴
with open(class_labels, 'rt') as f:
    classes = f.read().rstrip('\n').split('\n')

#low=0, high=255, size=(80,3)
#3은 RGB
colors = np.random.uniform(0, 255, size=(len(classes), 3)) #80개 각각 다른 색깔

# 출력 레이어 이름 받아오기
# 출력을 하는 3개의 layer 이름을 뽑는다
layer_names = net.getLayerNames()
output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
# output_layers = ['yolo_82', 'yolo_94', 'yolo_106'] 
'''
* 첫번째 82번 layer: 13*13영상 *3(RGB)=507에다가
컬럼은 총 85개 인데 순서대로 x,y,w,h,confidences,80개 클래스(해당 클래스일 확률이 들어감)
종합해서 507*8,
* 두번째 94번 layer: 2028,85
* 세번재 106번 layer: 8112,85
'''

# 실행

for f in img_files:
    img = cv2.imread(f)

    if img is None:
        continue

    # 블롭 생성 & 추론
    blob = cv2.dnn.blobFromImage(img, 1/255., (416, 416), swapRB=True)
    net.setInput(blob)

    
    #forward(문자열 리스트):문자열에 해당하는 레이어의 out을 outs에 전달
    outs = net.forward(output_layers)

    # outs는 3개의 ndarray 리스트.
    # 아래 값은 입력이 416일때
    # outs[0].shape=(507, 85), 13*13*3=507
    # outs[1].shape=(2028, 85), 26*26*3=2028
    # outs[2].shape=(8112, 85), 52*52*3=8112

    h, w = img.shape[:2]

    class_ids = []
    confidences = []
    boxes = []

    for out in outs:
        #하나의 행 씩 85개중 앞에 4개는 바운딩박스의 좌표값
        # 그 다음은 objectness Score, 나머지 80개는 Class Scores(80개 클래스에 대한 각각의 확률값)
        for detection in out:
            # detection: 4(bounding box) + 1(objectness_score) + 80(class confidence)
            scores = detection[5:] #80개 클래스 확률값 
            class_id = np.argmax(scores) #80개 클래스 중에서 가장 최대값을 가지는 확률값의 인덱스
            confidence = scores[class_id]
    
            if confidence > confThreshold: #클래스에 대한 확률값이 confThreshold 이상인 경우에만바운딩박스를 취합한다
                # 바운딩 박스 중심 좌표 & 박스 크기
                
                #cx,cy: 바운딩박스 센터좌표
                cx = int(detection[0] * w)
                cy = int(detection[1] * h)
                bw = int(detection[2] * w)
                bh = int(detection[3] * h)

                # 바운딩 박스 좌상단 좌표
                sx = int(cx - bw / 2)
                sy = int(cy - bh / 2)

                #boxes안에 객체 검출할때 쓰인 모든 박스 정보가 들어있다
                #이중에서 대표 박스 하나를 골라야 하므로 NMSBoxes를 사용해야한다
                boxes.append([sx, sy, bw, bh]) 
                confidences.append(float(confidence))
                class_ids.append(int(class_id)) #인식한 객체가 인덱스로 정리되어 있음

    # 비최대 억제
    '''
    한 객체에 여러 바운딩 박스가 있으므로 이 여러 박스 중 가장 좋은 박스를 뽑아내기
    예를들어 두 박스가 있는데 이 겹쳐진 부분이 nmsThreshold%만큼 겹쳐진 것들중
    confidence 값이 confThreshold이상인 것들중에 가장 큰 confidence를 갖는 하나만 골라서 
    indices에 넣기(몇번째 박스인지에 대한 정보)
    '''
    #정리:40% 이상 겹치는 바운딩 박스에 대해 최대 confidence (>0.5)바운딩 박스만 선별
    #nmsThreshold값이 0.99라면 99%이상의 공간을 같이 공유하는 박스들이 겹치게 될것
    # indices.shape=(N, 1)
    indices = cv2.dnn.NMSBoxes(boxes, confidences, confThreshold, nmsThreshold)

    for i in indices: #각 객체당 선별된 박스가 for loop를 통해 정보 추출
        # print(i)
        i = i[0]
        print
        sx, sy, bw, bh = boxes[i]
        label = f'{classes[class_ids[i]]}: {confidences[i]:.2}'
        color = colors[class_ids[i]]
        cv2.rectangle(img, (sx, sy, bw, bh), color, 2)
        cv2.putText(img, label, (sx, sy - 10),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2, cv2.LINE_AA)

    #getPerfProfile: 실행시간 계산에 관련된 함수  
    t, _ = net.getPerfProfile()
    label = 'Inference time: %.2f ms' % (t * 1000.0 / cv2.getTickFrequency())
    cv2.putText(img, label, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
                0.7, (0, 0, 255), 1, cv2.LINE_AA)

    cv2.imshow('img', img)
    cv2.waitKey()

cv2.destroyAllWindows()

 

Mask_rcnn

import sys
import numpy as np
import cv2


def drawBox(img, classId, conf, left, top, right, bottom):
    # Draw a bounding box.
    cv2.rectangle(img, (left, top), (right, bottom), colors[classId], 2)

    label = f'{classes[classId]}: {conf:.2f}'

    labelSize, baseLine = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 1)
    top = max(top, labelSize[1])
    cv2.rectangle(img, (left - 1, top - labelSize[1] - baseLine),
                  (left + labelSize[0], top), colors[classId], -1)
    cv2.putText(img, label, (left, top - baseLine), cv2.FONT_HERSHEY_SIMPLEX,
                0.6, (0, 0, 0), 1, cv2.LINE_AA)


# 모델 & 설정 파일
model = 'mask_rcnn/frozen_inference_graph.pb'
config = 'mask_rcnn/mask_rcnn_inception_v2_coco_2018_01_28.pbtxt'
class_labels = 'mask_rcnn/coco_90.names'
confThreshold = 0.6
maskThreshold = 0.3

# 테스트 이미지 파일
img_files = ['dog.jpg', 'traffic.jpg', 'sheep.jpg']

# 네트워크 생성
net = cv2.dnn.readNet(model, config)

if net.empty():
    print('Net open failed!')
    sys.exit()

# 클래스 이름 불러오기

classes = []
with open(class_labels, 'rt') as f:
    classes = f.read().rstrip('\n').split('\n')

colors = np.random.uniform(0, 255, size=(len(classes), 3))

# 전체 레이어 이름 받아오기
'''
객체를 바운딩박스 치는 output: (1,1,100,7) => 최대100개까지 객체 검출 가능 하며 7은 클래스 정보 또는 바운딩박스정보
바운딩 된 객체에서 윤곽선을 도출하는 output: (100,90,15,15) => 90개 class에 대한 15*15마스크가 출력된다
                                                        각각의 100개 객체에 대해서 90개의 마스크맵을 출력으로 준다   
'''

layer_names = net.getLayerNames() #네트워크의 모든 레이어 이름 가져오기
# net.getUnconnectedOutLayers(): 출력 레이어의 인덱스를 가져오기(인덱스는 332)
# output_layers: detection_masks 1개(출력레이어)
# 위 내용을 가지고 생각해 보면 네트워크 layer 중에서 마지막 출력 레이어는 네트워크 레이어에 포함되지 않는다는 걸로 연결됨
# 레이어 종류가 네트워크 레이어, 출력 레이어 이렇게 두개로 나뉜걸로 생각할수 있음(뇌피셜이라 확실치 않음)
output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
for name in  layer_names:
    print(name)

# 실행

for f in img_files:
    img = cv2.imread(f)

    if img is None:
        continue

    # 블롭 생성 & 추론

    #blob의 사이즈는 지정 안해도 됨->이미지 크기와 동일한 크기로 만들어줌
    #평균값 디폴트는 0 이므로 여기서는 따로 지정 안함
    blob = cv2.dnn.blobFromImage(img, swapRB=True) #RGB로 학습되어 있음
    net.setInput(blob)

    #detection_out_final: 바운딩박스 정보
    #detection_masks: 각각의 바운딩 박스에서 마스크 정보를 가지고 있음
    #큰 순서: 각 객체의 바운딩박스 정보를 추출한 후에 그 박스 안에 있는 객체윤곽를 추출
    boxes, masks = net.forward(['detection_out_final', 'detection_masks'])

    # boxes.shape=(1, 1, 100, 7)
    # masks.shape=(100, 90, 15, 15)
    '''
    객체를 바운딩박스 치는 output: (1,1,5,7) => 5개 객체 검출함 클래스명을 직접 조사했지만 dog사진에 4개이상의 클래스명은 찾을수 없었다
                                                다시말해 같은 객체를 중복하여 검출했을 거라 생각해 볼수 있다. 
        바운딩 된 객체에서 윤곽선을 도출하는 output: (100,90,15,15) => 90개 class에 대한 15*15마스크가 출력된다
                                                         각각의 100개 객체에 대해서 90개의 마스크맵을 출력으로 준다   
    '''
    h, w = img.shape[:2]
    numClasses = masks.shape[1]  # 90(coco dataset의 class 개수)
    numDetections = boxes.shape[2]  # 5(detection 개수, 100개가 기본값이며 이보다 낮게 나올수도 있다)

    boxesToDraw = []
    for i in range(numDetections):
        box = boxes[0, 0, i]  # box.shape=(7,) 검출된 5개 객체의 바운딩 박스 정보를 가져온다
                              # (7,)짜리가 5개가 있는것이다.
        # classID: 몇번째에 대한 바운딩 박스 인가를 나타냄
        # confidence: 확률값(임계값보다 커야 제대로 찾았다고 인식한다)
        # 입력영상의 크기가 0~1 정규화가 되었다는 가정 하에 x1y1:좌측상단 , x2y2:우측하단
        #box: (0,classID,confidence, x1,y1,x2,y2) -> (7,)
        
        #i번째 객체 검출한것에 대해서 15*15 마스크 맵이 90개가 있는 것
        #90개 마스크를 다 쓰는게 아니라 94번줄의 ClassId에 해당하는 것만 사용
        mask = masks[i]  # mask.shape=(90, 15, 15)
        score = box[2] #confidence값
        if score > confThreshold: # 임계값보다 커야 제대로 찾은 것이다.
            classId = int(box[1]) #classID 추출
            #print(classId, classes[classId], score)

            #변환된 좌표
            x1 = int(w * box[3])
            y1 = int(h * box[4])
            x2 = int(w * box[5])
            y2 = int(h * box[6])

            #변환된 좌표와 원래 img크기를 비교해서
            #변환된 좌표가 본 이미지 크기보다 큰 경우를 없애기 위한 코드?
            x1 = max(0, min(x1, w - 1))
            y1 = max(0, min(y1, h - 1))
            x2 = max(0, min(x2, w - 1))
            y2 = max(0, min(y2, h - 1))

            boxesToDraw.append([img, classId, score, x1, y1, x2, y2])
            
            # classMask: 15*15짜리 행렬의 float형태의 마스크
            # 이 안에서 배경에 해당하는 부분은 값이 작을것이고, 객체에 해당하는 건 값이 크다
            classMask = mask[classId] #ClassID에 해당하는 mask 정보를 가져온다

            # 객체별 15x15 마스크를 바운딩 박스 크기로 resize한 후, 불투명 컬러로 표시
            classMask = cv2.resize(classMask, (x2 - x1 + 1, y2 - y1 + 1)) 
            
            #바운딩박스 안에는 배경과 객체가 있는데, 어떠한 기준값이 있어야 객체와 배경을 구분할수가 있다
            #이 구분하기 위한 기준 값이 maskThreshold (maskThreshold보다 크면 객체 아니면 배경)
            mask = (classMask > maskThreshold)

            #객체마다 다른 색깔로 불투명하게 하는 코드-> 결과영상에서 클래스 윤곽정보를 보여줌
            roi = img[y1:y2+1, x1:x2+1][mask] #원본 실제 객체 이미지
            img[y1:y2+1, x1:x2+1][mask] = (0.7 * colors[classId] + 0.3 * roi).astype(np.uint8)

    # 객체별 바운딩 박스 그리기 & 클래스 이름 표시-> 이게 없으면 결과영상에서 바운딩 박스가 없다
    for box in boxesToDraw:
        drawBox(*box)

    t, _ = net.getPerfProfile()
    label = 'Inference time: %.2f ms' % (t * 1000.0 / cv2.getTickFrequency())
    cv2.putText(img, label, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
                0.7, (0, 0, 255), 1, cv2.LINE_AA)

    cv2.imshow('img', img)
    cv2.waitKey()

cv2.destroyAllWindows()

 

OpenPose

import sys
import numpy as np
import cv2


# 모델 & 설정 파일
model = 'openpose/pose_iter_440000.caffemodel'
config = 'openpose/pose_deploy_linevec.prototxt'

# 포즈 점 개수, 점 연결 개수, 연결 점 번호 쌍
nparts = 18 #전체 점의 개수
npairs = 17 # 점과 점 사이를 잇는 직선의 개수(관절표현)

#관절을 어떻게 이을건지를 설정해준다
pose_pairs = [(1, 2), (2, 3), (3, 4),  # 왼팔: 1번점 2번점, 2번점 3번점, 3번점 4번점을 연결하면 왼팔이된다
              (1, 5), (5, 6), (6, 7),  # 오른팔
              (1, 8), (8, 9), (9, 10),  # 왼쪽다리
              (1, 11), (11, 12), (12, 13),  # 오른쪽다리
              (1, 0), (0, 14), (14, 16), (0, 15), (15, 17)]  # 얼굴

# 테스트 이미지 파일
img_files = ['pose1.jpg', 'pose2.jpg', 'pose3.jpg']

# 네트워크 생성
net = cv2.dnn.readNet(model, config)

if net.empty():
    print('Net open failed!')
    sys.exit()

for f in img_files:
    img = cv2.imread(f)

    if img is None:
        continue

    # 블롭 생성 & 추론
    blob = cv2.dnn.blobFromImage(img, 1/255., (368, 368))
    net.setInput(blob)
    out = net.forward()  # out.shape=(1, 57, 46, 46) 46*46가 57개, 앞에서 18개만 사용(keypoint)

    h, w = img.shape[:2]

    # 검출된 점 추출
    points = []
    for i in range(nparts): #nparts:18

        #heatMap 46*46 짜리 행렬이된다
        #heatMap 모양: 관절 부분 point 부분에서 headmap생김
        heatMap = out[0, i, :, :] #46*46의 float32 행렬

        '''
        ##heatmap 시각화##
        heatImg = cv2.normalize(heatMap, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U) #0~1 -> 0~255
        heatImg = cv2.resize(heatImg, (w, h))
        heatImg = cv2.cvtColor(heatImg, cv2.COLOR_GRAY2BGR)
        heatImg = cv2.addWeighted(img, 0.5, heatImg, 0.5, 0)
        cv2.imshow('heatImg', heatImg)
        cv2.waitKey()
        '''
        #point: 46*46 행렬에서의 최대값 위치
        _, conf, _, point = cv2.minMaxLoc(heatMap)

        #point 최대값 위치가 전체 영상에서는 어디에 위치해 있는가를 알기 위함->w,h 곱셈
        #입력영상 해상도에 맞는 특정 관절 위치 x,y => (21,9) 위치를 정 사이즈로 변환
        x = int(w * point[0] / out.shape[3]) #out.shape[3]:46,  point[0]:21
        y = int(h * point[1] / out.shape[2]) #out.shape[2]:46,  point[1]:9

        points.append((x, y) if conf > 0.1 else None)  # heat map threshold=0.1

    # 검출 결과 영상 만들기 
    for pair in pose_pairs:
        p1 = points[pair[0]]
        p2 = points[pair[1]]

        if p1 is None or p2 is None: #67번줄에서 None이 들어오면 무시
            continue

        cv2.line(img, p1, p2, (0, 255, 0), 3, cv2.LINE_AA)

        #각각의 끝점을 원으로 그리기
        cv2.circle(img, p1, 4, (0, 0, 255), -1, cv2.LINE_AA)
        cv2.circle(img, p2, 4, (0, 0, 255), -1, cv2.LINE_AA)

    # 추론 시간 출력
    t, _ = net.getPerfProfile()
    label = 'Inference time: %.2f ms' % (t * 1000.0 / cv2.getTickFrequency())
    cv2.putText(img, label, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
                0.7, (0, 0, 255), 1, cv2.LINE_AA)

    cv2.imshow('img', img)
    cv2.waitKey()

cv2.destroyAllWindows()

 

728x90

'실습 note' 카테고리의 다른 글

bitcoin 예측 실습  (0) 2021.04.14
제조 공정 불량 검출 실습  (0) 2021.03.11
OpenCV_11(딥러닝)  (0) 2021.03.04
OpenCV_10(머신러닝)  (0) 2021.03.03
OpenCV_9(객체 추적과 모션벡터)  (0) 2021.03.01

+ Recent posts