728x90

2021/02/24 - [기록 note] - 2021-02-24(OpenCV_6)

### threshold

import sys
import numpy as np
import cv2


src = cv2.imread('cells.png', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

# 임계값이 왜 리턴 될까? 자동으로 임계값을 결정하는 알고리즘에서 필요하게된다
#수동으로 임계값 결정할땐 리턴되는 임계값을 받을 필요가 없다
#dst1은 모든 세포를 검출
_,dst1= cv2.threshold(src, 210,255, cv2.THRESH_BINARY)

#dst2는 세포중에서 염색된 것만 검출
_,dst2= cv2.threshold(src, 100,255, cv2.THRESH_BINARY)

cv2.imshow('src', src)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()
cv2.destroyAllWindows()

 

### threshold2

import sys
import numpy as np
import cv2


src = cv2.imread('cells.png', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()


def on_threshold(pos):
    _, dst = cv2.threshold(src, pos, 255, cv2.THRESH_BINARY)
    cv2.imshow('dst', dst)


cv2.imshow('src', src)
cv2.namedWindow('dst') #창을 미리 하나 만들어야 트랙바가 만들어진다
cv2.createTrackbar('Threshold', 'dst', 0, 255, on_threshold)
cv2.setTrackbarPos('Threshold', 'dst', 128)

cv2.waitKey()
cv2.destroyAllWindows()

 

### ostu

import sys
import numpy as np
import cv2


src = cv2.imread('rice.png', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

#128대신 아무 숫자 넣어도 이 값으로 계산 안됨, 자동으로 결정된다
#강의에서는 대표 숫자 0을 입력함
#th는 실수형으로 리턴됨
#cv2.THRESH_BINARY | 없어도 똑같이 진행된다
th,dst=cv2.threshold(src, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

print("otsu's threshold:", th)  # 131

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

### local_threshold

import sys
import numpy as np
import cv2


# 입력 영상 불러오기
src = cv2.imread('rice.png', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

# 전역 이진화 by Otsu's method
_, dst1 = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

# 지역 이진화 by Otsu's method
dst2 = np.zeros(src.shape, np.uint8)

bw = src.shape[1] // 4 #block width , 128
bh = src.shape[0] // 4 #block height, 128

for y in range(4): #세로 4등분
    for x in range(4): #가로 4등분

        #copy가 아니기 때문에 dst_를 변경하면 dst2도 변경됨을 인지해야한다
        # print(y*bh,'부터',(y+1)*bh, x*bw,'부터',(x+1)*bw)

        #y=0일때  y는 0:128 고정이고, x방향으로 0:128, 128:256, 256:384, 384:512
        #y=1일때  y는 128:256 고정이고, x방향으로 0:128, 128:256, 256:384, 384:512
        src_ = src[y*bh:(y+1)*bh, x*bw:(x+1)*bw]
        dst_ = dst2[y*bh:(y+1)*bh, x*bw:(x+1)*bw]

        #dst_를 인자로써 입력은 dst_를 입력이자 출력으로 사용한다는 것
        #만일 인자로 주지 않고 _,dst_= cv2.thresh~~ 로 작성하면
        #새로운 정보로 채워진 dst_는 기존에 받았던 부분영상 정보가 없어진다
        #src_,dst_ 크기가 같아야한다. 다르면 dst_가 또 정보를 잃게 되어서(dst2와 같은 정보를 공유하는 것을 못하게됨) 아예 새로운 형태를 만든다
        cv2.threshold(src_, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU, dst_)

# 결과 출력
cv2.imshow('src', src)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()
cv2.destroyAllWindows()

 

### adaptive_threshold

import sys
import numpy as np
import cv2


src = cv2.imread('sudoku.jpg', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()


def on_trackbar(pos):
    bsize = pos
    if bsize % 2 == 0: #짝수이면 하나를 빼서 홀수로 만듦(kernel은 반드시 홀수)
        bsize = bsize - 1
    if bsize < 3:
        bsize = 3

    dst = cv2.adaptiveThreshold(src, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                cv2.THRESH_BINARY, bsize, 5)

    cv2.imshow('dst', dst)


cv2.imshow('src', src)
cv2.namedWindow('dst')
cv2.createTrackbar('Block Size', 'dst', 0, 200, on_trackbar)
cv2.setTrackbarPos('Block Size', 'dst', 11)

cv2.waitKey()
cv2.destroyAllWindows()

 

### 모폴로지

import sys
import numpy as np
import cv2


src = cv2.imread('circuit.bmp', cv2.IMREAD_GRAYSCALE) #0또는 255인 기판 이미지

if src is None:
    print('Image load failed!')
    sys.exit()

# 3행 5열짜리의 행렬의 structure element
se = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 3))
dst1 = cv2.erode(src, se)

# 3행 3열의 의미는 상하좌우 방향으로 1픽셀을 팽창한다는 의미
dst2 = cv2.dilate(src, None)

cv2.imshow('src', src)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()
cv2.destroyAllWindows()

 

### 지역이진화+모폴로지

import sys
import numpy as np
import cv2


src = cv2.imread('rice.png', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

# src 영상에 지역 이진화 수행 (local_th.py 참고)
dst1 = np.zeros(src.shape, np.uint8)

bw = src.shape[1] // 4
bh = src.shape[0] // 4

for y in range(4):
    for x in range(4):
        src_ = src[y*bh:(y+1)*bh, x*bw:(x+1)*bw]
        dst_ = dst1[y*bh:(y+1)*bh, x*bw:(x+1)*bw]
        cv2.threshold(src_, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU, dst_)

# connectedComponents: 흰색 덩어리가 몇개가 있는지 알려준다
# 모폴로지 하기전(잡음 제거전)
cnt1, _ = cv2.connectedComponents(dst1)
print('cnt1:', cnt1)

#침식과 팽창을 통해서 노이즈 제거 후 갯수 파악
# dst2 = cv2.morphologyEx(dst1, cv2.MORPH_OPEN, None)
dst2 = cv2.erode(dst1, None) #위에서 지역 이진화 진행한 dst1
dst2 = cv2.dilate(dst2, None)

cnt2, _ = cv2.connectedComponents(dst2)
# 모폴로지 시행 후(잡음 제거 후)
print('cnt2:', cnt2)

cv2.imshow('src', src)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()
cv2.destroyAllWindows()

 

### labeling

import sys
import numpy as np
import cv2


src = cv2.imread('keyboard.bmp', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

#1.먼저 이진화 작업
_, src_bin = cv2.threshold(src, 0,255, cv2.THRESH_OTSU)

#2.이진화 된 영상에서 레이블링을 작업
cnt, labels, stats, centroids=cv2.connectedComponentsWithStats(src_bin)

#stats나 centroids 정보를 통해서 각 객체가 어느위치에 어떻게 놓여 있는지 알수있다
#stats로 각각 객체의 바운딩 박스를 알수 있다 -> 각 객체를 구분 영상으로 짤라 낼수도있다

#3.각각의 객체 정보에 접근하기위해서 for문
dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)
for i in range(1,cnt): #1부터 시작한 이유: 배경 0 은 제외한다는 의미
    x,y,w,h,a=stats[i]


    '''
    dst 영상의 빨간색 점들은 원본의 있는 노이즈
    제거하기 위해서는 opening(침식&팽창)도 있지만 
    아래 처럼 간단하게 코드 작성한다
    '''
    #검출한 면적 수가 20보다 작으면 무시
    if a <20:
        continue
    
    #빨간 바운딩 박스
    cv2.rectangle(dst, (x,y,w,h),(0,0,255),2)


cv2.imshow('src', src)
cv2.imshow('src_bin', src_bin)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

### 외곽선검출

import sys
import random
import numpy as np
import cv2


src = cv2.imread('contours.bmp', cv2.IMREAD_GRAYSCALE) #이진영상 load

if src is None:
    print('Image load failed!')
    sys.exit()

#1.외곽선 검출(좌표구하기)
#RETR_CCOMP: 2level(계층:2)          
#CHAIN_APPROX_NONE: 모든 좌표 정보 저장             
contours, hier = cv2.findContours(src, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)

dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)

idx = 0 #첫번째 사용할 외곽선 인덱스 0번
while idx >= 0:
    c = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
    
    #검출한 외곽선좌표를 이용한 외곽선 그리기
    #hier인자를 주면 홀 외곽선도 취급이 가능하다
    #hier인자가 없다면 바깥의 외곽선만 취급한다.
    #contours: 외곽선 좌표정보
    cv2.drawContours(dst, contours, idx, c, 2, cv2.LINE_8, hier)
    
    # hier의 shape가 (1,N,4)형태를 가진다
    #[0, idx, 0], 무조건 첫번째는 0을 주면되고 N자리에 index번호를 준다.
    #N은 외곽선의 갯수이다.
    #맨 마지막 자리에 0부터3사이에 값을 줄수 있는데 0을 주면 계층에서 next로 넘어간다
    #index번호에 따라서 next로 이동을 한다는 의미
    idx = hier[0, idx, 0]

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

    

 

### 외곽선검출2

import sys
import random
import numpy as np
import cv2


src = cv2.imread('milkdrop.bmp', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

_, src_bin = cv2.threshold(src, 0, 255, cv2.THRESH_OTSU)

#RETR_LIST: 모든 외곽선을 검출(리스트로 표현)
#hier 인자는 받지 않았음
contours, _ = cv2.findContours(src_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)

h, w = src.shape[:2]
dst = np.zeros((h, w, 3), np.uint8)

#contours는 리스트이기 때문에 contours의 길이는 객체의 갯수와 같다
for i in range(len(contours)):
    c = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
    cv2.drawContours(dst, contours, i, c, 1, cv2.LINE_AA)

cv2.imshow('src', src)
cv2.imshow('src_bin', src_bin)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

import math
import cv2


def setLabel(img, pts, label):
    (x, y, w, h) = cv2.boundingRect(pts) #외곽선을 감싸는 가장 작은 크기 사각형 정보출력
    pt1 = (x, y)
    pt2 = (x + w, y + h) #(가로길이,세로길이)
    cv2.rectangle(img, pt1, pt2, (0, 0, 255), 1)
    cv2.putText(img, label, pt1, cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 255))


def main():
    img = cv2.imread('polygon.bmp', cv2.IMREAD_COLOR)

    if img is None:
        print('Image load failed!')
        return

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    #이진 영상만들기
    #이때 THRESH_BINARY_INV사용해서 반전을 시킨다
    #본 이미지의 도형은 어두운 컬러이고 오히려 바탕이 회색에 가까운 밝은 픽셀이기 때문에 반전 시켜야한다
    _, img_bin = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
    
    #findContours: 각각의 객체를 찾는다
    #RETR_EXTERNAL: 바깥쪽 외곽선만 검출
    contours, _ = cv2.findContours(img_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

    #contours는 ndarray를 원소로 갖고 있는 리스트이다
    #for문을 돌리면 pts가 객체 하나하나의 외곽선 객체를 받아 올수 있게된다
    for pts in contours:

        #노이즈를 제거하기 위한 코드
        #하나하나의 객체의 면적이 400보다 작으면 무시
        #400: 가로세로 20씩 
        if cv2.contourArea(pts) < 400:  #  너무 작으면 무시
            continue

        #근사화를 진행서 단순화 시킨다    
        #근사화된 좌표로 리턴된다
        #만일 좌표10개로 표현된 도형이 마진을 통해 근사화를 거치면 적은 좌표로 표현이 가능
        approx = cv2.approxPolyDP(pts, cv2.arcLength(pts, True)*0.02, True)

        vtc = len(approx)

        #좌표갯수가 3도 아니고 4도 아니면 원 모양인지를 계산하는 코드
        if vtc == 3:
            setLabel(img, pts, 'TRI')
        elif vtc == 4:
            setLabel(img, pts, 'RECT')
        else:
            length = cv2.arcLength(pts, True)
            area = cv2.contourArea(pts)
            ratio = 4. * math.pi * area / (length * length)

            if ratio > 0.85:
                setLabel(img, pts, 'CIR')

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


if __name__ == '__main__':
    main()

 

"""
Tesseract-ocr 설치하기

1. tesseract-ocr-w64-setup-v5.0.0-alpha.20200328 파일 다운로드 
   (https://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-w64-setup-v5.0.0-alpha.20200328.exe)
2. 설치 시 "Additional script data" 항목에서 "Hangul Script", "Hangul vertical script" 항목 체크,
   "Additional language data" 항목에서 "Korean" 항목 체크.
4. 설치 후 시스템 환경변수 PATH에 Tesseract 설치 폴더 추가
   (e.g.) c:\Program Files\Tesseract-OCR
4. 설치 후 시스템 환경변수에 TESSDATA_PREFIX를 추가하고, 변수 값을 <Tesseract-DIR>\tessdata 로 설정
5. <Tesseract-DIR>\tessdata\script\ 폴더에 있는 Hangul.traineddata, Hangul_vert.traineddata 파일을
   <Tesseract-DIR>\tessdata\ 폴더로 복사
6. 명령 프롬프트 창에서 pip install pytesseract 명령 입력
"""

import sys
import random
import numpy as np
import cv2
import pytesseract


def reorderPts(pts):
    idx = np.lexsort((pts[:, 1], pts[:, 0]))  # 칼럼0 -> 칼럼1 순으로 정렬한 인덱스를 반환
    pts = pts[idx]  # x좌표로 정렬

    if pts[0, 1] > pts[1, 1]: #x좌표로 구분된 두 그룹중 왼쪽그룹에서 y좌표가 더 작은 좌표를 0번인덱스준다
        pts[[0, 1]] = pts[[1, 0]] #두개의 점을 스와핑하는 코드 

    if pts[2, 1] < pts[3, 1]: #x좌표로 구분된 두 그룹중 오른쪽그룹에서 y좌표가 큰게 인덱스2, 나머지 우측상단이 3번
        pts[[2, 3]] = pts[[3, 2]]

    return pts #좌측상단부터 반시계방향으로 돌아가는 pts


# 영상 불러오기
filename = 'namecard1.jpg'
if len(sys.argv) > 1:
    filename = sys.argv[1]

src = cv2.imread(filename)

if src is None:
    print('Image load failed!')
    sys.exit()

# 출력 영상 설정
dw, dh = 720, 400 #명함을 편 상태의 영상크기를 미리 설정

#입력영상의 네개의 모서리를 넣을 자리를 미리 만듦
srcQuad = np.array([[0, 0], [0, 0], [0, 0], [0, 0]], np.float32)

#dw, dh를 이용해서 좌측상단부터 반시계 방향으로 돈다
dstQuad = np.array([[0, 0], [0, dh], [dw, dh], [dw, 0]], np.float32)
dst = np.zeros((dh, dw), np.uint8)

# 입력 영상 전처리_이진화
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
_, src_bin = cv2.threshold(src_gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

# 외곽선 검출 및 명함 검출
contours, _ = cv2.findContours(src_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

cpy = src.copy()
for pts in contours:
    # 너무 작은 객체는 무시
    if cv2.contourArea(pts) < 1000:
        continue

    # 외곽선 근사화
    # approx: 점 네개로 근사화가 된다
    approx = cv2.approxPolyDP(pts, cv2.arcLength(pts, True)*0.02, True)

    # 컨벡스가 아니고, 사각형이 아니면 무시
    if not cv2.isContourConvex(approx) or len(approx) != 4:
        continue

    #polylines통해 외곽선을 그린다    
    cv2.polylines(cpy, [approx], True, (0, 255, 0), 2, cv2.LINE_AA)

    #reorderPts: 각 점의 순서를 수동으로 정해준다
    #approx 점들을 분석해서 좌측상단부터 반시계방향으로 reorder => srcQuad로 넘긴다
    srcQuad = reorderPts(approx.reshape(4, 2).astype(np.float32))

pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)

#똑바로 펴는 작업
dst = cv2.warpPerspective(src, pers, (dw, dh))

dst_gray = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)
print(pytesseract.image_to_string(dst_gray, lang='Hangul+eng'))

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

728x90

+ Recent posts