ABOUT ME

Today
Yesterday
Total
  • 찌그러진 도표 바로펴기
    카테고리 없음 2024. 12. 30. 16:29
    import sys
    import numpy as np
    import cv2
    import os
    
    def detect_largest_rectangle(image):
        # 그레이스케일 변환
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        
        # 노이즈 제거를 위한 블러 처리
        blurred = cv2.GaussianBlur(gray, (5, 5), 0)
        
        # 이미지 이진화 - 적응형 임계값 적용
        thresh = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 
                                     cv2.THRESH_BINARY_INV, 11, 2)
        
        # 모폴로지 연산으로 노이즈 제거
        kernel = np.ones((3,3), np.uint8)
        thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
        
        # 윤곽선 검출 - 계층 구조 포함
        contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, 
                                             cv2.CHAIN_APPROX_SIMPLE)
        
        # 면적이 일정 크기 이상인 윤곽선만 선택
        min_area = image.shape[0] * image.shape[1] * 0.3  # 전체 이미지 크기의 30%
        valid_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > min_area]
        
        if not valid_contours:
            return None
        
        # 가장 큰 윤곽선 찾기
        largest_contour = max(valid_contours, key=cv2.contourArea)
        
        # 윤곽선을 근사화하여 사각형 꼭지점 찾기
        epsilon = 0.02 * cv2.arcLength(largest_contour, True)
        approx = cv2.approxPolyDP(largest_contour, epsilon, True)
        
        # 디버깅을 위한 출력
        print(f"검출된 꼭지점 수: {len(approx)}")
        
        # 사각형의 꼭지점이 4개인 경우에만 처리
        if len(approx) == 4:
            points = approx.reshape(4, 2).astype(np.float32)
            
            # 좌표 정렬
            rect = np.zeros((4, 2), dtype=np.float32)
            
            # x+y가 가장 작은 점이 좌상단
            s = points.sum(axis=1)
            rect[0] = points[np.argmin(s)]
            # x+y가 가장 큰 점이 우하단
            rect[2] = points[np.argmax(s)]
            
            # 나머지 두 점 중에서
            remaining_points = points[~np.isin(range(4), [np.argmin(s), np.argmax(s)])]
            # x 좌표가 작은 것이 좌하단
            rect[1] = remaining_points[np.argmin(remaining_points[:, 0])]
            # x 좌표가 큰 것이 우상단
            rect[3] = remaining_points[np.argmax(remaining_points[:, 0])]
            
            return rect
        return None
    
    # 디버깅을 위한 이미지 저장 함수 추가
    def save_debug_image(image, name, output_dir, base_name):
        debug_path = os.path.join(output_dir, f'{base_name}_{name}.jpg')
        cv2.imwrite(debug_path, image)
        print(f'Debug image saved as: {debug_path}')
    
    
    def drawROI(img, corners):
        cpy = img.copy()
        c1 = (192, 192, 255)
        c2 = (128, 128, 255)
    
        for pt in corners:
            cv2.circle(cpy, tuple(pt.astype(int)), 25, c1, -1, cv2.LINE_AA)
    
        cv2.line(cpy, tuple(corners[0].astype(int)), tuple(corners[1].astype(int)), c2, 2, cv2.LINE_AA)
        cv2.line(cpy, tuple(corners[1].astype(int)), tuple(corners[2].astype(int)), c2, 2, cv2.LINE_AA)
        cv2.line(cpy, tuple(corners[2].astype(int)), tuple(corners[3].astype(int)), c2, 2, cv2.LINE_AA)
        cv2.line(cpy, tuple(corners[3].astype(int)), tuple(corners[0].astype(int)), c2, 2, cv2.LINE_AA)
    
        disp = cv2.addWeighted(img, 0.3, cpy, 0.7, 0)
        return disp
    
    # 명령줄 인자 확인을 맨 위로 이동
    if len(sys.argv) != 2:
        print('Usage: python3 drawRectangle.py <image_path>')
        sys.exit()
    
    # 입력 이미지 불러오기
    image_path = sys.argv[1]
    src = cv2.imread(image_path)
    
    if src is None:
        print(f'Image open failed! Check if the file exists: {image_path}')
        sys.exit()
    
    output_dir = '/home/pi/'
    base_name = os.path.splitext(os.path.basename(image_path))[0]
    
    # 출력 디렉토리가 없는 경우 생성
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    
    # 디버깅용 이미지 처리
    gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    thresh = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 
                                 cv2.THRESH_BINARY_INV, 11, 2)
    kernel = np.ones((3,3), np.uint8)
    thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
    
    # 디버깅 이미지 저장
    save_debug_image(gray, 'gray', output_dir, base_name)
    save_debug_image(thresh, 'thresh', output_dir, base_name)
    
    # 입력 영상 크기 및 출력 영상 크기
    h, w = src.shape[:2]
    dw = 500
    dh = round(dw * 297 / 210)  # A4 용지 크기: 210x297cm
    
    # 가장 큰 사각형 검출
    srcQuad = detect_largest_rectangle(src)
    if srcQuad is None:
        print('Rectangle detection failed!')
        sys.exit()
    
    # 꼭지점 좌표 출력 추가
    print("\n꼭지점 좌표:")
    print(f"좌상단: ({int(srcQuad[0][0])}, {int(srcQuad[0][1])})")
    print(f"좌하단: ({int(srcQuad[1][0])}, {int(srcQuad[1][1])})")
    print(f"우하단: ({int(srcQuad[2][0])}, {int(srcQuad[2][1])})")
    print(f"우상단: ({int(srcQuad[3][0])}, {int(srcQuad[3][1])})")
    
    dstQuad = np.array([[0, 0], [0, dh-1], [dw-1, dh-1], [dw-1, 0]], np.float32)
    
    # 모서리점, 사각형 그리기
    disp = drawROI(src, srcQuad)
    
    # 결과 이미지 저장 경로를 '/home/pi/www/signature'로 변경
    output_dir = '/home/pi'
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    
    detected_path = os.path.join(output_dir, f'{base_name}_detected.jpg')
    transformed_path = os.path.join(output_dir, f'{base_name}_transformed.jpg')
    
    cv2.imwrite(detected_path, disp)
    
    # 투시 변환
    pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)
    dst = cv2.warpPerspective(src, pers, (dw, dh), flags=cv2.INTER_CUBIC)
    
    # 변환된 이미지 저장
    cv2.imwrite(transformed_path, dst)
    
    print(f'Detected rectangle saved as: {detected_path}')
    print(f'Transformed image saved as: {transformed_path}')

     

     

    import cv2
    import numpy as np
    import os
    
    def detect_paper_region(image):
        # Convert to grayscale
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        
        # Apply Gaussian blur to remove noise
        blurred = cv2.GaussianBlur(gray, (5, 5), 0)
        
        # Apply adaptive thresholding
        thresh = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 
                                       cv2.THRESH_BINARY_INV, 11, 2)
        
        # Remove noise using morphological operations
        kernel = np.ones((5,5), np.uint8)
        thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
        
        # Find contours
        contours, _ = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
        
        # Filter contours by area
        min_area = image.shape[0] * image.shape[1] * 0.05
        max_area = image.shape[0] * image.shape[1] * 0.9
        valid_contours = [cnt for cnt in contours if min_area < cv2.contourArea(cnt) < max_area]
        
        if not valid_contours:
            return None
        
        # Find the largest contour
        largest_contour = max(valid_contours, key=cv2.contourArea)
        
        # Approximate the contour to a polygon
        epsilon = 0.02 * cv2.arcLength(largest_contour, True)
        approx = cv2.approxPolyDP(largest_contour, epsilon, True)
        
        if len(approx) != 4:
            return None  # If the detected contour is not a quadrilateral
        
        return approx.reshape(4, 2)
    
    def crop_to_paper_region(image, corners):
        # Sort the coordinates
        rect = np.zeros((4, 2), dtype=np.float32)
        s = corners.sum(axis=1)
        rect[0] = corners[np.argmin(s)]
        rect[2] = corners[np.argmax(s)]
        
        diff = np.diff(corners, axis=1)
        rect[1] = corners[np.argmin(diff)]
        rect[3] = corners[np.argmax(diff)]
        
        # Define the destination points for perspective transform
        width = max(np.linalg.norm(rect[2] - rect[3]), np.linalg.norm(rect[1] - rect[0]))
        height = max(np.linalg.norm(rect[1] - rect[2]), np.linalg.norm(rect[0] - rect[3]))
        dst = np.array([
            [0, 0],
            [width - 1, 0],
            [width - 1, height - 1],
            [0, height - 1]], dtype="float32")
        
        # Get the perspective transform matrix and apply it
        M = cv2.getPerspectiveTransform(rect, dst)
        warped = cv2.warpPerspective(image, M, (int(width), int(height)))
        
        return warped
    
    def save_debug_image(image, name, output_dir, base_name):
        debug_path = os.path.join(output_dir, f'{base_name}_{name}.jpg')
        cv2.imwrite(debug_path, image)
        print(f'Debug image saved as: {debug_path}')
    
    def process_image(image_path, output_dir):
        if not os.path.exists(image_path):
            print(f"Error: {image_path} does not exist.")
            return
    
        image = cv2.imread(image_path)
        if image is None:
            print(f"Error: Cannot read {image_path}. Check the path.")
            return
    
        base_name = os.path.splitext(os.path.basename(image_path))[0]
        paper_corners = detect_paper_region(image)
        if paper_corners is None:
            print('Paper detection failed!')
            return
    
        print("\nDetected Paper Corners:")
        for i, corner in enumerate(paper_corners):
            print(f"Corner {i+1}: ({int(corner[0])}, {int(corner[1])})")
    
        cropped_image = crop_to_paper_region(image, paper_corners)
        save_debug_image(cropped_image, 'cropped', output_dir, base_name)
    
    # Example usage
    path = "/home/pi/templates/upload/"
    
    for file in os.listdir(path):
        print(f"\nProcessing file: {file}")
    
        file_path = os.path.join(path, file)
        output_dir = '/home/pi/templates/upload'
        process_image(file_path, output_dir)
Designed by Tistory.