|
7 | 7 | from scipy.ndimage import measurements, filters, interpolation, morphology
|
8 | 8 | from scipy import stats, signal
|
9 | 9 | #from skimage.morphology import convex_hull_image
|
| 10 | +from skimage.measure import find_contours, approximate_polygon |
10 | 11 | from PIL import Image
|
11 | 12 |
|
12 | 13 | from . import ocrolib
|
@@ -996,7 +997,9 @@ def h_compatible(obj1, obj2, center1, center2):
|
996 | 997 | # (which must be split anyway)
|
997 | 998 | # - with tighter polygonal spread around foreground
|
998 | 999 | # - with spread of line labels against separator labels
|
| 1000 | +# - with centerline extraction |
999 | 1001 | # - return bg line and sep labels intead of just fg line labels
|
| 1002 | +# - return centerline coords, too |
1000 | 1003 | @checks(ABINARY2)
|
1001 | 1004 | def compute_segmentation(binary,
|
1002 | 1005 | zoom=1.0,
|
@@ -1046,6 +1049,7 @@ def compute_segmentation(binary,
|
1046 | 1049 | foreground may remain unlabelled for
|
1047 | 1050 | separators and other non-text like small
|
1048 | 1051 | noise, or large drop-capitals / images),
|
| 1052 | + - list of Numpy arrays of centerline coordinates [x, y points in lr order] |
1049 | 1053 | - Numpy array of horizontal foreground lines mask,
|
1050 | 1054 | - Numpy array of vertical foreground lines mask,
|
1051 | 1055 | - Numpy array of large/non-text foreground component mask,
|
@@ -1141,10 +1145,66 @@ def compute_segmentation(binary,
|
1141 | 1145 | LOG.debug('sorting labels by reading order')
|
1142 | 1146 | llabels = morph.reading_order(llabels,rl,bt)[llabels]
|
1143 | 1147 | DSAVE('llabels_ordered', llabels)
|
1144 |
| - |
| 1148 | + |
1145 | 1149 | #segmentation = llabels*binary
|
1146 | 1150 | #return segmentation
|
1147 |
| - return llabels, hlines, vlines, images, colseps, scale |
| 1151 | + clines = compute_centerlines(bottom, top, llabels, scale) |
| 1152 | + return llabels, clines, hlines, vlines, images, colseps, scale |
| 1153 | + |
| 1154 | +@checks(AFLOAT2,AFLOAT2,SEGMENTATION,NUMBER) |
| 1155 | +def compute_centerlines(bottom, top, lines, scale): |
| 1156 | + """Get the coordinates of center lines running between each bottom and top gradient peak.""" |
| 1157 | + # smooth bottom+top maps horizontally for centerline estimation |
| 1158 | + bottom = filters.gaussian_filter(bottom, (scale*0.25,scale), mode='constant') |
| 1159 | + top = filters.gaussian_filter(top, (scale*0.25,scale), mode='constant') |
| 1160 | + # idea: center is where bottom and top gradient meet in the middle |
| 1161 | + # (but between top and bottom, not between bottom and top) |
| 1162 | + # - calculation via numpy == or isclose is too fragile numerically: |
| 1163 | + #clines = np.isclose(top, bottom, rtol=0.1) & (np.diff(top - bottom, axis=0, append=0) < 0) |
| 1164 | + #DSAVE('clines', [clines, bottom, top], enabled=True) |
| 1165 | + # - calculation via skimage.measure contours is reliable, but produces polygon segments |
| 1166 | + gradients = bottom - top |
| 1167 | + #seeds = np.diff(bottom - top, axis=0, append=0) > 0 |
| 1168 | + seeds = lines > 0 |
| 1169 | + contours = find_contours(gradients, 0, mask=seeds) |
| 1170 | + img = np.zeros_like(bottom, np.int) |
| 1171 | + #img = gradients |
| 1172 | + from skimage import draw |
| 1173 | + clines = [] |
| 1174 | + for j, contour in enumerate(contours): |
| 1175 | + # map y,x to x,y points |
| 1176 | + contour = contour[:,::-1] |
| 1177 | + #contour = approximate_polygon(contour, 1.0) |
| 1178 | + if len(contour) <= 3: |
| 1179 | + # too short already |
| 1180 | + clines.append(contour[np.argsort(contour[:,0])]) |
| 1181 | + continue |
| 1182 | + img[draw.polygon_perimeter(contour[:,1], contour[:,0], img.shape)] = j |
| 1183 | + # ensure the segment runs from left-most to right-most point once, |
| 1184 | + # find the middle between both paths (back and forth) |
| 1185 | + left = contour[:,0].argmin() |
| 1186 | + contour = np.concatenate((contour[left:], contour[:left])) |
| 1187 | + right = contour[:,0].argmax() |
| 1188 | + if right >= len(contour)-2 or right <= 1: |
| 1189 | + # no plateau - no back path |
| 1190 | + clines.append(contour[np.argsort(contour[:,0])]) |
| 1191 | + continue |
| 1192 | + contour1 = contour[0:right] |
| 1193 | + contour2 = contour[right:] |
| 1194 | + interp1 = np.interp(contour2[:,0], contour1[:,0], contour1[:,1]) |
| 1195 | + interp2 = np.interp(contour1[:,0], contour2[:,0], contour2[:,1]) |
| 1196 | + order = np.argsort(contour[:,0]) |
| 1197 | + interpolated = [] |
| 1198 | + for i in order: |
| 1199 | + if i >= right: |
| 1200 | + interpolated.append([contour[i,0], 0.5 * (contour2[i-right,1] + interp1[i-right])]) |
| 1201 | + else: |
| 1202 | + interpolated.append([contour[i,0], 0.5 * (contour1[i,1] + interp2[i])]) |
| 1203 | + interpolated = np.array(interpolated) |
| 1204 | + img[draw.polygon_perimeter(interpolated[:,1], interpolated[:,0], img.shape)] = j+0.5 |
| 1205 | + clines.append(interpolated) |
| 1206 | + DSAVE("centerline contours", img, enabled=True) |
| 1207 | + return clines |
1148 | 1208 |
|
1149 | 1209 | # from ocropus-gpageseg, but
|
1150 | 1210 | # - on both foreground and background,
|
|
0 commit comments