import glob
import os 
from time import time
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
 
# load test image
def load_images():
	my_path = glob.glob("sign_photos/noc/hlavni_silnice/*.jpg")
	images = []
	for img in my_path:
		# read image
		img_read = cv.imread(img)
		# save image to vector
		images.append(img_read)
	return(images)

# load templates
def load_templates(shape):
	folder = shape
	my_path = glob.glob(folder)
	for template in my_path:
		templates = []
		for filename in os.listdir(folder):
			# read template
			template = cv.imread(os.path.join(folder,filename))
			if template is not None:
				filename = filename.replace('.jpg', '')
				filename = filename.replace('_', ' ')
				filename = filename.replace('2', '')
				# save template with its name
				template = [template, filename]
				templates.append(template)
	return(templates)
	
	
	
# edges detection	
def edge_detection(image, filter_method, sigma, threshold_value):	
	# filter image with different method
	if filter_method == 'blur':
		filtered_image = cv.blur(image, (3, 3))
	elif filter_method == 'GaussianBlur':
		filtered_image = cv.GaussianBlur(image, (3, 3), 0)
	elif filter_method == 'medianBlur':
		filtered_image = cv.medianBlur(image, 3)
	elif filter_method == 'bilateralFilter':
		filtered_image = cv.bilateralFilter(image, 3, 175, 175)
	else:
		filtered_image = image.copy()
	
	# get threshold values
	if threshold_value == 'median':
		value = np.median(image)
	elif threshold_value == 'mean':
		value = np.mean(image)
	else:
		value = eval(threshold_value)
	print(value, sigma)
	
	# lower and upper threshold values
	threshold = auto_canny(sigma, value)
	
	# edge detection							   lower_value	 upper_value
	edge_detected_image = cv.Canny(filtered_image, threshold[0], threshold[1])
	
	# find edge contours
	_, contours, _= cv.findContours(edge_detected_image, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
	
	coordinates = []
	for contour in contours:
		# ignore too small and large area of contours
		h, w = image.shape
		image_area = w*h
		contour_area = cv.contourArea(contour)
		
		if (contour_area < image_area * 0.0005) | (contour_area > image_area * 0.02):
			continue
			
		# approximate contours 
		approx = cv.approxPolyDP(contour, 0.018 * cv.arcLength(contour, True), True)
		
		# find geometry of detected contours
		if len(approx) == 3:
			geometry = 'triangle'
		elif len(approx) == 4:
			geometry = 'square'
		elif len(approx) > 8:
			geometry = 'circle'
		else:
			geometry = ''
		
		# find coordinates of detected geometry
		if geometry:
			coordinate = cv.boundingRect(contour)	# coordinates = [x, y, w, h]
			coordinate = [coordinate, geometry]
			coordinates.append(coordinate)
		
	return(coordinates)

# auto Canny threshold values
def auto_canny(sigma, value):
	
	# lower and upper threshold values
	lower = int(max(0, (1.0 - sigma) * value))
	upper = int(min(255, (1.0 + sigma) * value))
	
	return(lower, upper)




# shape detection	
def shape_detection(image, thresh_method, thresh_value):
	value = eval(thresh_value)
	
	# thresholding
	if thresh_value in ['cv.THRESH_OTSU', 'cv.THRESH_TRIANGLE']:
		thresh, thresh_image = cv.threshold(image, 0, 255, thresh_method + value)		# get auto thrash value (otsu, triangle)
	else:
		thresh, thresh_image = cv.threshold(image, value, 255, thresh_method)			# user thrash value
	
	# find contours
	_, contours, _ = cv.findContours(thresh_image, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
	
	coordinates = []
	for contour in contours:
		# ignore too small and large area of contours
		h, w = image.shape
		image_area = w*h
		contour_area = cv.contourArea(contour)
		if (contour_area < image_area * 0.0005) | (contour_area > image_area * 0.02):
			continue
		
		# approximate contours 
		approx = cv.approxPolyDP(contour, 0.018 * cv.arcLength(contour, True), True)
		
		# find geometry of detected contours
		if len(approx) == 3:
			geometry = 'triangle'
		elif len(approx) == 4:
			geometry = 'square'
		elif len(approx) > 8:
			geometry = 'circle'
		else:
			geometry = ''
		
		# find coordinates of detected geometry
		if geometry:
			coordinate = cv.boundingRect(contour)	# coordinates = [x, y, w, h]
			coordinate = [coordinate, geometry]
			coordinates.append(coordinate)
		
	return(coordinates)
	
# convert color to print
def convert_color(image):
	RGB_image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
	return(RGB_image)				

# print results
def print_images(RGB_image, matchTemplate_method, recognized_sign_title):
	plt.figure('Match template recognition')
	plt.imshow(RGB_image)
	plt.title(recognized_sign_title), plt.xticks([]), plt.yticks([])
	plt.suptitle(matchTemplate_method)
	plt.show()	


# main

# thresh_methods
# thresh_methods = ['cv.THRESH_BINARY', 'cv.THRESH_BINARY_INV', 'cv.THRESH_TOZERO', 'cv.THRESH_TOZERO_INV']
# thresh_values = ['15', '30', '45', '60', '75', '90', '105', '120', 'cv.THRESH_OTSU', 'cv.THRESH_TRIANGLE']

#filter_methods = ['without_filter', 'blur', 'GaussianBlur', 'medianBlur', 'bilateralFilter']#  
#threshold_values = ['15', '30', '60', '120', '180', 'median', 'mean']  #   

# match template methods
#matchTemplate_methods = ['cv.TM_CCOEFF', 'cv.TM_CCORR', 'cv.TM_SQDIFF']
matchTemplate_methods = ['cv.TM_CCOEFF_NORMED', 'cv.TM_CCORR_NORMED', 'cv.TM_SQDIFF_NORMED']# 

images = load_images()
for image in images:
	# save image in new variable
	matched_sign_image = image.copy()
	
	# thresh configuration
	thresh_method = 'cv.THRESH_BINARY_INV'
	method = eval(thresh_method)
	thresh_value = '68'#'105'
	
	# edge configuration
	filter_method = 'bilateralFilter'
	sigma = 0.3
	thresh_value = '60'
	
	# convert image color to gray
	gray_img = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
	image = gray_img																# convert image to gray
	
	# detect signs
	#signs_coordinates = shape_detection(gray_img, method, thresh_value)	
	signs_coordinates = edge_detection(gray_img, method, sigma, thresh_value)	#image, filter_method, sigma, threshold_value

	# match template methods testing
	for matchTemplate_method in matchTemplate_methods:
		method = eval(matchTemplate_method)
		
		# save image in new variable
		new_matched_sign_image = matched_sign_image.copy()
		
		# measure time
		start = time()
		
		recognized_sign_title = 'Nelze rozpoznat'
		
		for signs_coordinate_values in signs_coordinates:
			# get sign coordinates
			signs_coordinate = signs_coordinate_values[0]
			# get sign geometry
			geometry = signs_coordinate_values[1]
			
			# load templates with detected geometry
			templates = load_templates(geometry)
			
			# detected image values
			x, y, w, h = signs_coordinate
			cv.rectangle(new_matched_sign_image, (x, y), (x+w, y+h), (255, 0, 255), 3)	# print detected sign
			# load detected sign
			detected_sign = image[y:y+h, x:x+w]
			
			# reset values
			best_corr = 0.9
			recognized_sign_name = 'Nelze rozpoznat'
			control = False
			
			for template_values in templates:
				template = template_values[0]
				template = cv.cvtColor(template, cv.COLOR_BGR2GRAY)					# covert template to gray
				template_name = template_values[1]
				
				# resize detected image
				temp_size = template.shape
				w_template = temp_size[1]
				h_template = temp_size[0]				
				detected_sign = cv.resize(detected_sign, (w_template, h_template))
				
				# get result of actual method
				res = cv.matchTemplate(detected_sign, template, method)
				min_corr, max_corr, min_loc, max_loc = cv.minMaxLoc(res)
				
				# if max correlation value is more then 0.6...sign recognized
				if max_corr > best_corr:
					best_corr = max_corr
					control = True
					recognized_sign_name = template_name
				
			if control == True:
				# show recognized sign 
				print('Detekovana_znacka = ', recognized_sign_name)
				print('Nejlepsi korelacni hodnota = ', best_corr)
				cv.putText(new_matched_sign_image, recognized_sign_name, (x, y-10), cv.FONT_ITALIC, 1, (0, 255, 255), 2, cv.LINE_4)
				cv.rectangle(new_matched_sign_image, (x, y), (x+w, y+h), (0, 255, 255), 3)	# print recognized sign
				recognized_sign_title = 'Detekovane znacky'
							
		print('cas vypoctu [ms] = ', (time()-start)*1000)
		RGB_image = convert_color(new_matched_sign_image)
		print_images(RGB_image, matchTemplate_method, recognized_sign_title)
				


	
	
	

