def splitimage(image): dpmm = min(image.shape[0:2]) / DOCSIZE[0] sizethresh = SIZE_THRESH_MM * dpmm uprightimg = makeupright(image) grayimg = getgrayimage(uprightimg) # top line top = grayimg[0, :] sepx = [ 0, ] ret, binimg = cv2.threshold(top, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats( binimg) for i in range(1, nlabels): if stats[i, cv2.CC_STAT_AREA] >= sizethresh: sepx.append(centroids[i][1]) # left line left = grayimg[:, 0] sepy = [ 0, ] ret, binimg = cv2.threshold(left, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats( binimg) for i in range(1, nlabels): if stats[i, cv2.CC_STAT_AREA] >= sizethresh: sepy.append(centroids[i][1]) # divide into images imgs = [] for iy in range(len(sepy)): for ix in range(len(sepx)): if iy == len(sepy) - 1: if ix == len(sepx) - 1: #right-bottom corner imgs.append(uprightimg[int(sepy[iy]):, int(sepx[ix]):]) else: #bottom end imgs.append(uprightimg[int(sepy[iy]):, int(sepx[ix]):int(sepx[ix + 1])]) else: if ix == len(sepx) - 1: #right end imgs.append(uprightimg[int(sepy[iy]):int(sepy[iy + 1]), int(sepx[ix]):]) else: #others imgs.append(uprightimg[int(sepy[iy]):int(sepy[iy + 1]), int(sepx[ix]):int(sepx[ix + 1])]) return imgs
def scanchars(img, outdir, verbose=True): wholeimage = slantcorrection.correctslant(img) markersize = getapproxmarkersize(wholeimage) #dpmm = min(img.shape[0:2]) / DOCSIZE[0] imgs = splitimage(wholeimage) resol = detectresol(imgs.pop()) if resol == None: if verbose: print( 'QR Code for the page cannot be detected. skipping the image') return for im in imgs: name, croppedimg = getcroppedarea(im, markersize) if name == None: continue grayimg = getgrayimage(croppedimg) ret, binimg = cv2.threshold(grayimg, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) if verbose: print(name, end=" ") sys.stdout.flush() #cv2.imwrite("chr/"+name+".jpg", croppedimg) imgheight = 1000 margintop = int(imgheight * resol[1][0] / resol[1][1]) #-h[px] * (margint[mm]/h[mm]) marginbottom = int(imgheight * resol[1][2] / resol[1][1]) #-h[px] * (marginb[mm]/h[mm]) imgwidth = int(imgheight * resol[0][1] / resol[1][1]) #h[px] * (w[mm]/h[mm]) marginleft = int(imgheight * resol[0][0] / resol[1][1]) #-h[px] * (marginl[mm]/h[mm]) marginright = int(imgheight * resol[0][2] / resol[1][1]) #-h[px] * (marginr[mm]/h[mm]) # potrace cannot set size in px when converting to SVG. # set size in pt and convert to SVG, after that, replace pt with px optargs = [ "-W%dpt" % (imgwidth + marginleft + marginright), "-H%dpt" % (imgheight + margintop + marginbottom), "-L%dpt" % (-marginleft), "-R%dpt" % (-marginright), "-T%dpt" % (-margintop), "-B%dpt" % (-marginbottom) ] bsvg = passpotrace.passpotrace(binimg, optargs) bsvg = bsvg.replace(b"pt", b"px") # any exceptions? #save function if outdir != '' and not os.path.isdir(outdir): os.makedirs(outdir) saveasfile(outdir, name, bsvg) #break if verbose: print("")
def makeupright(image): #binarize image grayimg = getgrayimage(image) ret, binimg = cv2.threshold(grayimg,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) # markers which are placed for dividing make # the top line darker than the bottom line nwhitefirst = np.count_nonzero(binimg[0,:]) #first line nwhitelast = np.count_nonzero(binimg[-1,:]) #last line if nwhitefirst <= nwhitelast: # upright return image else: return np.rot90(image, k=2)
def makeupright(image): #binarize image grayimg = getgrayimage(image) ret, binimg = cv2.threshold(grayimg, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # markers which are placed for dividing make # the top line darker than the bottom line nwhitefirst = np.count_nonzero(binimg[0, :]) #first line nwhitelast = np.count_nonzero(binimg[-1, :]) #last line if nwhitefirst <= nwhitelast: # upright return image else: return np.rot90(image, k=2)
def detectmarker(image): grayscale = getgrayimage(image) mkradius = getapproxmarkerradius(grayscale) # approximate marker radius marker = cv2.resize(MARKER, (mkradius*2, mkradius*2)) # resize the marker #template matching matched = cv2.matchTemplate(grayscale, marker, cv2.TM_CCORR_NORMED) #returns float32 #detect 4 greatest values markerposarray = [] for i in range(4): (minval, maxval, minloc, maxloc) = cv2.minMaxLoc(matched) markerposarray.append(tuple(map(lambda x: x+mkradius, maxloc))) cv2.circle(matched, maxloc, mkradius, (0.0), -1) #ignore near the current minloc return markerposarray
def splitimage(image): dpmm = min(image.shape[0:2]) / DOCSIZE[0] sizethresh = SIZE_THRESH_MM * dpmm uprightimg = makeupright(image) grayimg = getgrayimage(uprightimg) # top line top = grayimg[0,:] sepx = [0,] ret, binimg = cv2.threshold(top,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats(binimg) for i in range(1,nlabels): if stats[i,cv2.CC_STAT_AREA] >= sizethresh: sepx.append(centroids[i][1]) # left line left = grayimg[:,0] sepy = [0,] ret, binimg = cv2.threshold(left,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats(binimg) for i in range(1,nlabels): if stats[i,cv2.CC_STAT_AREA] >= sizethresh: sepy.append(centroids[i][1]) # divide into images imgs = [] for iy in range(len(sepy)): for ix in range(len(sepx)): if iy == len(sepy) - 1: if ix == len(sepx) - 1: #right-bottom corner imgs.append(uprightimg[int(sepy[iy]):,int(sepx[ix]):]) else: #bottom end imgs.append(uprightimg[int(sepy[iy]):,int(sepx[ix]):int(sepx[ix+1])]) else: if ix == len(sepx) - 1: #right end imgs.append(uprightimg[int(sepy[iy]):int(sepy[iy+1]),int(sepx[ix]):]) else: #others imgs.append(uprightimg[int(sepy[iy]):int(sepy[iy+1]),int(sepx[ix]):int(sepx[ix+1])]) return imgs
def scanchars(img, outdir, verbose=True): wholeimage = slantcorrection.correctslant(img) markersize = getapproxmarkersize(wholeimage) #dpmm = min(img.shape[0:2]) / DOCSIZE[0] imgs = splitimage(wholeimage) resol = detectresol(imgs.pop()) if resol == None: if verbose: print('QR Code for the page cannot be detected. skipping the image') return for im in imgs: name, croppedimg = getcroppedarea(im, markersize) if name == None: continue grayimg = getgrayimage(croppedimg) ret, binimg = cv2.threshold(grayimg,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) if verbose: print(name, end=" ") sys.stdout.flush() #cv2.imwrite("chr/"+name+".jpg", croppedimg) imgheight = 1000 margintop = int(imgheight * resol[1][0] / resol[1][1]) #-h[px] * (margint[mm]/h[mm]) marginbottom= int(imgheight * resol[1][2] / resol[1][1]) #-h[px] * (marginb[mm]/h[mm]) imgwidth = int(imgheight * resol[0][1] / resol[1][1]) #h[px] * (w[mm]/h[mm]) marginleft = int(imgheight * resol[0][0] / resol[1][1]) #-h[px] * (marginl[mm]/h[mm]) marginright = int(imgheight * resol[0][2] / resol[1][1]) #-h[px] * (marginr[mm]/h[mm]) # potrace cannot set size in px when converting to SVG. # set size in pt and convert to SVG, after that, replace pt with px optargs = [ "-W%dpt"%(imgwidth + marginleft + marginright), "-H%dpt"%(imgheight + margintop + marginbottom), "-L%dpt"%(- marginleft), "-R%dpt"%(- marginright), "-T%dpt"%(- margintop), "-B%dpt"%(- marginbottom)] bsvg = passpotrace.passpotrace(binimg, optargs) bsvg = bsvg.replace(b"pt", b"px") # any exceptions? #save function if outdir != '' and not os.path.isdir(outdir): os.makedirs(outdir) saveasfile(outdir, name, bsvg) #break if verbose: print("")
def getmarkercenter(image, pos): mkradius = getapproxmarkerradius(image) buffer = int(mkradius * 0.15) roisize = mkradius + buffer # half of the height or width x = pos[0] - roisize y = pos[1] - roisize w = 2 * roisize h = 2 * roisize roi = image[y:y+h, x:x+w] grayroi = getgrayimage(roi) ret, binimage = cv2.threshold(grayroi,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats(binimage) # stats[0], centroids[0] are for the background label. ignore lblareas = stats[1:,cv2.CC_STAT_AREA] ave = np.average(centroids[1:], axis=0, weights=lblareas) return tuple(np.array([x, y]) + ave) # weighted average pos of centroids
def detectmarker(image): grayscale = getgrayimage(image) mkradius = getapproxmarkerradius(grayscale) # approximate marker radius marker = cv2.resize(MARKER, (mkradius * 2, mkradius * 2)) # resize the marker #template matching matched = cv2.matchTemplate(grayscale, marker, cv2.TM_CCORR_NORMED) #returns float32 #detect 4 greatest values markerposarray = [] for i in range(4): (minval, maxval, minloc, maxloc) = cv2.minMaxLoc(matched) markerposarray.append(tuple(map(lambda x: x + mkradius, maxloc))) cv2.circle(matched, maxloc, mkradius, (0.0), -1) #ignore near the current minloc return markerposarray
def getmarkerboundingrect(img, mkpos, mksize): buffer = int(mksize * 0.15) x = mkpos[0] - buffer y = mkpos[1] - buffer w = mksize + buffer*2 h = mksize + buffer*2 roi = img[y:y+h, x:x+w] grayroi = getgrayimage(roi) ret, binimage = cv2.threshold(grayroi,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats(binimage) # stats[0], centroids[0] are for the background label. ignore # cv2.CC_STAT_LEFT, cv2.CC_STAT_TOP, cv2.CC_STAT_WIDTH, cv2.CC_STAT_HEIGHT lblareas = stats[1:,cv2.CC_STAT_AREA] imax = max(enumerate(lblareas), key=(lambda x: x[1]))[0] + 1 boundingrect = Rect(stats[imax, cv2.CC_STAT_LEFT], stats[imax, cv2.CC_STAT_TOP], stats[imax, cv2.CC_STAT_WIDTH], stats[imax, cv2.CC_STAT_HEIGHT]) return boundingrect.addoffset((x,y))
def getmarkercenter(image, pos): mkradius = getapproxmarkerradius(image) buffer = int(mkradius * 0.15) roisize = mkradius + buffer # half of the height or width x = pos[0] - roisize y = pos[1] - roisize w = 2 * roisize h = 2 * roisize roi = image[y:y + h, x:x + w] grayroi = getgrayimage(roi) ret, binimage = cv2.threshold(grayroi, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats( binimage) # stats[0], centroids[0] are for the background label. ignore lblareas = stats[1:, cv2.CC_STAT_AREA] ave = np.average(centroids[1:], axis=0, weights=lblareas) return tuple(np.array([x, y]) + ave) # weighted average pos of centroids
def getcroppedarea(img, markersize): #use template matching to detect area to be cropped grayimg = getgrayimage(img) # detect top-left marker using template matching marker_tl = cv2.resize(MARKER_TL, (markersize, markersize)) matched = cv2.matchTemplate(grayimg, marker_tl, cv2.TM_CCORR_NORMED) #returns float32 (minval, maxval, minloc, maxloc) = cv2.minMaxLoc(matched) mkrect = getmarkerboundingrect(grayimg, maxloc, markersize) pos_tl = (mkrect.x + mkrect.w, mkrect.y + mkrect.h) #pos_tl = (maxloc[0]+markersize, maxloc[1]+markersize) # detect bottom-right marker using template matching marker_br = cv2.resize(MARKER_BR, (markersize, markersize)) matched = cv2.matchTemplate(grayimg, marker_br, cv2.TM_CCORR_NORMED) #returns float32 (minval, maxval, minloc, maxloc) = cv2.minMaxLoc(matched) mkrect = getmarkerboundingrect(grayimg, maxloc, markersize) pos_br = (mkrect.x, mkrect.y) #pos_br = maxloc #detect QR code qrarea = img[pos_br[1]:, :img.shape[0] - pos_br[1]] typ, val = passzbar.passzbar(qrarea) if not typ: return None, None strval = val.decode('ascii').strip() #print(strval) #cv2.circle(img, pos_tl, 5, (255, 0, 0), -1) #cv2.circle(img, pos_br, 5, (0, 255, 0), -1) #print(pos_tl, pos_br #cv2.imshow("hoge", img) #cv2.imshow("hoge", img[pos_tl[1]:pos_br[1], pos_tl[0]:pos_br[0]]) # crop and return detected area return strval, img[pos_tl[1]:pos_br[1], pos_tl[0]:pos_br[0]]
def getmarkerboundingrect(img, mkpos, mksize): buffer = int(mksize * 0.15) x = mkpos[0] - buffer y = mkpos[1] - buffer w = mksize + buffer * 2 h = mksize + buffer * 2 roi = img[y:y + h, x:x + w] grayroi = getgrayimage(roi) ret, binimage = cv2.threshold(grayroi, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats( binimage) # stats[0], centroids[0] are for the background label. ignore # cv2.CC_STAT_LEFT, cv2.CC_STAT_TOP, cv2.CC_STAT_WIDTH, cv2.CC_STAT_HEIGHT lblareas = stats[1:, cv2.CC_STAT_AREA] imax = max(enumerate(lblareas), key=(lambda x: x[1]))[0] + 1 boundingrect = Rect(stats[imax, cv2.CC_STAT_LEFT], stats[imax, cv2.CC_STAT_TOP], stats[imax, cv2.CC_STAT_WIDTH], stats[imax, cv2.CC_STAT_HEIGHT]) return boundingrect.addoffset((x, y))
def getcroppedarea(img, markersize): #use template matching to detect area to be cropped grayimg = getgrayimage(img) # detect top-left marker using template matching marker_tl = cv2.resize(MARKER_TL, (markersize, markersize)) matched = cv2.matchTemplate(grayimg, marker_tl, cv2.TM_CCORR_NORMED) #returns float32 (minval, maxval, minloc, maxloc) = cv2.minMaxLoc(matched) mkrect = getmarkerboundingrect(grayimg, maxloc, markersize) pos_tl = (mkrect.x+mkrect.w, mkrect.y+mkrect.h) #pos_tl = (maxloc[0]+markersize, maxloc[1]+markersize) # detect bottom-right marker using template matching marker_br = cv2.resize(MARKER_BR, (markersize, markersize)) matched = cv2.matchTemplate(grayimg, marker_br, cv2.TM_CCORR_NORMED) #returns float32 (minval, maxval, minloc, maxloc) = cv2.minMaxLoc(matched) mkrect = getmarkerboundingrect(grayimg, maxloc, markersize) pos_br = (mkrect.x, mkrect.y) #pos_br = maxloc #detect QR code qrarea = img[pos_br[1]:,:img.shape[0]-pos_br[1]] typ, val = passzbar.passzbar(qrarea) if not typ: return None, None strval = val.decode('ascii').strip() #print(strval) #cv2.circle(img, pos_tl, 5, (255, 0, 0), -1) #cv2.circle(img, pos_br, 5, (0, 255, 0), -1) #print(pos_tl, pos_br #cv2.imshow("hoge", img) #cv2.imshow("hoge", img[pos_tl[1]:pos_br[1], pos_tl[0]:pos_br[0]]) # crop and return detected area return strval, img[pos_tl[1]:pos_br[1], pos_tl[0]:pos_br[0]]