This repository was archived by the owner on May 20, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
Ocr #40
Open
Morgan-W
wants to merge
14
commits into
master
Choose a base branch
from
OCR
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Ocr #40
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
045e46a
implement OCR
Morgan-W 37aa090
OCR v2
Morgan-W cb5d5fd
OCR v3
Morgan-W 692d977
OCR v4. issues with pytesseract identification persist
Morgan-W cf057cb
alert update
Morgan-W 6345116
OCR final
Morgan-W 538f400
removal of extraneous code
Morgan-W fd5d3c3
testing implemented
Morgan-W b536816
testing implemented v2
Morgan-W c868384
testing implemented minor fix to prevent testfile overwriting
Morgan-W f07ba30
response to comments
Morgan-W b1a3495
response to comments2
Morgan-W fd98b19
change when to close image out
Morgan-W 6bc9fac
additional resiliency to partial or corrupted cropped images
Morgan-W File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,227 @@ | ||
|
|
||
| # Copyright 2018 The Fuego Authors. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| # ============================================================================== | ||
|
|
||
|
|
||
| import sys | ||
| import os | ||
| fuegoRoot = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||
| sys.path.insert(0, os.path.join(fuegoRoot, 'lib')) | ||
| sys.path.insert(0, fuegoRoot) | ||
| import settings | ||
| settings.fuegoRoot = fuegoRoot | ||
| import numpy as np | ||
| import matplotlib.pyplot as plt | ||
| from matplotlib.image import imread | ||
| from PIL import Image | ||
| import tempfile | ||
| import math | ||
| import logging | ||
| import string | ||
| import pytesseract | ||
| import time | ||
|
|
||
| #generalized OCR will attempt to compare img against all characters inclusive of | ||
| #UTF-8,etc. As both camera networks restrict their outputs to ASCII this improves the | ||
| #efficiency and accuracy of the OCR script by defining expected allowed characters | ||
| char_whitelist = string.digits | ||
| char_whitelist += string.ascii_lowercase | ||
| char_whitelist += string.ascii_uppercase | ||
| char_whitelist += string.punctuation.replace("'","").replace('"','') | ||
|
|
||
|
|
||
| def load_image( infilename ) : | ||
| """loads an image file to an array | ||
| Args: | ||
| infilename: file path | ||
| Returns: | ||
| numpy array of image data | ||
| """ | ||
| im = imread(infilename) | ||
| return np.array(im) | ||
|
|
||
| def save_image( npdata, outfilename ) : | ||
| """saves an image file from an array | ||
| Args: | ||
| npdata: (array) numpy array of image data | ||
| outfilename: (str)strfile path | ||
| Returns: | ||
| None | ||
| """ | ||
| outimg = Image.fromarray( npdata, "RGB" ) | ||
| outimg.save( outfilename, format='JPEG' ) | ||
|
|
||
|
|
||
| def ocr_crop(image,outputname = None,maxHeight=60): | ||
| """saves an image file from an array | ||
| Args: | ||
| image (str): image path | ||
| opt outputname (str): save crop to address | ||
| opt maxHeight (int): maximum height to search for metadata else default 60 | ||
| Returns: | ||
| npdata (array): numpy array of cropped image data | ||
| bottom (int): height of bottom of metadata from image bottom | ||
| top (int):height of top of metadata from image bottom | ||
| """ | ||
|
|
||
| cushionRows = 4 | ||
| minLetterBrightness = int(.8*255) # % of max value of 255 | ||
| minLetterSize = 12 | ||
|
|
||
| img = Image.open(image) | ||
Morgan-W marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| assert maxHeight < img.size[1] | ||
| try: | ||
| imgCroppedGray = img.crop((1, img.size[1] - maxHeight, 2*minLetterSize, img.size[1])).convert('L')#possibility to apply to hprwen under simuliar parameters | ||
| croppedArray = np.array(imgCroppedGray) | ||
|
|
||
| except Exception as e: | ||
| logging.error('Error processing image: %s', str(e)) | ||
| return | ||
|
|
||
| top = 0 | ||
| bottom = 0 | ||
| mode = 'find_dark_below_bottom' | ||
| for i in range(maxHeight): | ||
| row = maxHeight - i - 1 | ||
| maxVal = croppedArray[row].max() | ||
| # logging.warning('Mode = %s: Top %d, Bottom %d, Max val for row %d is %d', mode, top, bottom, row, maxVal) | ||
| if mode == 'find_dark_below_bottom': | ||
| if maxVal < minLetterBrightness: | ||
| mode = 'find_bottom' | ||
| elif mode == 'find_bottom': | ||
| if maxVal >= minLetterBrightness: | ||
| mode = 'find_top' | ||
| bottom = row | ||
| elif mode == 'find_top': | ||
| if maxVal < minLetterBrightness: | ||
| possibleTop = row | ||
| if bottom - possibleTop > minLetterSize: | ||
| top = possibleTop | ||
| break | ||
|
|
||
| if not top or not bottom: | ||
| logging.error('Unable to locate metadata') | ||
| return | ||
|
|
||
| # row is last row with letters, so row +1 is first without letters, and add cushionRows | ||
| bottom = min(img.size[1] - maxHeight + bottom + 1 + cushionRows, img.size[1]) | ||
| # row is first row without letters, so subtract cushionRows | ||
| top = max(img.size[1] - maxHeight + top - cushionRows, img.size[1] - maxHeight) | ||
|
|
||
| logging.warning('Top = %d, bottom = %d', top, bottom) | ||
| imgOut = img.crop((0, top, img.size[0], bottom)) | ||
| img.close() | ||
| if outputname: | ||
| imgOut.save(outputname, format='JPEG') | ||
| npdata = np.array(imgOut) | ||
| return npdata, bottom, top | ||
|
|
||
|
|
||
| def cut_metadata(im, camera_type): | ||
| """locates and cuts the metadata tag from image | ||
| Args: | ||
| im (str) : filepath | ||
| camera_type (str): {'hpwren','Axis','unknown'} defined type of image to remove metadata from. | ||
| Returns: | ||
| metadatastrip (array): numpy array containing only the presumed metadata line | ||
| """ | ||
|
|
||
| if camera_type == 'unknown':#needs update | ||
| logging.warning('unkown has not been implemented yet') | ||
| return | ||
|
|
||
| if camera_type == 'Axis': | ||
| #output = im[:-4]+"_cutout"+im[-4:] | ||
| maxHeight=60 | ||
| metadatastrip, metabottom, metatop = ocr_crop(im,maxHeight=maxHeight) | ||
|
|
||
|
|
||
| return metadatastrip | ||
| if camera_type == 'hpwren':#uses first order central difference gradient in 1-D to determine edges of text | ||
| im = load_image( im ) | ||
| index = 10 | ||
| xview =10 | ||
|
|
||
| while 30>index: | ||
| pt1up = np.sum(im[index-1,:xview,:]) | ||
| pt1down =np.sum(im[index+1,:xview,:]) | ||
| if np.abs(.5*pt1down-.5*pt1up)>160*xview:#(np.sum(im[index,:xview,:]) <1000) and (np.sum(im[index+1,:xview,:]) <1000): | ||
| index=math.ceil(index*1.5)#index+=3#add a buffer for lower than average chars like g,j,q,p... | ||
| break | ||
| index+=1 | ||
| metadatastrip = im[:index,:,:] | ||
| return metadatastrip | ||
| return None | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
| def ocr_core(filename=None, data=None): | ||
| """ | ||
| This function will handle the core OCR processing of images. | ||
| Args: | ||
| opt filename (str) : filepath | ||
| opt data (array): data | ||
| Returns: | ||
| text (str): string of OCR recognized data | ||
| """ | ||
| if filename: | ||
| text = pytesseract.image_to_string(load_image( filename ),config="-c tessedit_char_whitelist=%s_-." % char_whitelist) | ||
| elif type(data) == np.ndarray: | ||
| text = pytesseract.image_to_string(data,config="-c tessedit_char_whitelist=%s_-." % char_whitelist) | ||
| else: | ||
| logging.warning('Please feed in processable data to ocr_core of type filename or data') | ||
| return | ||
| return text | ||
|
|
||
|
|
||
|
|
||
| def pull_metadata(camera_type,filename = None, save_location=False): | ||
| """ function to separate metadata from image | ||
| Args: | ||
| opt filename (str) : filepath | ||
| camera_type (str): {'hpwren','Axis','unknown'} defined type of image to remove metadata from. | ||
| opt save_location (str): filepath to save metadata strip to | ||
| Returns: | ||
| vals (list): list of OCR recognized data | ||
| """ | ||
| if not filename: | ||
| logging.warning('specify data location of data itself') | ||
| return | ||
| tic=time.time() | ||
| metadata = cut_metadata(filename,camera_type) | ||
| logging.warning('time to complete cropping: %s',time.time()-tic) | ||
| try: | ||
| tic=time.time() | ||
| vals = ocr_core(data = metadata) | ||
| logging.warning('time to complete OCR: %s',time.time()-tic) | ||
| except Exception as e: | ||
| vals = '' | ||
| if save_location: | ||
| save_image(metadata,save_location) | ||
| logging.warning('metadata strip saved to location, %s',save_location) | ||
| return vals | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.