Detect Vertical&Horizontal Segments By OpenCV,and Save the data to csv.
Steps:
- Using adaptiveThreshold to generate thresholded image.
- Using threshold to find lines.
- Save the data to csv by convert it to json.
1 # coding=gbk 2 import cv2 3 import numpy as np 4 import json 5 import csv 6 import os 7 8 def find_lines(threshold, regions=None, direction='horizontal', 9 line_scale=15, iterations=0): 10 """Finds horizontal and vertical lines by applying morphological 11 transformations on an image. 12 13 Parameters 14 ---------- 15 threshold : object 16 numpy.ndarray representing the thresholded image. 17 regions : list, optional (default: None) 18 List of page regions that may contain tables of the form x1,y1,x2,y2 19 where (x1, y1) -> left-top and (x2, y2) -> right-bottom 20 in image coordinate space. 21 direction : string, optional (default: 'horizontal') 22 Specifies whether to find vertical or horizontal lines. 23 line_scale : int, optional (default: 15) 24 Factor by which the page dimensions will be divided to get 25 smallest length of lines that should be detected. 26 27 The larger this value, smaller the detected lines. Making it 28 too large will lead to text being detected as lines. 29 iterations : int, optional (default: 0) 30 Number of times for erosion/dilation is applied. 31 32 For more information, refer `OpenCV's dilate <https://docs.opencv.org/2.4/modules/imgproc/doc/filtering.html#dilate>`_. 33 34 Returns 35 ------- 36 dmask : object 37 numpy.ndarray representing pixels where vertical/horizontal 38 lines lie. 39 lines : list 40 List of tuples representing vertical/horizontal lines with 41 coordinates relative to a left-top origin in 42 image coordinate space. 43 44 """ 45 lines = [] 46 47 if direction == 'vertical': 48 size = threshold.shape[0] // line_scale 49 el = cv2.getStructuringElement(cv2.MORPH_RECT, (1, size)) 50 elif direction == 'horizontal': 51 size = threshold.shape[1] // line_scale 52 el = cv2.getStructuringElement(cv2.MORPH_RECT, (size, 1)) 53 elif direction is None: 54 raise ValueError("Specify direction as either 'vertical' or" 55 " 'horizontal'") 56 57 if regions is not None: 58 region_mask = np.zeros(threshold.shape) 59 for region in regions: 60 x, y, w, h = region 61 region_mask[y : y + h, x : x + w] = 1 62 threshold = np.multiply(threshold, region_mask) 63 64 threshold = cv2.erode(threshold, el) 65 threshold = cv2.dilate(threshold, el) 66 dmask = cv2.dilate(threshold, el, iterations=iterations) 67 68 try: 69 _, contours, _ = cv2.findContours( 70 threshold.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 71 except ValueError: 72 # for opencv backward compatibility 73 contours, _ = cv2.findContours( 74 threshold.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 75 76 for c in contours: 77 x, y, w, h = cv2.boundingRect(c) 78 x1, x2 = x, x + w 79 y1, y2 = y, y + h 80 if direction == 'vertical': 81 lines.append(((x1 + x2) // 2, y2, (x1 + x2) // 2, y1)) 82 elif direction == 'horizontal': 83 lines.append((x1, (y1 + y2) // 2, x2, (y1 + y2) // 2)) 84 85 return dmask, lines 86 87 def adaptive_threshold(imagename, process_background=False, blocksize=15, c=-2): 88 """Thresholds an image using OpenCV's adaptiveThreshold. 89 90 Parameters 91 ---------- 92 imagename : string 93 Path to image file. 94 process_background : bool, optional (default: False) 95 Whether or not to process lines that are in background. 96 blocksize : int, optional (default: 15) 97 Size of a pixel neighborhood that is used to calculate a 98 threshold value for the pixel: 3, 5, 7, and so on. 99 100 For more information, refer `OpenCV's adaptiveThreshold <https://docs.opencv.org/2.4/modules/imgproc/doc/miscellaneous_transformations.html#adaptivethreshold>`_. 101 c : int, optional (default: -2) 102 Constant subtracted from the mean or weighted mean. 103 Normally, it is positive but may be zero or negative as well. 104 105 For more information, refer `OpenCV's adaptiveThreshold <https://docs.opencv.org/2.4/modules/imgproc/doc/miscellaneous_transformations.html#adaptivethreshold>`_. 106 107 Returns 108 ------- 109 img : object 110 numpy.ndarray representing the original image. 111 threshold : object 112 numpy.ndarray representing the thresholded image. 113 114 """ 115 img = cv2.imread(imagename) 116 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 117 118 if process_background: 119 threshold = cv2.adaptiveThreshold( 120 gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 121 cv2.THRESH_BINARY, blocksize, c) 122 else: 123 threshold = cv2.adaptiveThreshold( 124 np.invert(gray), 255, 125 cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, blocksize, c) 126 return img, threshold 127 128 count = 0 129 root = 'E:/VGID_Text/Mycode/linelabel/PDF_JPG/' 130 rows=[] 131 for root, dirs, files in os.walk(root): 132 for img in files: 133 if img.endswith('jpg'): 134 img_path = root+'/'+img 135 image, threshold = adaptive_threshold(img_path) 136 find_lines(threshold) 137 vertical_mask, vertical_segments = find_lines(threshold, direction='vertical') 138 horizontal_mask, horizontal_segments = find_lines(threshold, direction='horizontal') 139 lines_list = vertical_segments + horizontal_segments 140 objects = [] 141 lines_dict = {"objects":objects} 142 for line in lines_list: 143 point1 = {"x": line[0], "y": line[1]} 144 point2 = {"x": line[2], "y": line[3]} 145 ptList = [point1,point2] 146 polygon = {"ptList":ptList} 147 line_dict ={"polygon":polygon, 148 "name": "line", 149 "type": 4, 150 "color": "#aa40bf", 151 "id": "6173cf75-ea09-4ff4-a75e-5cc99a5ea40e", 152 "cur": 0, 153 "lineStyle": "solid" 154 } 155 objects.append(line_dict) 156 lines_json = json.dumps(lines_dict) 157 print(count, lines_json) 158 row = [img_path, lines_json] 159 rows.append(row) 160 count = count + 1 161 with open('E:/线条标注3k+.csv', 'w', newline='') as csv_file: 162 csv_writer = csv.writer(csv_file) 163 csv_writer.writerow(['image_path','lines']) 164 for row in rows: 165 csv_writer.writerow(row)
Reference:Camelot:https://camelot-py.readthedocs.io/en/master/