File size: 11,359 Bytes
30420b9 c884cd4 3aa5dc8 c884cd4 3aa5dc8 c884cd4 3aa5dc8 e9493dd 3aa5dc8 c884cd4 30420b9 3aa5dc8 30420b9 3aa5dc8 30420b9 3aa5dc8 30420b9 c884cd4 30420b9 e9493dd 30420b9 3aa5dc8 30420b9 c884cd4 30420b9 3aa5dc8 30420b9 c884cd4 30420b9 e9493dd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | import cv2
import fitz
import numpy as np
import os
import pandas as pd
import pytesseract
import warnings
def pdf2png(folderpath):
doc = fitz.open(folderpath + '/opinion.pdf')
zoom = 1
mat = fitz.Matrix(zoom, zoom)
for (i, p) in enumerate(doc):
pix = p.get_pixmap(matrix=mat)
pix.save(folderpath + '/' + str(i) + '.png')
def is_leftmost(image, x, y_top, y_bot):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (7,7), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
left_portion = thresh[int((y_top+y_bot)/2), :x]
return np.sum(left_portion) == 0
def get_indents(filename, body_bbox, page):
indented_lines = []
image = cv2.imread(filename)
body_rect = fitz.Rect(body_bbox)
pg_dict = page.get_text('dict', clip=body_rect)
all_lines = [(int(line['bbox'][0]), int(line['bbox'][1]), int(line['bbox'][2]), int(line['bbox'][3]), line)for block in pg_dict['blocks'] for line in block['lines']]
body_text = page.get_text("text", clip=body_rect).strip()
baseline = min([l[0] for l in all_lines])
indented_inds = [i for (i,l) in enumerate(all_lines) if (l[0]-baseline > 9 and is_leftmost(image, l[0]-12, l[1], l[3]))]# and l[0]-baseline < 30
for i in indented_inds:
indented_lines.append((i, all_lines[i][0], all_lines[i][1], all_lines[i][2], all_lines[i][3]))
return indented_lines
def get_footnote_bbox(filename):
footnotes_bbox = (None, None, None, None)
x1p, y1p, x2p, y2p = get_page_bbox(filename)
x1h, y1h, x2h, y2h = get_header_bbox(filename)
image = cv2.imread(filename)
im_h, im_w, im_d = image.shape
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (20, 1))
dilate = cv2.dilate(thresh, kernel, iterations=1)
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=lambda x: cv2.boundingRect(x)[1])
for (i, c) in enumerate(cnts):
x, y, w, h = cv2.boundingRect(c)
if h < 7 and w > 50 and y > y1p and x - x1p < 30:
footnotes_bbox = (x, y, x2p, y2p)
return footnotes_bbox
def get_header_bbox(filename):
image = cv2.imread(filename)
im_h, im_w, im_d = image.shape
base_image = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (9,9), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (200,10))
dilate = cv2.dilate(thresh, kernel, iterations=1)
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=lambda x: cv2.boundingRect(x)[1])
for (i,c) in enumerate(cnts):
x,y,w,h = cv2.boundingRect(c)
break
header_bbox = (x, y, x+w, y+40)
return header_bbox
def get_page_bbox(filename):
image = cv2.imread(filename)
im_h, im_w, im_d = image.shape
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (7, 7), 0)
thresh = cv2.threshold(blur, 240, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50, 10))
dilate = cv2.dilate(thresh, kernel, iterations=1)
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=lambda x: cv2.boundingRect(x)[1])
header_bbox = get_header_bbox(filename)
all_x1 = [cv2.boundingRect(c)[0] for c in cnts]
all_y1 = [cv2.boundingRect(c)[1] for c in cnts]
all_x2 = [cv2.boundingRect(c)[0] + cv2.boundingRect(c)[2] for c in cnts]
all_y2 = [cv2.boundingRect(c)[1] + cv2.boundingRect(c)[3] for c in cnts]
return min(all_x1), header_bbox[1], max(all_x2), max(all_y2)
def get_case_separator(filename):
new_case_line = (None, None, None, None)
x1p, y1p, x2p, y2p = get_page_bbox(filename)
x1h, y1h, x2h, y2h = get_header_bbox(filename)
image = cv2.imread(filename)
im_h, im_w, im_d = image.shape
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (7, 7), 0)
thresh = cv2.threshold(blur, 240, 255, cv2.THRESH_BINARY_INV)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (20, 1))
dilate = cv2.dilate(thresh, kernel, iterations=1)
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=lambda x: cv2.boundingRect(x)[1])
for (i, c) in enumerate(cnts):
x, y, w, h = cv2.boundingRect(c)
x_center = (x1p + x2p) / 2
if h < 8 and w > 70 and ((x - x1p) < x_center and (x - x1p) > 0.3 * x_center) and (y > y1p and y > y1h): #
new_case_line = (x1p, y, x2p, y)
break
return new_case_line
def get_page_elements(filename, page):
page_bbox = get_page_bbox(filename)
header_bbox = get_header_bbox(filename)
fn_bbox = get_footnote_bbox(filename)
case_separator_bbox = get_case_separator(filename)
if fn_bbox[0] is not None:
body_bbox = (page_bbox[0], header_bbox[3], page_bbox[2], fn_bbox[1])
else:
body_bbox = (page_bbox[0], header_bbox[3], page_bbox[2], page_bbox[3])
indent_lines = get_indents(filename, body_bbox, page)
image = cv2.imread(filename)
cv2.rectangle(image, (page_bbox[0], page_bbox[1]), (page_bbox[2], page_bbox[3]), (0, 0, 0), 4)
cv2.rectangle(image, (header_bbox[0], header_bbox[1]), (header_bbox[2], header_bbox[3]), (0, 255, 0), 2)
cv2.rectangle(image, (body_bbox[0], body_bbox[1]), (body_bbox[2], body_bbox[3]), (255, 0, 0), 2)
if fn_bbox[0] is not None:
cv2.rectangle(image, (fn_bbox[0], fn_bbox[1]), (fn_bbox[2], fn_bbox[3]), (0, 0, 255), 2)
if case_separator_bbox[0] is not None:
cv2.rectangle(image, (case_separator_bbox[0], case_separator_bbox[1]),
(case_separator_bbox[2], case_separator_bbox[3]), (255, 0, 255), 2)
for (i, il) in enumerate(indent_lines):
cv2.circle(image, (il[1]-15, int(0.5*(il[2] + il[4]))), radius=1, color=(240, 32, 160), thickness=2)
return page_bbox, header_bbox, fn_bbox, body_bbox, case_separator_bbox, indent_lines, image
def paragraphs(folderpath):
doc = fitz.open(folderpath + '/opinion.pdf')
df = pd.read_csv(folderpath + '/data.csv')
df = df.replace({np.nan: None})
nl_inds = df['Indent Lines'].tolist()
nl_inds = [eval(nli) for nli in nl_inds]
nl_indents = [nli[1] for page_nli in nl_inds for nli in page_nli]
nl_inds = [(i, nli[0]) for (i, page_nli) in zip(df['Pg Ind'].tolist(), nl_inds) for nli in page_nli]
paras = [([], 0, 0, 0)] # Text, indent amount, start pg ind, end pg ind
para_lines = []
for (i, page) in enumerate(doc):
ind = df.index[df['Pg Ind'] == i].tolist()[0]
body_bbox = [df.iloc[ind]['Body X1'], df.iloc[ind]['Body Y1'], df.iloc[ind]['Body X2'], df.iloc[ind]['Body Y2']]
case_separator = df.iloc[ind]['Case Separator Y']
if case_separator is not None:
body_bbox[-1] = case_separator
body_rect = fitz.Rect(body_bbox)
pg_dict = page.get_text('dict', clip=body_rect)
all_lines = [get_line_text(line) for block in pg_dict['blocks'] for line in block['lines']]
for (j, line) in enumerate(all_lines):
if line == "":
continue
if (i, j) in nl_inds:
indent_amt = nl_indents[nl_inds.index((i, j))] # This is for the starting one
paras.append(([], indent_amt, i, i))
paras[-1] = list(paras[-1])
paras[-1][0].append(line.strip())
paras[-1][-1] = i # Update the page ind
paras[-1] = tuple(paras[-1])
paras = block_quotes(paras)
paras_df = pd.DataFrame(data=paras, index=None, columns=['Text', 'Indent Amount', 'Start Pg Ind', 'End Pg Ind'])
return paras_df
def get_line_text(line):
words = []
for s in line['spans']:
text = s['text'].strip()
if text != "":
words.append(text)
words = " ".join(words)
return words
def block_quotes(paras):
modified_paras = []
start_para, end_para, end_quote_passed = None, None, None
for (i, (para, ind_amt, start_pg_ind, end_pg_ind)) in enumerate(paras):
if i == len(paras) - 1:
break
if len(para) == 1 and "“" == para[0][0] and start_para is None:
start_para = i
if len(para) == 1 and "”" == para[0][-1] and start_para is not None:
end_quote_passed = True
if len(para) == 1 and (
paras[i + 1][1] - ind_amt) < -5 and end_para is None and start_para is not None and end_quote_passed:
end_para = i
if start_para is not None and end_para is not None:
para = [p[0][0] for p in paras[start_para:end_para + 1]]
start_para, end_para, end_quote_passed = None, None, False
if start_para is None and end_para is None:
modified_paras.append((para, ind_amt, start_pg_ind, end_pg_ind))
return modified_paras
def process_file(folderpath):
pdf2png(folderpath)
doc = fitz.open(folderpath + '/opinion.pdf')
files = [f for f in os.listdir(folderpath) if '.png' in f.lower() and "processed" not in f.lower()]
data = {'Pg Ind':[],
'Header X1':[], 'Header Y1': [], 'Header X2': [], 'Header Y2':[],
'Body X1':[], 'Body Y1': [], 'Body X2': [], 'Body Y2':[],
'Footer X1':[], 'Footer Y1': [], 'Footer X2': [], 'Footer Y2':[],
'Page X1':[], 'Page Y1': [], 'Page X2': [], 'Page Y2':[],
'Case Separator Y': [],
'Indent Lines': [],
}
data_df = pd.DataFrame(data)
for (i,f) in enumerate(files):
ind = int(f.split('.png')[0])
page = doc[ind]
page_bbox, header_bbox, fn_bbox, body_bbox, case_separator_bbox, indent_lines, image = get_page_elements(folderpath +'/' + f, page)
row = {'Pg Ind':[ind],
'Header X1':[header_bbox[0]], 'Header Y1': [header_bbox[1]], 'Header X2': [header_bbox[2]], 'Header Y2':[header_bbox[3]],
'Body X1':[body_bbox[0]], 'Body Y1': [body_bbox[1]], 'Body X2': [body_bbox[2]], 'Body Y2':[body_bbox[3]],
'Footer X1':[fn_bbox[0]], 'Footer Y1': [fn_bbox[1]], 'Footer X2': [fn_bbox[2]], 'Footer Y2':[fn_bbox[3]],
'Page X1':[page_bbox[0]], 'Page Y1': [page_bbox[1]], 'Page X2': [page_bbox[2]], 'Page Y2':[page_bbox[3]],
'Case Separator Y': [case_separator_bbox[1]],
'Indent Lines': [indent_lines]
}
row_df = pd.DataFrame(row)
data_df = pd.concat([data_df, row_df], ignore_index=True)
cv2.imwrite(folderpath + '/' + str(ind) + '-processed.png', image)
data_df['Pg Ind'] = data_df['Pg Ind'].astype('int')
data_df.to_csv(folderpath +'/data.csv', index=False)
paras_df = paragraphs(folderpath)
paras_df.to_csv(folderpath + '/paragraphs.csv', index=False)
process_file('PDF Cases/19-896_2135') |