
# DESCRIPTION:
# A python script to measure thickness of small tissue mchanical testing samples. This scripts creates a GUI to 
# 1. pick the desired image from its location (this image will be taken using the Optical thickness measurement system set up and flycap camera software),
# 2. calculate pixels from manually chosen ruler measurement, 
# 3. allow multiple thickness masurements on the sample, 
# 4. allow calculating average and standard deviation, and, 
# 5  save results in an xml file as well as save the final image with all the measurements overlaying on it. 
#
# WRITTEN BY: Snehal Chokhandre
#             Computational Biomodeling (CoBi) Core
#             Department of Biomedical Engineering
#             The Cleveland Clinic Foundation



import tkinter as tk
import os
import tkinter.filedialog
from tkinter.filedialog import askopenfilename
from tkinter import *
from PIL import Image, ImageTk
#import sys
#import _imagingtk
#import image
import math
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.image as mpimg
from scipy.misc import imread, imsave
import statistics

#from xml.etree.ElementTree import Element, SubElement, Comment
import xml.etree.ElementTree as ET

import lxml.etree as etree
from xml.etree import ElementTree
#import lxml.etree.XMLParser

#from xml.etree import ElementTree
#from xml.dom import minidom
#
#def prettify(elem):
#    """Return a pretty-printed XML string for the Element.
#    """
#    rough_string = ElementTree.tostring(elem, 'utf-8')
#    reparsed = minidom.parseString(rough_string)
#    return reparsed.toprettyxml(indent="  ", encoding='UTF-8')

import ntpath

file_path = ''
mm=''
measurement= ''
#filename=''

#~~~~ FUNCTIONS~~~~


def browse_and_display_file():

          global file_path
          global x
          global y
          global filename
          global scaled_image_data
          global image_data
          filename = askopenfilename()
#          infile = open(filename, 'r')
          file_path = os.path.abspath(filename)   # display filepath in window
          entry.delete(0, END)
          entry.insert(0, file_path)
          
          image_data = imread(file_path).astype(np.float32)  # display image in a new figure window
          scaled_image_data = image_data / 255.
        # imsave('test_out.png', scaled_image_data)
          plt.imshow(scaled_image_data)
          
      
          lines = []
#      def draw_line(self):
          ax = plt.gca()             # pick two points and draw a line
          xy = plt.ginput(n=2,timeout=0)
#          xy = plt.ginput(n=2, timeout=0, show_clicks=True, mouse_add=1, mouse_pop=3, mouse_stop=2)
          x = [p[0] for p in xy]
          y = [p[1] for p in xy]
          line = plt.plot(x,y)
          ax.figure.canvas.draw()
          lines.append(line)
#          print (x)
#          print (y)
          plt.draw()
#    im = Image.open(filename)
#    tkimage = PhotoImage(im)
#    tkinter.Label(root, image=tkimage).grid() 
   
#    photo = ImageTk,PhotoImage(im)
#    photo_label = Label(root,image=photo)         
#    photo_label.image = photo
#    photo_label.grid() 

#    im=Image.open(filename)
#    im.show()
  

def mm_to_pxl():
          global mm
          global one_pxl_mm
          global MM
          global round_pxl
          global pxl
#          print (mm.get())            # get the user entered input mm value 
          MM=mm.get()

#          pxl = y[1]-y[0]  
          pxl=math.sqrt(math.pow((y[1]-y[0]),2)+math.pow((x[1]-x[0]),2))
          print (pxl)
          round_pxl= round(pxl,1)
#          print (round_pxl)
          Label(f1,text=round_pxl).grid(row=5, column=1, sticky='we')
          one_pxl_mm= MM/pxl
#          print (one_pxl_mm)
#          

def pick_measurement():
          lines = []
          global x_m
          global y_m

          ax = plt.gca()             # pick two points and draw a line on sample
          xy = plt.ginput(n=2,timeout=0)
          x_m = [p[0] for p in xy]
          y_m = [p[1] for p in xy]
          line = plt.plot(x_m,y_m)
          ax.figure.canvas.draw()
          lines.append(line)
          plt.draw()
          

def ok_measurement(row,column): 
           global all_thickness
           
#           print (row)
#          info = tk.Button().grid_info()
#          print ('row='),row
#          print("row:", info["row"], "column:", info["column"])
#           pxl = y_m[1]-y_m[0]  # pixels for thickness measurement
           pxl= math.sqrt(math.pow((y_m[1]-y_m[0]),2)+math.pow((x_m[1]-x_m[0]),2))
#           X= math.sqrt(math.pow((y_m[1]-y_m[0]),2)+math.pow((x_m[1]-x_m[0]),2))
#           Y= math.sqrt(math.pow((y_m[0]-y_m[1]),2)+math.pow((x_m[1]-x_m[1]),2))
#           pxl=math.sqrt(math.pow(X,2)+math.pow(Y,2))
           pxl= round(pxl,1)
           thickness=pxl*one_pxl_mm  # thickness in mm
           thickness=round(thickness,2)
           all_thickness.append(thickness)
           Label(f1,text=thickness).grid(row=row, column=15, sticky='we')
           
#           for children in f1.children.values():
#                info = children.grid_info()
#        #note that rows and column numbers are stored as string                                                                         
#                if info['row'] == str(row) and info['column'] == str(column):
#                   return children
#           return None
#           print (info)

def compute():
        global average
        global std_dev
        global all_thickness
        average=sum(all_thickness)/len(all_thickness)
        Label(f1,text=average).grid(row=4, column=28, sticky='we')
        std_dev= statistics.stdev(all_thickness)
        Label(f1,text=std_dev).grid(row=5, column=28, sticky='we')


def create_xml():
        head, tail = ntpath.split(file_path) # works only for windows to split filename from path
        tail=tail[:-4]

#        root = ET.Element(tail)
        root = ET.Element('OTMS')
        
        ET.SubElement(root,'Sample').text=tail
#        ET.SubElement(sample_name,'Name').text=tail
      

        calib = ET.SubElement(root,"calibration")
        ET.SubElement(calib,"Physical",units="mm").text=str(MM)
        ET.SubElement(calib,"image",units="pixel").text= str(pxl)
        ET.SubElement(calib,"resolution",units="mm/pixel").text= str(one_pxl_mm)
        
        ET.SubElement(root,"Thickness",units="mm").text= str(all_thickness)
        
        stats= ET.SubElement(root,"Statistics",units="mm")
        ET.SubElement(stats,"average").text=str(average)
        ET.SubElement(stats,"std_dev").text=str(std_dev)
        
        tree = ET.ElementTree(root)
        tree.write( tail+ '.xml')
#

        plt.savefig(tail+ '.png')   #save figure with measurements
        prettyPrintXml(tail+ '.xml')
        
def prettyPrintXml(xmlFilePathToPrettyPrint):
    assert xmlFilePathToPrettyPrint is not None
    parser = etree.XMLParser(resolve_entities=False, strip_cdata=False)
    document = etree.parse(xmlFilePathToPrettyPrint, parser)
    document.write(xmlFilePathToPrettyPrint, pretty_print=True, encoding='utf-8')
#    
    
#~~~~~~ GUI ~~~~~~~~

root = Tk()
root.title('Thickness measurement')
root.geometry("500x400+250+100")

mf = Frame(root)
mf.grid()


f1 = Frame(mf, width=500, height=250)
f1.grid()
f2 = Frame(mf, width=500, height=250)
f2.grid()
#f3 = Frame(mf, width=600, height=250)
#f3.pack()
#f4 = Frame(mf, width=600, height=250)
#f4.pack()

file_path = StringVar()
mm= IntVar()
all_thickness=[]
#measurement=IntVar()

Label(f1,text="Calibration",fg='blue').grid(row=3, column=1, sticky='we')
Label(f1,text="Measurements (mm)",fg='blue').grid(row=3, column=15, sticky='we')
Label(f1,text="Results",fg='blue').grid(row=3, column=27, sticky='we')

Label(f1,text="mm").grid(row=4, column=0, sticky='we')
entry = Entry(f1, width=5,textvariable=mm)  # change filepath to measured mm on ruler 
entry.grid(row=4,column=1,padx=0,pady=0,sticky='we',columnspan=1)

Label(f1,text="pixels").grid(row=5, column=0, sticky='we')
#entry = Entry(f1, width=5)  # replace filepath for measured pixels 
#entry.grid(row=5,column=1,padx=0,pady=0,sticky='we',columnspan=1)

#---
#R=4
#for f in range(1,11):
##    if i<=11:
##      Label(f1,text=i).grid(row=R, column=14, sticky='we')
##      entry = Entry(f1, width=5) # filepath replaced with measurement
##      entry.grid(row=R,column=15,padx=0,pady=0,sticky='we',columnspan=1)
#      Button(f1, text="OK",command=lambda row=R, column=16: fourth_iter.ok_measurement(row,column)).grid(row=R, column=16, sticky='ew', padx=8, pady=4) # command will be replaced with aafirmation of measurement
#      Button(f1, text="Pick",command=third_iter.pick_measurement).grid(row=R, column=14, sticky='ew', padx=8, pady=4)       
#      R+=1
# ---

Button(f1, text="OK",command=lambda row=4, column=16: ok_measurement(row,column)).grid(row=4, column=16, sticky='ew', padx=8, pady=4) # command will be replaced with aafirmation of measurement
Button(f1, text="Pick",command=pick_measurement).grid(row=4, column=14, sticky='ew', padx=8, pady=4)
#  
#Button(f1, text="OK",command=lambda row=5, column=16: ok_measurement(row,column)).grid(row=5, column=16, sticky='ew', padx=8, pady=4) # command will be replaced with aafirmation of measurement
#Button(f1, text="Pick",command=pick_measurement).grid(row=5, column=14, sticky='ew', padx=8, pady=4) 
# 
#Button(f1, text="OK",command=lambda row=6, column=16: ok_measurement(row,column)).grid(row=6, column=16, sticky='ew', padx=8, pady=4) # command will be replaced with aafirmation of measurement
#Button(f1, text="Pick",command=pick_measurement).grid(row=6, column=14, sticky='ew', padx=8, pady=4)  
#
#Button(f1, text="OK",command=lambda row=7, column=16: ok_measurement(row,column)).grid(row=7, column=16, sticky='ew', padx=8, pady=4) # command will be replaced with aafirmation of measurement
#Button(f1, text="Pick",command=pick_measurement).grid(row=7, column=14, sticky='ew', padx=8, pady=4) 
# 
#Button(f1, text="OK",command=lambda row=8, column=16: ok_measurement(row,column)).grid(row=8, column=16, sticky='ew', padx=8, pady=4) # command will be replaced with aafirmation of measurement
#Button(f1, text="Pick",command=pick_measurement).grid(row=8, column=14, sticky='ew', padx=8, pady=4)  
#
#Button(f1, text="OK",command=lambda row=9, column=16: ok_measurement(row,column)).grid(row=9, column=16, sticky='ew', padx=8, pady=4) # command will be replaced with aafirmation of measurement
#Button(f1, text="Pick",command=pick_measurement).grid(row=9, column=14, sticky='ew', padx=8, pady=4) 
# 
#Button(f1, text="OK",command=lambda row=10, column=16: ok_measurement(row,column)).grid(row=10, column=16, sticky='ew', padx=8, pady=4) # command will be replaced with aafirmation of measurement
#Button(f1, text="Pick",command=pick_measurement).grid(row=10, column=14, sticky='ew', padx=8, pady=4) 
# 
#Button(f1, text="OK",command=lambda row=11, column=16: ok_measurement(row,column)).grid(row=11, column=16, sticky='ew', padx=8, pady=4) # command will be replaced with aafirmation of measurement
#Button(f1, text="Pick",command=pick_measurement).grid(row=11, column=14, sticky='ew', padx=8, pady=4)
#  
#Button(f1, text="OK",command=lambda row=12, column=16: ok_measurement(row,column)).grid(row=12, column=16, sticky='ew', padx=8, pady=4) # command will be replaced with aafirmation of measurement
#Button(f1, text="Pick",command=pick_measurement).grid(row=12, column=14, sticky='ew', padx=8, pady=4)
#  
#Button(f1, text="OK",command=lambda row=13, column=16: ok_measurement(row,column)).grid(row=13, column=16, sticky='ew', padx=8, pady=4) # command will be replaced with aafirmation of measurement
#Button(f1, text="Pick",command=pick_measurement).grid(row=13, column=14, sticky='ew', padx=8, pady=4)  

Label(f1,text="Average=").grid(row=4, column=27, sticky='we')
Label(f1,text="Std Dev=").grid(row=5, column=27, sticky='we')


Label(f1,text="File").grid(row=0, column=0, sticky='e')
entry = Entry(f1, width=50, textvariable=file_path)
entry.grid(row=0,column=1,padx=2,pady=2,sticky='we',columnspan=25)
Button(f1, text="Browse", command=browse_and_display_file).grid(row=0, column=27, sticky='ew', padx=8, pady=4)
Button(f1, text="Compute",command=compute,fg='red').grid(row=6, column=28, sticky='ew', padx=8, pady=4)
Button(f1, text="Calibrate",command=mm_to_pxl,fg='red').grid(row=6, column=1, sticky='ew', padx=8, pady=4)
Button(f1, text="Save",command=create_xml,fg='red').grid(row=7, column=28, sticky='ew', padx=8, pady=4)



root.mainloop() # starts the GUI


# improvements needed


# deselect measurements 



