| @@ -0,0 +1,333 @@ | |||||
| #!/usr/bin/python | |||||
| #This takes a list of image files and acts as a tool to mark the crop region of the page | |||||
| import re | |||||
| import xml.etree.ElementTree as ET | |||||
| import os | |||||
| import sys | |||||
| from StringIO import StringIO | |||||
| import cv2 | |||||
| def showControls(): | |||||
| print(' -----------------------------------------------') | |||||
| print('| CONTROLS: |') | |||||
| print('| * set new corner (base on loc): left-click |') | |||||
| print('| * set new seam corner (two-page): middle-click|') | |||||
| print('| * confirm corners: enter |') | |||||
| print('| * mark page as abnormal: a |') | |||||
| print('| * undo: backspace |') | |||||
| print('| * start previous page over: backspace(+)|') | |||||
| #print('| * start current page over: delete |') | |||||
| print('| * exit: esc |') | |||||
| print('| |') | |||||
| print(' -----------------------------------------------') | |||||
| lastDidList=[] | |||||
| tl=(-1,-1) | |||||
| tr=(-1,-1) | |||||
| bl=(-1,-1) | |||||
| br=(-1,-1) | |||||
| tm=(-1,-1) | |||||
| bm=(-1,-1) | |||||
| image=None | |||||
| orig=None | |||||
| abnorm=False | |||||
| def draw(): | |||||
| global image,tl,tr,bl,br,tm,bm,abnorm | |||||
| if tm[0]>=0 and bm[0]>=0: | |||||
| cv2.line(image, tm, bm, (0,255,0), 2) | |||||
| if tm[0]>=0 and tl[0]>=0: | |||||
| cv2.line(image, tm, tl, (0,255,0), 2) | |||||
| if tm[0]>=0 and tr[0]>=0: | |||||
| cv2.line(image, tm, tr, (0,255,0), 2) | |||||
| if bm[0]>=0 and bl[0]>=0: | |||||
| cv2.line(image, bm, bl, (0,255,0), 2) | |||||
| if bm[0]>=0 and br[0]>=0: | |||||
| cv2.line(image, bm, br, (0,255,0), 2) | |||||
| if tl[0]>=0 and tr[0]>=0: | |||||
| cv2.line(image, tl, tr, (255,0,0), 2) | |||||
| if br[0]>=0 and tr[0]>=0: | |||||
| cv2.line(image, br, tr, (255,0,0), 2) | |||||
| if br[0]>=0 and bl[0]>=0: | |||||
| cv2.line(image, br, bl, (255,0,0), 2) | |||||
| if tl[0]>=0 and bl[0]>=0: | |||||
| cv2.line(image, tl, bl, (255,0,0), 2) | |||||
| if tl[0]>=0: | |||||
| image[tl[1],tl[0]]=(0,0,255) | |||||
| cv2.circle(image, tl, 2, (0,0,200), 1) | |||||
| cv2.circle(image, tl, 5, (0,0,200), 2) | |||||
| if tr[0]>=0: | |||||
| image[tr[1],tr[0]]=(0,0,255) | |||||
| cv2.circle(image, tr, 2, (0,0,200), 1) | |||||
| cv2.circle(image, tr, 5, (0,0,200), 2) | |||||
| if bl[0]>=0: | |||||
| image[bl[1],bl[0]]=(0,0,255) | |||||
| cv2.circle(image, bl, 2, (0,0,200), 1) | |||||
| cv2.circle(image, bl, 5, (0,0,200), 2) | |||||
| if br[0]>=0: | |||||
| image[br[1],br[0]]=(0,0,255) | |||||
| cv2.circle(image, br, 2, (0,0,200), 1) | |||||
| cv2.circle(image, br, 5, (0,0,200), 2) | |||||
| if tm[0]>=0: | |||||
| image[tm[1],tm[0]]=(0,0,255) | |||||
| cv2.circle(image, tm, 2, (0,100,200), 1) | |||||
| cv2.circle(image, tm, 5, (0,100,200), 2) | |||||
| if bm[0]>=0: | |||||
| image[bm[1],bm[0]]=(0,0,255) | |||||
| cv2.circle(image, bm, 2, (0,100,200), 1) | |||||
| cv2.circle(image, bm, 5, (0,100,200), 2) | |||||
| if abnorm: | |||||
| cv2.putText(image, 'ABNORMAL', (image.shape[1]/2,image.shape[0]/2), cv2.FONT_HERSHEY_PLAIN, 2, (0,0,255)) | |||||
| cv2.imshow("image", image) | |||||
| bimage=None | |||||
| def clicker(event, x, y, flags, param): | |||||
| # grab references to the global variables | |||||
| global image,tl,tr,bl,br,tm,bm,lastDidList,orig | |||||
| """if event == cv2.EVENT_LBUTTONDOWN: | |||||
| if len(segPts)>0: | |||||
| #change last boundary | |||||
| image=bimage.copy() | |||||
| segPts[-1]=x | |||||
| ll=max(0,segPts[-1]-1) | |||||
| rr=min(image.shape[1], segPts[-1]+1) | |||||
| image[:,ll:rr,0] = color[(colorIdx+len(color)-1)%len(color)][0] * image[:,ll:rr,0] | |||||
| image[:,ll:rr,1] = color[(colorIdx+len(color)-1)%len(color)][1] * image[:,ll:rr,1] | |||||
| image[:,ll:rr,2] = color[(colorIdx+len(color)-1)%len(color)][2] * image[:,ll:rr,2] | |||||
| cv2.imshow("image", image) | |||||
| """ | |||||
| if event == cv2.EVENT_LBUTTONDOWN: | |||||
| # a new boundary | |||||
| if x<image.shape[1]/2 and y<image.shape[0]/2: | |||||
| tl=(x,y) | |||||
| if 0 in lastDidList: | |||||
| image=orig.copy() | |||||
| lastDidList.remove(0) | |||||
| lastDidList.append(0) | |||||
| if x>image.shape[1]/2 and y<image.shape[0]/2: | |||||
| tr=(x,y) | |||||
| if 1 in lastDidList: | |||||
| image=orig.copy() | |||||
| lastDidList.remove(1) | |||||
| lastDidList.append(1) | |||||
| if x<image.shape[1]/2 and y>image.shape[0]/2: | |||||
| bl=(x,y) | |||||
| if 2 in lastDidList: | |||||
| image=orig.copy() | |||||
| lastDidList.remove(2) | |||||
| lastDidList.append(2) | |||||
| if x>image.shape[1]/2 and y>image.shape[0]/2: | |||||
| br=(x,y) | |||||
| if 3 in lastDidList: | |||||
| image=orig.copy() | |||||
| lastDidList.remove(3) | |||||
| lastDidList.append(3) | |||||
| draw() | |||||
| elif event == cv2.EVENT_MBUTTONDOWN: | |||||
| # a new boundary | |||||
| if y<image.shape[0]/2: | |||||
| tm=(x,y) | |||||
| if 1 in lastDidList: | |||||
| image=orig.copy() | |||||
| lastDidList.remove(1) | |||||
| lastDidList.append(4) | |||||
| if y>image.shape[0]/2: | |||||
| bm=(x,y) | |||||
| if 3 in lastDidList: | |||||
| image=orig.copy() | |||||
| lastDidList.remove(3) | |||||
| lastDidList.append(5) | |||||
| draw() | |||||
| def segmenter(imDir,imagePath,dispHeight): | |||||
| global image,tl,tr,bl,br,tm,bm,lastDidList,orig,abnorm | |||||
| print 'opening '+imDir+imagePath | |||||
| orig = cv2.imread(imDir+imagePath) | |||||
| scale = orig.shape[0]/dispHeight | |||||
| orig = cv2.resize(orig,(0,0),None,1.0/scale,1.0/scale) | |||||
| #print 'opened' | |||||
| assert orig is not None | |||||
| redo=True | |||||
| while redo: #undo loop | |||||
| abnorm=False | |||||
| lastDidList=[] | |||||
| tl=(-1,-1) | |||||
| tr=(-1,-1) | |||||
| bl=(-1,-1) | |||||
| br=(-1,-1) | |||||
| tm=(-1,-1) | |||||
| bm=(-1,-1) | |||||
| redo=False | |||||
| image = orig.copy() | |||||
| draw() | |||||
| while True: | |||||
| # display the imageWork and wait for a keypress | |||||
| key = cv2.waitKey(33) & 0xFF #so it is robust on all systems | |||||
| #print key | |||||
| if key == 13 and tl[0]>=0 and tr[0]>=0 and bl[0]>=0 and br[0]>=0: #enter | |||||
| toWrite = imagePath+','+str(int(scale*tl[0]))+','+str(int(scale*tl[1]))+','+str(int(scale*tr[0]))+','+str(int(scale*tr[1]))+','+str(int(scale*br[0]))+','+str(int(scale*br[1]))+','+str(int(scale*bl[0]))+','+str(int(scale*bl[1])) | |||||
| if abnorm: | |||||
| if tm[0]>=0 and bm[0]>=0: | |||||
| toWrite += ',ABNORMAL,'+str(int(scale*tm[0]))+','+str(int(scale*tm[1]))+','+str(int(scale*bm[0]))+','+str(int(scale*bm[1])) | |||||
| else: | |||||
| toWrite += ',ABNORMAL' | |||||
| else: | |||||
| if tm[0]>=0 and bm[0]>=0: | |||||
| toWrite += ',DOUBLE,'+str(int(scale*tm[0]))+','+str(int(scale*tm[1]))+','+str(int(scale*bm[0]))+','+str(int(scale*bm[1])) | |||||
| else: | |||||
| toWrite += ',SINGLE' | |||||
| toWrite+='\n'; | |||||
| return toWrite, False, False | |||||
| elif key == 8: #backspace | |||||
| if len(lastDidList)>0: | |||||
| imageWork = orig.copy() | |||||
| lastDid=lastDidList.pop() | |||||
| if lastDid==0: | |||||
| tl=(-1,-1) | |||||
| elif lastDid==1: | |||||
| tr=(-1,-1) | |||||
| elif lastDid==2: | |||||
| bl=(-1,-1) | |||||
| elif lastDid==3: | |||||
| br=(-1,-1) | |||||
| elif lastDid==4: | |||||
| tm=(-1,-1) | |||||
| elif lastDid==5: | |||||
| bm=(-1,-1) | |||||
| image=orig.copy() | |||||
| draw() | |||||
| else: | |||||
| return '', True, False | |||||
| elif key == 127: #del | |||||
| #if len(lastDidList)>0: | |||||
| print('[CLEAR]') | |||||
| redo=True | |||||
| break | |||||
| #else: | |||||
| # return '', True, False | |||||
| elif key == 27: #esc | |||||
| print('esc') | |||||
| return '', False, True | |||||
| #exit(0) | |||||
| #break | |||||
| elif key == 97: #'a' | |||||
| #return imagePath+',-1,-1,-1,-1,-1,-1,-1,-1,ABNORMAL\n', False, False | |||||
| abnorm = not abnorm | |||||
| image=orig.copy() | |||||
| draw() | |||||
| #return newWords, newWordBoxes | |||||
| if len(sys.argv)<4: | |||||
| print 'usage: '+sys.argv[0]+' imgDir imgList outAnn.csv [displayHeight]' | |||||
| print 'output format: imageFile, tlx, tly, trx, try, brx, bry, blx, bly, type (,tmx, tmy, bmx, bmy)' | |||||
| exit(0) | |||||
| inFile = sys.argv[2] | |||||
| imDir = sys.argv[1] | |||||
| if imDir[-1]!='/': | |||||
| imDir+='/' | |||||
| outFile = sys.argv[3] | |||||
| dispHeight=500.0 | |||||
| if len(sys.argv)>4: | |||||
| dispHeight=float(sys.argv[4]) | |||||
| cv2.namedWindow("image") | |||||
| cv2.setMouseCallback("image", clicker) | |||||
| didCount=0 | |||||
| did=[] | |||||
| try: | |||||
| check = open(outFile,'r') | |||||
| did = check.read().splitlines() | |||||
| didCount=len(did) | |||||
| check.close() | |||||
| print 'found '+outFile+', appending. Note: this is sychronizing based on count alone, if '+inFile+' hash changed, but sure to align '+outFile | |||||
| except IOError: | |||||
| print ('making new out:'+outFile) | |||||
| out = open(outFile,'w') | |||||
| print ' =============================================== ' | |||||
| print ' !!! INSTRUCTIONS !!!' | |||||
| print ' If the page does not contain a single page, or ' | |||||
| print ' an open book, mark as abnormal with INSERT (e.g.' | |||||
| print ' two seperate pages).' | |||||
| print ' Click on the four corners to include all the ' | |||||
| print ' full pages in the image (including two pages if ' | |||||
| print ' fully present).' | |||||
| print ' If two pages a full present also mark page seam ' | |||||
| print ' (middle-click).' | |||||
| print ' On placing points, prioritize the following to ' | |||||
| print ' be included/discluded from the polygons in the ' | |||||
| print ' following order:' | |||||
| print ' 1. Including the present page(s) content.' | |||||
| print ' 2. Discluding other pages and background.' | |||||
| print ' 3. Discluding the present page(s) boudary.' | |||||
| print ' 4. Including the present page(s) white area.' | |||||
| #print ' book). If a corner is torn, click where it ought' | |||||
| #print ' to be, based on page edges. The page seem on an ' | |||||
| #print ' open book is the page edge.' | |||||
| print ' Use ESC to exit or the latest page you finished ' | |||||
| print ' will be lost.' | |||||
| #i=didCount | |||||
| i=0 | |||||
| #pageCount=-1 | |||||
| prevSeg='' | |||||
| seg='' | |||||
| showControls() | |||||
| inF = open(inFile,'r') | |||||
| images = inF.read().splitlines() | |||||
| end=False | |||||
| doneOne=False | |||||
| while i<len(images) and not end: | |||||
| if i%10==9: | |||||
| showControls() | |||||
| print(str(i+1)+' of '+str(len(images))) | |||||
| if len(did)>i: | |||||
| line = did[i].strip().split(',') | |||||
| typ = line[8] | |||||
| #print typ | |||||
| if typ != '-1': | |||||
| out.write(did[i].strip()+'\n') | |||||
| i+=1 | |||||
| continue | |||||
| seg, undo, end = segmenter(imDir, images[i],dispHeight) | |||||
| out.write(seg) | |||||
| seg='' | |||||
| i+=1 | |||||
| else: | |||||
| seg, undo, end = segmenter(imDir, images[i],dispHeight) | |||||
| if len(seg)>0: | |||||
| doneOne=True | |||||
| if undo and i>0 and doneOne: | |||||
| prevSeg='' | |||||
| print(str(i)+' of '+str(len(images))) | |||||
| prevSeg, undo, end = segmenter(imDir, images[i-1],dispHeight) | |||||
| else: | |||||
| out.write(prevSeg) | |||||
| prevSeg=seg | |||||
| seg='' | |||||
| i+=1 | |||||
| out.write(prevSeg) | |||||
| out.write(seg) | |||||