
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# (c) Copyright 2003-2018 HP Development Company, L.P.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#
# Author: Santhosh M, Ram Varma P, Srinivas Teja, Badal Kumar Gouda
#



#from PIL import ImageStat
from base import utils, os_utils
from base.g import *
#from PIL import Image
#from PIL import ImageEnhance
#from tesserocr import PyTessBaseAPI, PSM
#from PyPDF2 import PdfFileMerger
#from PyPDF2 import PdfFileReader
#import numpy as np
#import PyPDF2
#import subprocess
#import imutils
#import cv2
import math
import os
import sys
import platform
import re
import subprocess
from installer import core_install
from installer import dcheck
#from .dcheck import *
threshold = 200     #the average of the darkest values must be _below_ this to count (0 is darkest, 255 is lightest)
obviousness = 50    #how many of the darkest pixels to include (1 would mean a single dark pixel triggers it)
punchhole_margin = 190 #the margin considered for the detection of punch hole from the edge of the paper for punch hole removal feature

def rotate_image(image, angle):
    import numpy as np
    import cv2
    """
    Rotates an OpenCV 2 / NumPy image about it's centre by the given angle
    (in radians). The returned image will be large enough to hold the entire
    new image, with a black background
    """

    # Get the image size
    # No that's not an error - NumPy stores image matricies backwards
    image_size = (image.shape[1], image.shape[0])
    image_center = tuple(np.array(image_size) / 2)

    # Convert the OpenCV 3x2 rotation matrix to 3x3
    rot_mat = np.vstack(
        [cv2.getRotationMatrix2D(image_center, angle, 1.0), [0, 0, 1]]
    )

    rot_mat_notranslate = np.matrix(rot_mat[0:2, 0:2])

    # Shorthand for below calcs
    image_w2 = image_size[0] * 0.5
    image_h2 = image_size[1] * 0.5

    # Obtain the rotated coordinates of the image corners
    rotated_coords = [
        (np.array([-image_w2,  image_h2]) * rot_mat_notranslate).A[0],
        (np.array([ image_w2,  image_h2]) * rot_mat_notranslate).A[0],
        (np.array([-image_w2, -image_h2]) * rot_mat_notranslate).A[0],
        (np.array([ image_w2, -image_h2]) * rot_mat_notranslate).A[0]
    ]

    # Find the size of the new image
    x_coords = [pt[0] for pt in rotated_coords]
    x_pos = [x for x in x_coords if x > 0]
    x_neg = [x for x in x_coords if x < 0]

    y_coords = [pt[1] for pt in rotated_coords]
    y_pos = [y for y in y_coords if y > 0]
    y_neg = [y for y in y_coords if y < 0]

    right_bound = max(x_pos)
    left_bound = min(x_neg)
    top_bound = max(y_pos)
    bot_bound = min(y_neg)

    new_w = int(abs(right_bound - left_bound))
    new_h = int(abs(top_bound - bot_bound))

    # We require a translation matrix to keep the image centred
    trans_mat = np.matrix([
        [1, 0, int(new_w * 0.5 - image_w2)],
        [0, 1, int(new_h * 0.5 - image_h2)],
        [0, 0, 1]
    ])

    # Compute the tranform for the combined rotation and translation
    affine_mat = (np.matrix(trans_mat) * np.matrix(rot_mat))[0:2, :]

    # Apply the transform
    result = cv2.warpAffine(
        image,
        affine_mat,
        (new_w, new_h),
        flags=cv2.INTER_LINEAR
    )

    return result


def largest_rotated_rect(w, h, angle):
    """
    Given a rectangle of size wxh that has been rotated by 'angle' (in
    radians), computes the width and height of the largest possible
    axis-aligned rectangle within the rotated rectangle.

    Original JS code by 'Andri' and Magnus Hoff from Stack Overflow

    Converted to Python by Aaron Snoswell
    """

    quadrant = int(math.floor(angle / (math.pi / 2))) & 3
    sign_alpha = angle if ((quadrant & 1) == 0) else math.pi - angle
    alpha = (sign_alpha % math.pi + math.pi) % math.pi

    bb_w = w * math.cos(alpha) + h * math.sin(alpha)
    bb_h = w * math.sin(alpha) + h * math.cos(alpha)

    gamma = math.atan2(bb_w, bb_w) if (w < h) else math.atan2(bb_w, bb_w)

    delta = math.pi - alpha - gamma

    length = h if (w < h) else w

    d = length * math.cos(alpha)
    a = d * math.sin(alpha) / math.sin(delta)

    y = a * math.cos(gamma)
    x = y * math.tan(gamma)

    return (
        bb_w - 2 * x,
        bb_h - 2 * y
    )


def crop_around_center(image, width, height):
    """
    Given a NumPy / OpenCV 2 image, crops it to the given width and height,
    around it's centre point
    """

    image_size = (image.shape[1], image.shape[0])
    image_center = (int(image_size[0] * 0.5), int(image_size[1] * 0.5))

    if(width > image_size[0]):
        width = image_size[0]

    if(height > image_size[1]):
        height = image_size[1]

    x1 = int(image_center[0] - width * 0.5)
    x2 = int(image_center[0] + width * 0.5)
    y1 = int(image_center[1] - height * 0.5)
    y2 = int(image_center[1] + height * 0.5)

    return image[y1:y2, x1:x2]

def Deskew_angle1(img):
    import numpy as np
    import cv2
    from PIL import Image
    import math
    image=np.array(img)
    image_height, image_width = image.shape[0:2]                                                  
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 100, 100, apertureSize=3)
    lines = cv2.HoughLinesP(edges, 1, math.pi / 180.0, 100, minLineLength=100, maxLineGap=5)
                                                                                                  
    angles = []
    temp_angles = []

    for x1, y1, x2, y2 in lines[0]:
        angle = math.degrees(math.atan2(y2 - y1, x2 - x1))
        temp_angles.append(angle)
        angle = abs(angle)
        if angle != 0:
            angles.append(angle)
    if not angles:
        median_angle = 0
        return median_angle
    else:
        median_angle = min(angles)

    for item in temp_angles:
        if median_angle == abs(item):
            median_angle = item
    if (median_angle >= 15 or median_angle <= -15):
        median_angle = 0
    return median_angle

def Deskew_angle2(img):
    import sys
    import math
    #import matplotlib.pyplot as plt
    import numpy as np
    from PIL import Image as im
    from scipy.ndimage import interpolation as inter

    #input_file = sys.argv[1]

    #img = im.open("hpscan004.png")

    # convert to binary
    wd, ht = img.size
    pix = np.array(img.convert('1').getdata(), np.uint8)
    bin_img = 1 - (pix.reshape((ht, wd)) / 255.0)
    #plt.imshow(bin_img, cmap='gray')
    #plt.savefig('binary.png')


    def find_score(arr, angle):
        data = inter.rotate(arr, angle, reshape=False, order=0)
        hist = np.sum(data, axis=1)
        score = np.sum((hist[1:] - hist[:-1]) ** 2)
        return hist, score


    delta = 1
    limit = 5
    angles = np.arange(-limit, limit+delta, delta)
    scores = []
    for angle in angles:
        hist, score = find_score(bin_img, angle)
        scores.append(score)
 
    if not scores:
        best_angle = 0
        return best_angle
    else:
        best_score = max(scores)

    best_angle = angles[scores.index(best_score)]
    if best_angle < 0 :
        best_angle = best_angle
    else:
        best_angle = best_angle
    if (best_angle >= 15 or best_angle <= -15):
        best_angle = 0
    return best_angle

def deskew(im):
    import numpy as np
    import cv2
    from PIL import Image
    import math
    angle1 = Deskew_angle1(im)
    angle2 = Deskew_angle2(im)
    #angle = max([Deskew_angle1(img),Deskew_angle2(img)])
    if angle1 <= 0 and angle2 <= 0:
        angle = min([angle1,angle2])
    else:
        angle = max([angle1,angle2])
    if not (angle != 90 and angle != -90):
        return im
    if (angle >= 15 or angle <= -15):
        return im
    image = np.array(im)
    image_height, image_width = image.shape[0:2]
    image_orig = np.copy(image)
    image_rotated = rotate_image(image, angle)
    image_rotated_cropped = crop_around_center(
            image_rotated,
            *largest_rotated_rect(
                image_width,
                image_height,
                math.radians(angle)
            )
        )

    return Image.fromarray(image_rotated_cropped)


def blankpage(im,lineart_mode):
    from PIL import ImageStat
    ''' check for the blank page '''

    v = ImageStat.Stat(im).var
    #print(v)
    if lineart_mode and ( v[0] < 300 ):
        return True
    if ( ( v[0] < 300 ) and ( v[1] < 300 ) and ( v[2] < 300)) and (lineart_mode ==False):
        return True
    else:
        return False

def rotate_bound(image, angle):
    import cv2
    import numpy as np
    # grab the dimensions of the image and then determine the
    # center
    (h, w) = image.shape[:2]
    (cX, cY) = (w / 2, h / 2)

    # grab the rotation matrix (applying the negative of the
    # angle to rotate clockwise), then grab the sine and cosine
    # (i.e., the rotation components of the matrix)
    M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
    cos = np.abs(M[0, 0])
    sin = np.abs(M[0, 1])

    # compute the new bounding dimensions of the image
    nW = int((h * sin) + (w * cos))
    nH = int((h * cos) + (w * sin))

    # adjust the rotation matrix to take into account translation
    M[0, 2] += (nW / 2) - cX
    M[1, 2] += (nH / 2) - cY

    # perform the actual rotation and return the image
    return cv2.warpAffine(image, M, (nW, nH))
    
def orientangle(im):
    if int(get_tesseract_version()) >= 4:
        import locale
        locale.setlocale(locale.LC_ALL, 'C')
        from tesserocr import PyTessBaseAPI, PSM
    else:
        from tesserocr import PyTessBaseAPI, PSM

    with PyTessBaseAPI(psm=PSM.AUTO_OSD) as api:        
        try:        
            api.SetImage(im)
        except IOError:
            im = im.convert("RGB")
            api.SetImage(im)
        api.Recognize()

        it = api.AnalyseLayout()
        orientation, direction, order, deskew_angle = it.Orientation()
        return orientation

def autoorient(im, angle):
    from PIL import Image
    import numpy as np
    #import imutils
    orient_dict ={ 0 :0 ,1:270, 2:180, 3:90}
    rotated=rotate_bound(np.array(im),orient_dict[angle])
    return  Image.fromarray(rotated)


#Auto Crop Code
def initialcrop(img):    
    w, h = img.size
    return img.crop((20, 20, w-20, h-20))

def find_line(vals):
    #implement edge detection once, use many times 
    for i,tmp in enumerate(vals):
        tmp.sort()
        average = float(sum(tmp[:obviousness]))/len(tmp[:obviousness])
        if average <= threshold:
            return i
    return i    #i is left over from failed threshold finding, it is the bounds

def getbox(img):
    import numpy as np    
    #get the bounding box of the interesting part of a PIL image object
    #this is done by getting the darekest of the R, G or B value of each pixel
    #and finding were the edge gest dark/colored enough
    #returns a tuple of (left,upper,right,lower)

    width, height = img.size    #for making a 2d array
    retval = [0,0,width,height] #values will be disposed of, but this is a black image's box 

    pixels = list(img.getdata())
    vals = []                   #store the value of the darkest color
    for pixel in pixels:
        vals.append(min(pixel)) #the darkest of the R,G or B values

    #make 2d array
    pyPlatform = platform.python_version()
    num = pyPlatform.split('.')
    if num[0] >= '3':
        vals = np.array([vals[i * width:(i + 1) * width] for i in range(height)])
    else:
        vals = np.array([vals[i * width:(i + 1) * width] for i in xrange(height)])

    #start with upper bounds
    forupper = vals.copy()
    retval[1] = find_line(forupper)

    #next, do lower bounds
    #forlower = vals.copy()
    forlower = np.flipud(forupper)
    retval[3] = height - find_line(forlower)

    #left edge, same as before but roatate the data so left edge is top edge
    #forleft = vals.copy()
    forleft = np.swapaxes(forupper,0,1)
    retval[0] = find_line(forleft)

    #and right edge is bottom edge of rotated array
    #forright = vals.copy()
    forright = np.swapaxes(forupper,0,1)
    forright = np.flipud(forright)
    retval[2] = width - find_line(forright)

    if retval[0] >= retval[2] or retval[1] >= retval[3]:
        return None
    return tuple(retval)

def autocrop(im):
    from PIL import Image
    import numpy as np
    import cv2
    blurred = cv2.blur(np.array(im), (3,3))
    canny = auto_canny(blurred)
    
    ## find the non-zero min-max coords of canny
    pts = np.argwhere(canny>0)
    y1,x1 = pts.min(axis=0)
    y2,x2 = pts.max(axis=0)
    ## crop the region
    cropped = np.array(im)[y1:y2, x1:x2]
    img = initialcrop(Image.fromarray(cropped))
    box = getbox(img)
    result = img.crop(box)
    return result
    
def mixedfeed(im):
    from PIL import Image
    import numpy as np
    import cv2
    blurred = cv2.blur(np.array(im), (3,3))
    canny = cv2.Canny(blurred, 10, 200)
    ## find the non-zero min-max coords of canny
    pts = np.argwhere(canny>0)
    y1,x1 = pts.min(axis=0)
    y2,x2 = pts.max(axis=0)
    ## crop the region
    cropped = np.array(im)[0:y2, x1:x2]
    return Image.fromarray(cropped)
    
def generatePdfFile(adf_page_files,outputfile):
    try:
        from PyPDF2 import PdfFileMerger, PdfFileReader
        merger = PdfFileMerger()
    except:
        from PyPDF2 import PdfMerger as PdfFileMerger
        from PyPDF2 import PdfReader as PdfFileReader
        merger = PdfFileMerger()
    #temp = utils.createSequencedFilename("hpscanAuto1", ".pdf")
    #temp = 'temp.pdf'
    #output_file1 = utils.createSequencedFilename("hpscanmultifeed", ".pdf")
    
    for p in adf_page_files:
        '''image = Image.open(p)
        image = image.convert("RGB")
        image.save(temp)
        merger.append(open(temp,'rb'))
        cmd = 'rm -f ' + temp
        utils.run(cmd)'''
        merger.append(PdfFileReader(p), 'hpscan')
    '''with open(outputfile, 'wb') as fout:
        merger.write(fout)'''
    merger.write(outputfile)
    for p in adf_page_files:
        os.remove(p)
    return outputfile

def generatePdfFile_canvas(adf_page_files,outputfile,orient_list,brx,bry,tlx,tly,output_path):
    #print ("called canvas")
    from reportlab.pdfgen import canvas
    from PIL import Image
    '''adf_page_files2 = []
    for p in adf_page_files:
        output = utils.createSequencedFilename("hpscan", ".png", output_path)
        cmd = "convert %s %s" %(p,output)
        status = utils.run(cmd)
        #print (status[0])
        #print (status[1])
        if status[0] == -1:
            #print ("entered status -1")  
            log.error("Convert command not found.")
            sys.exit(6)
        adf_page_files2.append(output)
        #print(adf_page_files2)
    for p in adf_page_files:
        os.unlink(p) 
    adf_page_files = adf_page_files2
    #print (adf_page_files)
    #print (adf_page_files2)
    #print (outputfile)'''
    c = canvas.Canvas(outputfile, (brx/0.3528, bry/0.3528))
    i=0
    for p in adf_page_files:
       #log.info("Processing page %s..." % p)
       im = Image.open(p)

       try:
           if orient_list and (orient_list[i] == 1 or orient_list[i] == 3):
               c.setPageSize(((bry-tly)/0.3528, (brx-tlx)/0.3528))
               c.drawInlineImage(im, (tlx/0.3528), (tly/0.3528), ((bry-tly)/0.3528), ((brx-tlx)/0.3528))
           else:
               c.setPageSize(((brx-tlx)/0.3528, (bry-tly)/0.3528))
               c.drawInlineImage(im, (tlx/0.3528), (tly/0.3528), ((brx-tlx)/0.3528),((bry-tly)/0.3528))
       except NameError:
           #log.error("A problem has occurred with PDF generation. This is a known bug in ReportLab. Please update your install of ReportLab to version 2.0 or greater.")
           sys.exit(1)
       except AssertionError as e:
           log.error(e)
           if PY3:
               #log.note("You might be running an older version of reportlab. Please update to the latest version")
               #log.note("More information is available at http://hplipopensource.com/node/369")
               sys.exit(1)
       except Exception as e:
           #log.error(e)
           #log.note("Try Updating to reportlab version >= 3.2")
           sys.exit(1)

       c.showPage()
       os.unlink(p)
       i+=1

    #log.info("Saving to file %s" % outputfile)
    c.save()
    #del adf_page_files2[:]
    #del adf_page_files[:]
    return outputfile
    '''log.info("Viewing PDF file in %s" % pdf_viewer)
    cmd = "%s %s &" % (pdf_viewer, output)
    os_utils.execute(cmd)
    sys.exit(0)'''

def documentmerge(adf_page_files,ext,output_path):
    import numpy as np
    from PIL import Image
    try:
        from PyPDF2 import PdfFileMerger
        if ext == ".pdf":
            merger = PdfFileMerger()
    except:
        from PyPDF2 import PdfMerger as PdfFileMerger
        if ext == ".pdf":
            merger = PdfFileMerger()
    #print(output_type)
    #adf_page_files2 = []
    list_im = []
    i = 0
    
    '''if ext == ".pdf":
        merger = PdfFileMerger()
        for p in adf_page_files:
            output = utils.createSequencedFilename("hpscan", ".png", output_path)
            cmd = "convert %s %s" %(p,output)
            status = utils.run(cmd)
            #print (status[0])
            #print (status[1])
            if status[0] == -1:
                #print ("entered status -1")  
                log.error("Convert command not found.")
                sys.exit(6)
            adf_page_files2.append(output)
        #print(adf_page_files2)
        for p in adf_page_files:
            os.unlink(p) 
        adf_page_files = adf_page_files2
        #del adf_page_files2[:]
        #print(adf_page_files)'''      	
    while i < len(adf_page_files):       
        list_im = [adf_page_files[i], adf_page_files[i+1]]        	
        imgs    = [Image.open(y) for y in list_im ]
        # pick the image which is the smallest, and resize the others to match it (can be arbitrary image shape here)
        min_shape = sorted( [(np.sum(z.size), z.size ) for z in imgs])[0][1]
        imgs_comb = np.hstack( (np.asarray( w.resize(min_shape) ) for w in imgs ) )

        # save that beautiful picture
        imgs_comb = Image.fromarray( imgs_comb)
        for p in list_im:
            os.remove(p)        
        '''if ext == ".pdf":
            temp = 'temp.pdf'
            imgs_comb = imgs_comb.convert("RGB")
            imgs_comb.save( temp )
            merger.append(open(temp,'rb'))
            cmd = 'rm -f ' + temp
            utils.run(cmd)
        else:'''
        if ext == ".pdf":
            temp = 'temp.pdf'
            imgs_comb = imgs_comb.convert("RGB")
            imgs_comb.save( temp )
            merger.append(open(temp,'rb'))
            cmd = 'rm -f ' + temp
            utils.run(cmd)
            #adf_page_files2.append(temp)
            #merger = PdfFileMerger()
        else:
            temp = utils.createSequencedFilename("hpscandoc", ext, output_path)
            imgs_comb.save( temp )
        i = i + 2
    if ext == ".pdf":
        output = utils.createSequencedFilename("hpscandoc", ext, output_path)
        with open(output, 'wb') as fout:
            merger.write(fout)
        '''del adf_page_files2[:]
        del adf_page_files[:]'''
        return output
    else:
        return None
    
def auto_canny(image, sigma = 0.33):
    import numpy as np
    import cv2
    # compute the mediam of the single channel pixel intensities
    v = np.median(image)

    # apply automatic Canny edge detection using the computed median
    lower = int(max(0, (1.0 - sigma) * v))
    upper = int(min(255, (1.0 + sigma) *v))
    edged = cv2.Canny(image, lower, upper)

    # return edged image
    return edged


def crushed(im):
    from PIL import Image
    import numpy as np
    #gray = im.convert('L')
    bw = np.asarray(im).copy()
    bw[bw < 90] = 0    # Black
    bw[bw >= 180] = 255 # White
    return Image.fromarray(bw)
    #imfile.save("result_bw.png")
    #return imfile

def bg_color_removal(im):
    import cv2
    import numpy as np
    from PIL import Image
    #im.save("Sample.png")
    image = np.array(im)
    #print (image)

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    alpha = 2.5
    beta = -0.0

    denoised = alpha * gray + beta
    denoised = np.clip(denoised, 0, 255).astype(np.uint8)

    denoised = cv2.fastNlMeansDenoising(denoised, None, 31, 7, 21)

    return Image.fromarray(denoised)
    
def adjust_sharpness(im, factor):
    from PIL import ImageEnhance
    #image = Image.open(input_image)
    enhancer_object = ImageEnhance.Sharpness(im)
    out = enhancer_object.enhance(factor)
    return out
    
def adjust_contrast(im, factor):
    from PIL import ImageEnhance
    #image = Image.open(input_image)
    enhancer_object = ImageEnhance.Contrast(im)
    out = enhancer_object.enhance(factor)
    return out
    
def adjust_brightness(im, factor):
    from PIL import ImageEnhance
    #image = Image.open(input_image)
    enhancer_object = ImageEnhance.Brightness(im)
    out = enhancer_object.enhance(factor)
    return out

def adjust_color(im, factor):
    from PIL import ImageEnhance
    #image = Image.open(input_image)
    enhancer_object = ImageEnhance.Color(im)
    out = enhancer_object.enhance(factor)
    return out
   
def merge_PDF_viewer(output,ocr):
    if (output.endswith('.pdf') == False):
        print("PDF viewer is trying open other than PDF file")
        return
    pdf_viewer = ''
    pdf_viewer_list = ['kpdf', 'acroread', 'xpdf', 'evince', 'xdg-open']
    for v in pdf_viewer_list:
        vv = utils.which(v)
        if vv:
            pdf_viewer = os.path.join(vv, v)
            break
    if ocr == True:
        #g = output.split(".pdf")
        #output_ocr = g[0] +"_ocr.pdf"
        #do not create ocr file based on the output file name, 
        # create unique sequenced file for OCR 
        # because original output file should be deleted if OCR was successful
        output_ocr_dir = os.path.dirname(output)
        output_ocr = utils.createSequencedFilename("hpscan_ocr_", ".pdf",output_ocr_dir)

        if sys.version_info[0] == 3:
            out = subprocess.Popen(['ocrmypdf',output,output_ocr],stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
            stdout,stderr =out.communicate()
        else:
            out = subprocess.Popen(['pypdfocr',output],stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
            stdout,stderr =out.communicate()

        if os.path.isfile(output_ocr):
            log.debug("OCR successful")
            cmd = pdf_viewer + "  " + output_ocr + " " + "&"
            #OCR was successful so delete the non-ocr original output
            os.unlink(output)
        else:
            log.debug("OCR failed to generate, returning original file")
            cmd = pdf_viewer + "  " + output + " " + "&"
        
        os_utils.execute(cmd)
    else:
        log.debug("OCR was not called, returning original file")
        cmd = pdf_viewer + "  " + output + " " + "&"           
        os_utils.execute(cmd)

def check_pil():
    scanjet_flag = None
    try:
        import PIL
    except ImportError as error:
        scanjet_flag=str(error)
		#.split(' ')[-1]
    except:
        scanjet_flag=str("Error occurred")
    return scanjet_flag

def check_numpy():
    scanjet_flag = None
    try:
        import numpy as np
    except ImportError as error:
        scanjet_flag=str(error)
		#.split(' ')[-1]
    except:
        scanjet_flag=str("Error occurred")
    return scanjet_flag

def check_opencv():
    scanjet_flag = None
    try:
        import cv2
    except ImportError as error:
        scanjet_flag=str(error)
		#.split(' ')[-1]
    except:
        scanjet_flag=str("Error occurred")
    return scanjet_flag

def check_pypdfocr():
    scanjet_flag = None
    try:
        import pypdfocr
    except:
        try:
            import ocrmypdf
        except:
            scanjet_flag=str("Error occurred")
    return scanjet_flag

def check_skimage():
    scanjet_flag = None
    try:
        import skimage
    except ImportError as error:
        scanjet_flag=str(error)
		
    except:
        scanjet_flag=str("Error occurred")
    return scanjet_flag

def check_tesserocr_imutils():
    scanjet_flag = None
    try:
        if int(get_tesseract_version()) >= 4:
            import locale
            locale.setlocale(locale.LC_ALL, 'C')
            import tesserocr
        else:
            import tesserocr
    except ImportError as error:
        scanjet_flag=str(error)
		#.split(' ')[-1]
    except AttributeError as error:
        scanjet_flag=str(error)
    except:
        scanjet_flag=str("Error occurred")
    return scanjet_flag


def check_pypdf2():
    scanjet_flag = None
    try:
        import PyPDF2
    except ImportError as error:
        scanjet_flag=str(error)
		#.split(' ')[-1]
    except:
        scanjet_flag=str("Error occurred")
    return scanjet_flag

def check_zbar():
    scanjet_flag = None
    pyPlatform = platform.python_version()
    num = pyPlatform.split('.')   
    if num[0] < '3':
        try:
            import zbar
        except ImportError as error:
            scanjet_flag=str(error)
			#.split(' ')[-1]
        except:
            scanjet_flag=str("Error occurred")
        return scanjet_flag
    else:
        return 'zbar'

def dominantcolor(xcord, ycord, radius, img):
    import numpy as np
    from skimage import io
    l = []
    d = {}


    width, height, channels = img.shape
    x1 =  punchhole_margin
    x2 =  (int)(width - punchhole_margin)
    y1 =  (int)(height - punchhole_margin)
    y2 =  punchhole_margin

    if((0 < ycord < width) and (0 < xcord < y2)):
        s1 = (xcord -4)
        s2 = (xcord +4)
        s3 = (ycord + (radius))
        s4 = (ycord + (8+radius))
        myimg = img[s3:s4, s1:s2]
        w , h,  c = myimg.shape
        for i in range(0, w-1):
            for j in range(0, h-1):
                rgblist = myimg[i,j]
                l.append((rgblist[0],rgblist[1],rgblist[2]))
        s1 = (xcord -4)
        s2 = (xcord +4)
        s3 = (ycord - (radius))
        s4 = (ycord - (8+radius))
        myimg = img[s3:s4, s1:s2]
        w , h,  c = myimg.shape
        for i in range(0, w-1):
            for j in range(0, h-1):
                rgblist = myimg[i,j]
                l.append((rgblist[0],rgblist[1],rgblist[2]))

    if((0 < ycord < width) and (y1 < xcord < height)):
        s1 = (xcord -4)
        s2 = (xcord +4)
        s3 = (ycord + (radius))
        s4 = (ycord + (8+radius))
        myimg = img[s3:s4, s1:s2]
        w , h,  c = myimg.shape
        for i in range(0, w-1):
            for j in range(0, h-1):
                rgblist = myimg[i,j]
                l.append((rgblist[0],rgblist[1],rgblist[2]))
        s1 = (xcord -4)
        s2 = (xcord +4)
        s3 = (ycord - (radius))
        s4 = (ycord - (8+radius))
        myimg = img[s3:s4, s1:s2]
        w , h,  c = myimg.shape
        for i in range(0, w-1):
            for j in range(0, h-1):
                rgblist = myimg[i,j]
                l.append((rgblist[0],rgblist[1],rgblist[2]))


    if((x2 < ycord < width) and (0 < xcord < height)):
        s1 = (xcord - (8+radius))
        s2 = (xcord - (radius))
        s3 = (ycord - 4)
        s4 = (ycord + 4)
        myimg = img[s3:s4, s1:s2]
        w , h,  c = myimg.shape 
        for i in range(0, w-1):
            for j in range(0, h-1):
                rgblist = myimg[i,j]
                l.append((rgblist[0],rgblist[1],rgblist[2]))
        s1 = (xcord + (8+radius))
        s2 = (xcord + (radius))
        s3 = (ycord - 4)
        s4 = (ycord + 4)
        myimg = img[s3:s4, s1:s2]
        w , h,  c = myimg.shape
        for i in range(0, w-1):
            for j in range(0, h-1):
                rgblist = myimg[i,j]
                l.append((rgblist[0],rgblist[1],rgblist[2]))

    if((0 < ycord < x1) and (0 < xcord < height)):
        s1 = (xcord - (8+radius))
        s2 = (xcord - (radius))
        s3 = (ycord - 4)
        s4 = (ycord + 4)
        myimg = img[s3:s4, s1:s2]
        w , h,  c = myimg.shape 
        for i in range(0, w-1):
            for j in range(0, h-1):
                rgblist = myimg[i,j]
                l.append((rgblist[0],rgblist[1],rgblist[2]))
        s1 = (xcord + (8+radius))
        s2 = (xcord + (radius))
        s3 = (ycord - 4)
        s4 = (ycord + 4)
        myimg = img[s3:s4, s1:s2]
        w , h,  c = myimg.shape
        for i in range(0, w-1):
            for j in range(0, h-1):
                rgblist = myimg[i,j]
                l.append((rgblist[0],rgblist[1],rgblist[2]))


    m = set(l)
    for z in m:
        d[l.count(z)] = z
    d_keys = d.keys()
    z_list = list(d_keys)
    sortlist = sorted(z_list)
    t = (d[sortlist[-1]])

    return list(t)


def lineargradient(dominant_color, white_color, pixelcount):
    n=pixelcount
    s = tuple(dominant_color)
    f = tuple(white_color)
    RGB_list = [s]
    # Calcuate a color at each evenly spaced value of t from 1 to n
    for t in range(1, n):
        # Interpolate RGB vector for color at the current value of t
        curr_vector = [int(s[j] + (float(t)/(n-1))*(f[j]-s[j])) for j in range(3)]
        # Add it to our list of output colors
        RGB_list.append(curr_vector)

    return RGB_list

def punchhole_removal(im):
    import numpy as np
    from PIL import Image
    from skimage import io
    from skimage.color import rgba2rgb, rgb2gray
    from skimage.transform import hough_circle, hough_circle_peaks
    from skimage.feature import canny
    try:
        from skimage.draw import circle # py2 legacy code
        draw_imported = "circle"
    except ImportError:
        from skimage.draw import disk #circle module renamed disk  ref:HPLIP-1475
        draw_imported = "disk"
    from skimage.util import img_as_ubyte

    ''' check for punch holes and remove  '''
    max_peaks =  24 #maximum number of peaks to be found. changed from 99 to 24 for reducing the unnecessary punch holes being filled.

    img = np.array(im)                      # Load picture .
    if( img.shape[-1] == 3):                # check for 3 channel rgb data
        img_gray = rgb2gray(img)            # convert to gray
    else:                                   # it is 4 channel rgba data
        img_gray = rgb2gray(rgba2rgb(img))  # first convert to rgb, then convert to gray ref:HPLIP-1475
    image = img_as_ubyte(img_gray)
    width, height = image.shape
    x1 =  punchhole_margin
    x2 =  (int)(width - punchhole_margin)
    y1 =  (int)(height - punchhole_margin)
    y2 =  punchhole_margin

    edges = auto_canny(image)
    #edges = canny(image, 3, 10, 40) # perform canny to detect the edges
    hough_radii = np.arange(31, 34, 1) #get the radius range with step as 1.
    hough_res = hough_circle(edges, hough_radii) # detect the circles centres coordinates

    # Select the most prominent circles based on the max_peaks
    accums, cx, cy, radii = hough_circle_peaks(hough_res, hough_radii,total_num_peaks=max_peaks)
    for center_y, center_x, radius in zip(cy, cx, radii):

        #if the circles centres fall in the border regions, 
        #get the dominant color near the hole and fill the hole with a linear gradient of the dominant color
        if(((0 < center_y < width) and (0 < center_x < y2)) or \
           ((0 < center_y < width) and (y1 < center_x < height)) or\
           ((0 < center_y < x1) and (0 < center_x < height)) or \
           ((x2 < center_y < width) and (0 < center_x < height))):
            if(draw_imported == "circle"):
                rr, cc= circle(center_y, center_x, radius+1, img.shape) #py2 legacy code
            elif(draw_imported == "disk"): 
                rr, cc= disk((center_y, center_x), radius+1, shape=img.shape)
            dominantpix = dominantcolor(center_x, center_y, radius,img)          
            for i , j in zip(list(rr), list(cc)):
                img[i,j]= (dominantpix[0], dominantpix[1], dominantpix[2], 255)

    finalimage=Image.fromarray(img)
    return finalimage

def color_dropout(im,color,color_range):
    from PIL import Image
    import numpy as np
    import cv2
    import math
    import sys

    #print(color, color_range)
    npimg = np.array(im,dtype="uint8")
    hsv_img = cv2.cvtColor(npimg, cv2.COLOR_RGB2HSV)
   
    hsv_color = list(rgb2hsv(color[0], color[1], color[2]))

    boundaries = []
    boundaries.append(([max(hsv_color[0] - 10, 0), max(hsv_color[1] - color_range, 0), max(hsv_color[2] - color_range, 0)],
                       [min(hsv_color[0] + 10, 179), min(hsv_color[1] + color_range, 255), min(hsv_color[2] + color_range, 255)]))

    for (lower, upper) in boundaries:
        lower =  np.array(lower,dtype="uint8")
        upper =  np.array(upper,dtype="uint8")
        #print(lower, upper)
        mask = cv2.inRange(hsv_img, lower, upper)
        height, width = mask.shape
        for h in range (0,height-1):
            for w in range (0,width-1):
                if (mask[h,w] == 255):
                    npimg[h,w] =  (255,255,255,255)



    finalimage = Image.fromarray(npimg)
    return finalimage
# Crop out edges from the scan area:", "--edge_erase_value=<border crop value in pixel> 
# where <edge_erase_value> is number of pixels to be removed from corners 
#       <im> is PIL image type
def edge_erase(im,edge_erase_value):
    log.debug("edge_erase called with value=%d px" %edge_erase_value)

    # Importing ImageOps class from PIL module
    from PIL import ImageOps 
    width, height= im.size
    
    left = edge_erase_value
    top = edge_erase_value
    right = height-edge_erase_value
    bottom = width-edge_erase_value
 
    # Cropped image of above dimension
    # (It will not change original image)
    im1 = im.crop((left, top, bottom, right))
    #fill the cropped part with white color
    #to do - implement fill color based on user input
    im = ImageOps.expand(im1,border=edge_erase_value,fill='white')
    return im

def rgb2hsv(r, g, b):
    r, g, b = r/255.0, g/255.0, b/255.0
    mx = max(r, g, b)
    mn = min(r, g, b)
    df = mx-mn
    if mx == mn:
        h = 0
    elif mx == r:
        h = (60 * ((g-b)/df) + 360) % 360
    elif mx == g:
        h = (60 * ((b-r)/df) + 120) % 360
    elif mx == b:
        h = (60 * ((r-g)/df) + 240) % 360
    if mx == 0:
        s = 0
    else:
        s = df/mx

    v = mx

    s = int(s * 255)
    v = int(v * 255)
    h = int(h/2)

    return h, s, v

def check_scipy():
    scanjet_flag = None
    try:
        from scipy.ndimage import interpolation as inter 	
    except ImportError as error:
        scanjet_flag=str(error)
    except:
        scanjet_flag=str("Error occurred")
    return scanjet_flag


def _read_string(s):
    if sys.version_info >= (3, 0):
        return s.decode('UTF-8')
    else:
        return s

def get_tesseract_version():
    try:
        p = subprocess.Popen(['tesseract', '-v'], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
        stdout_version, version = p.communicate()
        version = _read_string(version).strip()
        if version == '':
            version = _read_string(stdout_version).strip()
        version_match = re.search(r'^tesseract ((?:\d+\.)+\d+).*', version, re.M)
        if version_match:
            version = version_match.group(1)
            return version[0]
        else:
            return 0
    except OSError as e:
        #log.debug('Failed to extract tesseract version from executable: {}'.format(e))
        return 0

#convert grayscale images to 1bit black and white
def convert_to_BW(im,threshold=200):
    print("Converting to BW")
    #Apply threshold to create a monochrome image 
    fn = lambda x:255 if x > threshold else 0
    monochrome_image = im.point(fn, mode='1')
    return monochrome_image

#resize the image to the scan area
def resize_to_scan_area(im,size,res):
    from PIL import Image
    #print("Resizing to scan area")
    brx,bry,_desc,unit = size
    #print("brx , bry = ", brx,bry)
    if unit == 'mm':
        brx = int(brx * 0.03937*int(res))
        bry = int(bry * 0.03937*int(res))
    if unit == 'in':
        brx = int(brx*int(res))
        bry = int(bry*int(res))
    #print("brx, bry in px = ",brx,bry)
    #resize the image to the scan area
    try:
        im = im.resize((brx,bry), Image.ANTIALIAS)
    except AttributeError:
        im = im.resize((brx, bry), Image.Resampling.LANCZOS)

    return im
