-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathedge_kernel_single.py
203 lines (173 loc) · 7.73 KB
/
edge_kernel_single.py
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
# -*- coding: utf-8 -*-
import os
import cv2
import numpy as np
import pandas as pd
import scipy.stats as stat
import scipy.signal as signal
import scipy.ndimage as ndimage
import matplotlib.pyplot as plt
from argh import ArghParser, arg
def edge_dec(orig, mod):
'''See modified picture and original side-by-side'''
plt.clf()
plt.subplot(122),plt.imshow(mod, cmap = "gray")
plt.title('Modded Image'), plt.xticks([]), plt.yticks([])
plt.subplot(121),plt.imshow(orig, cmap = "gray")
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
def show_stats(mod, stats1, stats2):
'''Display the axes extracted from the picture on the picture'''
plt.clf()
plt.subplot(121), plt.imshow(mod, cmap = "gray")
plt.subplot(121), plt.plot(stats1)
plt.subplot(122), plt.imshow(np.rot90(mod), cmap = "gray")
plt.subplot(122), plt.plot(stats2)
def get_size(img):
"""Return the size of the image in pixels."""
ih, iw = img.shape[:2]
return iw * ih
def crop(img, boundaries):
"""Crop the image to the given boundaries."""
minx, miny, maxx, maxy = boundaries
return img[miny:maxy, minx:maxx]
def check_for_rect(mat, edge_coverage = 3, thresh = 1.98):
'''Checker function for controlling the presence of lines
with z-scores over 3.5 in quarters of edge detected images'''
dims = mat.shape
ax_vert = stat.zscore(np.std(mat, axis=1))
ax_vert_left = ax_vert[:int(dims[0]/edge_coverage)]
ax_vert_right = ax_vert[int(dims[0] * (edge_coverage - 1)/edge_coverage):]
ax_hori = stat.zscore(np.std(mat, axis=0))
ax_hori_upper = ax_hori[:int(dims[1]/edge_coverage)]
ax_hori_lower = ax_hori[int(dims[1] * (edge_coverage - 1)/edge_coverage):]
# check for in the corners and that
# the area is smaller than third of axis length
return (np.sum( ax_vert_left[ax_vert_left > thresh] ) / dims[0] > 0 and
np.sum( ax_vert_right[ax_vert_right > thresh] ) / dims[0] > 0 and
np.sum( ax_hori_upper[ax_hori_upper > thresh] ) / dims[1] > 0 and
np.sum( ax_hori_lower[ax_hori_lower > thresh] ) / dims[1] > 0 and
np.sum( ax_vert[ax_vert > thresh] ) / dims[0] < 0.33 and
np.sum( ax_hori[ax_hori > thresh] ) / dims[1] < 0.33)
def detect_rect(mat, minlineprop):
'''Detect lines from picture (mat) that are horizontal and rectangular
and minimum length of defined proprtion (minlineprop) of picture axis
length'''
dims = mat.shape
vertic_struct = cv2.getStructuringElement(cv2.MORPH_RECT,
(1, int(dims[0]*minlineprop)))
vertic = cv2.erode(mat, vertic_struct)
vertic = cv2.dilate(vertic, vertic_struct)
# detect horizontal lines
horiz_struct = cv2.getStructuringElement(cv2.MORPH_RECT,
(int(dims[1]*minlineprop), 1))
horiz = cv2.erode(mat, horiz_struct)
horiz = cv2.dilate(horiz, horiz_struct)
return vertic + horiz
def get_rect_bounds(mat, edge_coverage = 3, thresh = 1.98):
'''Get the minx, miny, maxx, maxy of detected rectangle on the picture,
from the area from the edge of edge_coverage'th of picture measured from the
edge'''
dims = mat.shape
ax_vert = stat.zscore(np.std(mat, axis=1))
ax_vert_left = ax_vert[:int(dims[0] / edge_coverage)][::-1]
ax_vert_right = ax_vert[int(dims[0] * ( edge_coverage-1 )/edge_coverage):]
ax_hori = stat.zscore(np.std(mat, axis=0))
ax_hori_upper = ax_hori[:int(dims[1] / edge_coverage)][::-1]
ax_hori_lower = ax_hori[int(dims[1] * ( edge_coverage-1 )/edge_coverage):]
# find first occurrence of qualifing line for frame
return [int( dims[1] / edge_coverage - np.where(ax_hori_upper > thresh)[0][0] ),
int( dims[0] / edge_coverage - np.where(ax_vert_left > thresh)[0][0] ),
int( np.where(ax_hori_lower > thresh)[0][0] + dims[1] * ( edge_coverage-1 )/edge_coverage ),
int( np.where(ax_vert_right > thresh)[0][0] + dims[0] * ( edge_coverage-1 )/edge_coverage )]
def detect_rot_rect(mat, minlineprop, rotrange, edge_coverage = 3, thresh=1.98):
'''Detect lines that are horizontal and rectangular and at least
2/3 of picture length. Finds also slightly rotated lines by image rotation'''
checkrange = np.insert(
np.arange(-int(rotrange / 2), int(rotrange / 2), 0.5), 0, 0)
test = mat.copy()
for degree in checkrange:
res = detect_rect(test, minlineprop)
if check_for_rect(res, thresh):
print("Rotated", degree, "degrees.", end = '\n')
return res, degree
else:
print("Rotate:", degree, "degrees.", end = '\r')
test = ndimage.rotate(mat, degree, reshape = False)
return 0, 0
def preprocess(img):
# denoise for more edgy picture
edg = cv2.fastNlMeansDenoisingColored(img, None, 8, 7, 12)
# Canny edge detection
edg = cv2.Canny(edg, 30, 250, apertureSize = 3)
# blur the edges slightly for smoother lines
edg = ndimage.gaussian_filter(edg, 2.2)
# see the detected lines:
#edge_dec(img, detect_rect( edg , .58))
return edg
def process(img, rotate, minlen=.58, thresh=1.98):
edg = preprocess(img)
# main line-based frame detection
rectd, degr = detect_rot_rect(edg, minlen, rotate,
edge_coverage=4, thresh=thresh)
if rectd is not 0:
# rotate the original image to correct angle
if degr != 0:
img = ndimage.rotate(img, degr, reshape = False)
# crop the frame
frames = get_rect_bounds(rectd, thresh=thresh)
proc_img = crop(img, frames)
# else recurse the frame picture until no
# good frames are to be found.
if get_size(proc_img) > get_size(img) / 1.4:
return process(proc_img, 4)
else:
return proc_img
else:
return img
# MAIN
def mainer(input_file, rotation=20, minlen = .59, thresh = 1.98, sizediff = 4):
if (minlen > 0 and sizediff > 1 and rotation > 0):
print("Checking", input_file)
img = cv2.imread(input_file, 1)
proc_img = process(img, rotation, minlen = minlen, thresh = thresh)
if get_size(proc_img) < get_size(img) / sizediff or get_size(proc_img) == get_size(img):
print("Did not find a good cut... trying lower minimum line lengths")
mainer(input_file, rotation - 8,
minlen = minlen - .20, thresh = thresh + 0.3,
sizediff = sizediff - 1)
return None
outname = save(proc_img, input_file, "_crop.png")
print("Found cut and printed:", outname)
return None
else:
print("Did not find a cut, exiting.")
return None
def save(mat, filename, extra):
outname = os.path.dirname(
filename) + "/" + os.path.splitext(os.path.basename(
filename))[0] + extra
cv2.imwrite(outname, mat)
return outname
# RUNNER
if __name__ == '__main__':
parser = ArghParser()
parser.set_default_command(mainer)
parser.dispatch()
# NOTE: NOT USED!
# def apply_hough(mat, thresh = 110, maxgap = 3, minline = 50):
# '''Detect and create mask of lines from the picture.
# Hough method, probabilistic line detection but not accurate enough
# for standalone usage. Mayby add it before rectangle detection for more
# flexible frame detection?'''
# dims = mat.shape
# blank = np.zeros(dims)
# lines = cv2.HoughLinesP(mat,
# rho = 1,
# theta = np.pi/180,
# threshold = thresh,
# minLineLength = minline,
# maxLineGap = maxgap)
#
# for x1,y1,x2,y2 in np.squeeze( lines ):
# cv2.line(blank,(x1,y1),(x2,y2),(254),1)
# return blank