#@ File[]    (Label="Templates", style="file") template_files   
#@ File[]    (label="Image for which to look for the template") image_files
#@ File 	 (label="Region of Interest (ROI) for search (optional)", required=false) roi_file
#@ Boolean   (Label="Flip template vertically") flipv 
#@ Boolean   (Label="Flip template horizontally") fliph 
#@ String    (Label="Additional rotation angles separated by ," ,required=False) angles 
#@ String    (Label="Matching method",choices={"Normalised Square Difference", "Normalised cross-correlation", "Normalised 0-mean cross-correlation"}, value="Normalised 0-mean cross-correlation") method 
#@ int       (Label="Expected number of templates", min=1) n_hit 
#@ String    (visibility="MESSAGE", value="The parameters below are used only if more than 1 template are expected in the image") doc 
#@ Float     (Label="Score Threshold (0-1)", min=0, max=1, value=0.5, stepSize=0.1) score_threshold 
#@ Float     (Label="Min peak height relative to neighborhood (0-1, decrease to get more hits)", min=0, max=1, value=0.1, stepSize=0.1) tolerance 
#@ Float     (Label="Maximal overlap between Bounding boxes (0-1)",min=0, max=1, value=0.4, stepSize=0.1) max_overlap 
#@ String    (visibility="MESSAGE", value="Output") out 
#@ Boolean   (Label="Open images (as stack ie images must have identical dimensions)") show_images 
#@ Boolean   (Label="Add ROI to ROI Manager") show_roi 
#@ Boolean   (Label="Show result table") show_table 
'''
previous field : Boolean   (Label="Display correlation map(s)") show_map 
 
Requires ImageJ 1.52i to have the possibility to fill the background while rotating for 16-bit images 
 
FIJI macro  to do template matching 
input : 
- template_files : list of template path 
- image_files    : list of image path for which we want to search a template 
ie this macro search for N templates (with eventual flipped/rotated version) into L target images. 
 
 
First of all, additionnal versions of the template are generated (flip+rotation) 
For the resulting list of templates the search is carried out and results in a list of correlation maps 
 
Minima/maxima in the correlation map are detected, followed by Non-Maxima Supression in case of multiple correlation map/templates 
 
 
## TO DO :  
- order of the column in result table 
 
- use steerable tempalte matching see steerable detector BIG Lausanne 
 
 
## NB :  
- If open_images is true, the images must have the same dimensions 
 
- The multifile input is not yet macro recordable. An alternative is to use a folder input and to process the content of the folder (but not as flexible) 
 
- (currently no search ROi so not applicable) Delete the previous ROI for every new Run otherwise 1st ROI is used to limit the search 
 
- Method limited to normalised method to have correlation map in range 0-1 : easier to apply a treshold.  
Otherwise normalising relative to maxima of each correlation map is not good since this result in having the global maxima to always be one,  
eventhough its correlation value was not one. 
Another possibility would be to have an absolute threshold (realtive to the correlation score) and a relative threshold (relative to the maxima of this particular map)   
 
The multifile input is not yet macro recordable. An alternative is to use a folder input and to process the content of the folder (but not as flexible) 
'''
### IMPORT ###
import time 	  # for benchmark
from ij import IJ # ImageJ1 import

## Home-Made module 
from ROIdetection.MatchTemplate_Module    import getHit_Template, CornerToCenter 
from ROIdetection.NonMaximaSupression_Py2 import NMS 

if show_images:  
	from ij import ImagePlus, ImageStack 
	Stack_Image     = ImageStack() 
	Stack_Image_ImP = ImagePlus() 
 
if show_roi: 
	from ij.plugin.frame 	import RoiManager 
	from ij.gui 			import Roi 
	RM = RoiManager() 
	rm = RM.getInstance()  
	 
if show_table: 
	from ij.measure import ResultsTable 
	from utils 		import AddToTable 
	Table = ResultsTable().getResultsTable() # allows to append to an existing table 


# Convert method string to the opencv corresponding index 
Dico_Method  = {"Square difference":0,"Normalised Square Difference":1,"Cross-Correlation":2,"Normalised cross-correlation":3,"0-mean cross-correlation":4,"Normalised 0-mean cross-correlation":5} 
Method       =  Dico_Method[method] 

# Initialise time
ListTime = []

# Initialise list of templates (rather than opening them for every image iteration) 
List_Template = [ IJ.openImage( templ_file.getPath() ) for templ_file in template_files ]  



### Search ROI ? ###
# Check if there is a searchRoi
if roi_file:
	from ij.io import RoiDecoder
	searchRoi = RoiDecoder.open(roi_file.getPath())
else:
	searchRoi = None

# Check if it is a rectangular one
if searchRoi and searchRoi.getTypeAsString()=="Rectangle": 
	Bool_SearchRoi = True
else: 
	Bool_SearchRoi = False

# Define Offset for BBox if proper searchRoi
if Bool_SearchRoi:
	dX = searchRoi.getXBase()
	dY = searchRoi.getYBase()
else:
	dX = dY = 0
	
	
	

#nROI = 0 
## Loop over templates for template matching and maxima detection 
for i, im_file in enumerate(image_files): 
		
	# Get the current image 
	PathIm   = im_file.getPath() 
	ImpImage = IJ.openImage(PathIm) 
	ImName = ImpImage.getTitle() 
	ImProc = ImpImage.getProcessor().duplicate() 
	
	# Crop Image if searchRoi
	if Bool_SearchRoi:
		ImpImage.setRoi(searchRoi)
		ImpImage = ImpImage.crop()
	
	## Start Timer here (dont count opening of the image)
	Start = time.clock()
	
	# Initialise list before looping over templates 
	Hits_BeforeNMS = [] 
	 
	## Loop over template for matching against current image 
	for ImpTemplate in List_Template:
		
		# Check that template is smaller than the searched image
		if ImpTemplate.width>ImpImage.width or ImpTemplate.height>ImpImage.height:
			raise Exception('The current template is larger in width and/or height than the searched image')

		# Get hits for the current template (and his flipped and/or rotated versions) 
		List_Hit = getHit_Template(ImpTemplate, ImpImage, flipv, fliph, angles, Method, n_hit, score_threshold, tolerance) # raher use ImagePlus as input to get the name of the template used
		 
		# Store the hits 
		Hits_BeforeNMS.extend(List_Hit) 
	 
	 
	 
	### NMS ###
	print "\n-- Hits before NMS --\n", 
	for hit in Hits_BeforeNMS: print hit 
 
	# InterHit NMS if more than one hit 
	if Method in [0,1]:  
		Hits_AfterNMS = NMS(Hits_BeforeNMS, N=n_hit, maxOverlap=max_overlap, sortDescending=False) # only difference is the sorting 
 
	else: 
		Hits_AfterNMS = NMS(Hits_BeforeNMS, N=n_hit, maxOverlap=max_overlap, sortDescending=True) 
 
	print "\n-- Hits after NMS --\n" 
	for hit in Hits_AfterNMS : print hit
	
	
	
	## Stop time here (we dont benchmark display time)
	Stop = time.clock()
	Elapsed = Stop - Start # in seconds
	ListTime.append(Elapsed)
	 
	
	
	## Loop over final hits to generate ROI, result table... ###
	for hit in Hits_AfterNMS: 
		
		if Bool_SearchRoi: # Add the offset of the search ROI
			hit['BBox'] = (hit['BBox'][0]+dX, hit['BBox'][1]+dY, hit['BBox'][2], hit['BBox'][3])  
		 
		if show_roi: 
			roi = Roi(*hit['BBox']) 
			roi.setName(hit['TemplateName']) 
			roi.setPosition(i+1) # set slice position 
			#nROI+=1 
			rm.add(None, roi, i+1) # Trick to be able to set slice when less images than ROI. Here i is an digit index before the Roi Name 
			 
		if show_table: 
			Xcorner, Ycorner = hit['BBox'][0], hit['BBox'][1] 
			Xcenter, Ycenter = CornerToCenter(Xcorner, Ycorner, hit['BBox'][2], hit['BBox'][3]) 
			Dico = {'Image':ImName, 'Template':hit['TemplateName'] ,'Xcorner':Xcorner, 'Ycorner':Ycorner, 'Xcenter':Xcenter, 'Ycenter':Ycenter, 'Score':hit['Score']} 
			AddToTable(Table, Dico, Order=('Image', 'Template', 'Score', 'Xcorner', 'Ycorner', 'Xcenter', 'Ycenter')) 
			Table.show("Results")
	 
	 
	
	## Display outputs
	if show_images: 
	
		# Initialise a stack of proper size if not the case before
		if Stack_Image.getSize()==0:
			 Stack_Image = ImageStack(ImProc.width, ImProc.height) # instead of using ImagePlus.getStack otherwise we loose the slice title for the 1st image
		 
		# Add images to Stack
		Stack_Image.addSlice(ImName, ImProc)  
		Stack_Image_ImP.setStack("Result", Stack_Image)

		# Update display
		Stack_Image_ImP.setSlice(i)
		Stack_Image_ImP.show()

		if show_roi:
			# Show All ROI + Associate ROI to slices 
			rm.runCommand("Associate", "true")	
			rm.runCommand("Show All with labels")
	
for i in ListTime:
	print i