Skip to content

OpenCV:Example:IronPlateCamber

철판 솟음 (Camber; 휘어짐) 측정

Code

import cv2
import glob
import time
import numpy as np
from math import sqrt
from typing import Tuple
from datetime import datetime

# 접점
def get_crosspt(ST1, ST2, ED1, ED2):

    x11, y11 = ST1[0], ST1[1]
    x12, y12 = ST2[0], ST2[1]
    x21, y21 = ED1[0], ED1[1]
    x22, y22 = ED2[0], ED2[1]

    if x12 == x11 or x22 == x21:
        # print('delta x=0')
        if x12 == x11:
            cx = x12
            m2 = (y22 - y21) / (x22 - x21)
            cy = m2 * (cx - x21) + y21
            return np.array([round(cx), round(cy)])
        if x22 == x21:
            cx = x22
            m1 = (y12 - y11) / (x12 - x11)
            cy = m1 * (cx - x11) + y11
            return np.array([round(cx), round(cy)])

    m1 = (y12 - y11) / (x12 - x11)
    m2 = (y22 - y21) / (x22 - x21)
    if m1 == m2:
        print('parallel')
        return None
    # print(x11,y11, x12, y12, x21, y21, x22, y22, m1, m2)
    cx = (x11 * m1 - y11 - x21 * m2 + y21) / (m1 - m2)
    cy = m1 * (cx - x11) + y11

    return np.array([round(cx), round(cy)])
# 2개의 점으로 기울기,상수 구하기
def cal_ab(PT1,PT2):
    x1,y1 = PT1[0],PT1[1]
    x2,y2 = PT2[0],PT2[1]
    # y = ax + b
    a = (y2 - y1)/(x2 - x1)
    b = (x2*y1 - x1*y2)/(x2 - x1)

    return a,b
Point = Tuple[float, float]
# 2개점 사이의 길이 구하는 함수
def norm(p1: Point, p2: Point) -> float:
    x1, y1 = p1
    x2, y2 = p2
    dx = x2 - x1
    dy = y2 - y1
    return sqrt((dx ** 2) + (dy ** 2))
# 2개의 점을 지나는 직선과 1개의 점 사이의 거리
def cal_dist(Lpt,Rpt,Countorpt):
    x1,y1 = Lpt[0],Lpt[1]
    x2,y2 = Rpt[0],Rpt[1]
    a,b = Countorpt[0][0],Countorpt[0][1]
    area = abs((x1-a) * (y2-b) - (y1-b) * (x2 - a))
    AB = ((x1-x2)**2 + (y1-y2)**2) **0.5
    distance = area/AB
    return distance


# 파일로드설정
# data_list = glob.glob('data/*/*.mp4')
# d_num = 4 # 4 short 5 long
# Vid = cv2.VideoCapture(data_list[d_num])
Vid = cv2.VideoCapture('curve_origin2.mp4')
w = round(Vid.get(cv2.CAP_PROP_FRAME_WIDTH))
h = round(Vid.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = Vid.get(cv2.CAP_PROP_FPS)
speed = 2 # play speed

# 동영상 저장
fourcc = cv2.VideoWriter_fourcc(*'mp4v')  # mp4v
# file_name = data_list[d_num].split('/')[-1]
# surfix = '.curve'
# out = cv2.VideoWriter(f'video_result/curve_result2.mp4', fourcc, fps, (w, h))

# color
red = (0, 0, 255)
orange = (0, 128, 255)
yellow = (0, 255, 255)
green = (0, 255, 0)
skyblue = (255, 255, 0)
blue = (255, 0, 0)
magenta = (255, 0, 255)
black = (0, 0, 0)
white = (255, 255, 255)

# 변수 초기값
pivot_crossL = None
pivot_crossR = None

# 동영상 재생 시작
while Vid.isOpened():
    begin = datetime.now()
    ret, frame = Vid.read()

    # 전처리(전 기능 공통)
    img_gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
    ret, dst = cv2.threshold(img_gray, 130, 255, cv2.THRESH_BINARY)     # rect 170 contour 125

    # 출력프레임 설정
    show_frame = frame

    # 최대넓이윤곽추출 contours / type np.ndarray / shape (N,1,2)
    contours, hierarchy = cv2.findContours(dst, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    c_list = []
    for index in range(len(contours)):
        c_list.append(cv2.contourArea(contours[index]))
    max_index = c_list.index(max(c_list))
    largest_contour_size = c_list[max_index]
    largest_contour = contours[max_index]

    # 출력세팅
    Ltxt_x = 20
    Rtxt_x = 750
    txt_y = 1300
    y_mr = 250
    Fface = cv2.FONT_HERSHEY_SIMPLEX
    Fsize = 2.5
    Lthick = 10
    Ltype = cv2.LINE_AA

    # 기준선 세로
    topPt = [650, 100]
    botPt = [445, 1600]
    cv2.line(show_frame, topPt, botPt, white, 5)

    # 기준선 가로
    Lpt = [220, 1520]
    Rpt = [640, 1550]
    cv2.line(show_frame, Lpt, Rpt, white, 5)

    # 특정 크기 이상일때 실행
    if max(c_list) > 12000:
        cv2.drawContours(show_frame, contours, max_index, skyblue, 3)

        contours_min = np.argmin(largest_contour, axis = 0)

        y_Min = largest_contour[contours_min[0][1]][0][1]

        # 모든 contour의 각 점에 대하여 반복문 돌림
        #  좌우 합의 평균을 기준으로 x값을 비교하여 2구간으로 나눔. (좌,우 구분)
        # 1.가로기준선의 방정식에 맞는 해가 있으면 그 점은 접점임.
        # 2.없을 경우 기준선의 위쪽,아래쪽에 각각 거리가 가장 짧은 2점을 잡고 교점을 구함.

        w, b = cal_ab(Lpt, Rpt)

        dtLplus_index = []
        dtLmins_index = []
        dtRplus_index = []
        dtRmins_index = []
        dtLplus_list = []
        dtLmins_list = []
        dtRplus_list = []
        dtRmins_list = []

        crossL = None
        crossR = None

        for i, pt in enumerate(largest_contour):

            if pt[0][0] < (Lpt[0]+Rpt[0])/2:
                if pt[0][1] == round(w * pt[0][0] + b):
                    # print('한번에 접점 찾음')
                    crossL = pt.reshape(2,)
                    # break
                elif pt[0][1] < round(w * pt[0][0] + b):        # opencv상에서 y축 반대개념
                    # print('양의 최소값 찾는중')
                    dtLplus_index.append(i)
                    dtLplus_list.append(cal_dist(Lpt, Rpt, pt))
                elif pt[0][1] > round(w * pt[0][0] + b):
                    # print('음의 최소값 찾는중')
                    dtLmins_index.append(i)
                    dtLmins_list.append(cal_dist(Lpt, Rpt, pt))

            # print('--------------------- 여기서 왼쪽점 끝나고 오른쪽으로 넘어가야함 -----------')

            if pt[0][0] >= (Lpt[0]+Rpt[0])/2:
                if pt[0][1] == round(w * pt[0][0] + b):
                    # print('한번에 접점 찾음')
                    crossR = pt.reshape(2,)
                    # continue
                elif pt[0][1] < round(w * pt[0][0] + b):        # opencv상에서 y축 반대개념
                    # print('양의 최소값 찾는중')
                    dtRplus_index.append(i)
                    dtRplus_list.append(cal_dist(Lpt, Rpt, pt))
                elif pt[0][1] > round(w * pt[0][0] + b):
                    # print('음의 최소값 찾는중')
                    dtRmins_index.append(i)
                    dtRmins_list.append(cal_dist(Lpt, Rpt, pt))

                # print('---------- 1frame 끝 ----------------')


        if crossL is None and len(dtLplus_list) != 0 and len(dtLmins_list) !=0:
            plusmin = largest_contour[dtLplus_index[dtLplus_list.index(min(dtLplus_list))]].reshape(2,)
            minsmin = largest_contour[dtLmins_index[dtLmins_list.index(min(dtLmins_list))]].reshape(2,)
            crossL = get_crosspt(Lpt,Rpt,plusmin,minsmin)

        if crossR is None and len(dtRplus_list) != 0 and len(dtRmins_list) !=0:
            plusmin = largest_contour[dtRplus_index[dtRplus_list.index(min(dtRplus_list))]].reshape(2,)
            minsmin = largest_contour[dtRmins_index[dtRmins_list.index(min(dtRmins_list))]].reshape(2,)
            crossR = get_crosspt(Lpt,Rpt,plusmin,minsmin)

        if crossL is None or crossR is None:
            dtL = '0'
            dtR = '0'
        else:
            # 초기값 할당
            if pivot_crossL is None:
                pivot_crossL = crossL
                pivot_crossR = crossR

            # 이전프레임의 2개 교점과 현재프레임의 2개 교점의 길이
            dtL = round(norm(crossL, pivot_crossL), 1)
            dtR = round(norm(crossR, pivot_crossR), 1)

            # -= 부호판별식
            if crossL[1] > pivot_crossL[1]: dtL = -dtL
            if crossR[1] < pivot_crossR[1]: dtR = -dtR

            dtL, dtR = str(dtL), str(dtR)

            # 이전프레임 교점 2개
            cv2.line(show_frame, pivot_crossL, pivot_crossL, red, 10)
            cv2.line(show_frame, pivot_crossR, pivot_crossR, red, 10)

            # 현재프레임 교점 2개
            cv2.line(show_frame, crossL, crossL, blue, 10)
            cv2.line(show_frame, crossR, crossR, blue, 10)

            pivot_crossL = crossL
            pivot_crossR = crossR

        # 왼쪽 글자
        cv2.putText(show_frame, f'{dtL}', (Ltxt_x, txt_y + y_mr), Fface, Fsize, black, Lthick + 10, Ltype)
        cv2.putText(show_frame, f'{dtL}', (Ltxt_x, txt_y + y_mr), Fface, Fsize, white, Lthick, Ltype)

        # 오른쪽 글자
        cv2.putText(show_frame, f'{dtR}', (Rtxt_x, txt_y + y_mr), Fface, Fsize, black, Lthick + 10, Ltype)
        cv2.putText(show_frame, f'{dtR}', (Rtxt_x, txt_y + y_mr), Fface, Fsize, white, Lthick, Ltype)

    else:
        # 왼쪽 글자
        cv2.putText(show_frame, '0', (Ltxt_x, txt_y + y_mr), Fface, Fsize, black, Lthick + 10, Ltype)
        cv2.putText(show_frame, '0', (Ltxt_x, txt_y + y_mr), Fface, Fsize, white, Lthick, Ltype)

        # 오른쪽 글자
        cv2.putText(show_frame, '0', (Rtxt_x, txt_y + y_mr), Fface, Fsize, black, Lthick + 10, Ltype)
        cv2.putText(show_frame, '0', (Rtxt_x, txt_y + y_mr), Fface, Fsize, white, Lthick, Ltype)

    frametime = (datetime.now() - begin).total_seconds()
    print(f"Duration {frametime:.4f}s")
    if ret:
        cv2.imshow('curve',show_frame)
        # cv2.imshow('dst',dst2_gray)

        # print(max(c_list))

        # out.write(show_frame)

        key = cv2.waitKeyEx(10)

        if key == ord('q'):
            break
        elif key == ord('d'):
            speed *= 2
            if speed == 16:
                speed = 16
                print('max speed')
        elif key == ord('a'):
            speed /= 2
            if speed == 0.25:
                speed = 0.25
                print('min speed')
        # time.sleep(1./fps/speed)

    else:
        break

Vid.release()
cv2.destroyAllWindows()

See also