# coding=utf-8

from __future__ import division
import os
import cv2
import numpy as np
import sys
import pickle
from optparse import OptionParser
import time
from keras_frcnn import config
from keras import backend as K
from keras.layers import Input
from keras.models import Model
from keras_frcnn import roi_helpers

sys.setrecursionlimit(40000)

parser = OptionParser()

parser.add_option("-p", "--path", dest="test_path", help="Path to test data.")
parser.add_option("-n", "--num_rois", dest="num_rois",
                  help="Number of ROIs per iteration. Higher means more memory use.", default=32)
parser.add_option("--config_filename", dest="config_filename", help=
"Location to read the metadata related to the training (generated when training).",
                  default="config.pickle")
parser.add_option("--network", dest="network", help="Base network to use. Supports vgg or resnet50.",
                  default='resnet50')
parser.add_option("-d", "--defect", dest="defect", help="Expectation defect of data set.", default="none")

(options, args) = parser.parse_args()

if not options.test_path:  # if filename is not given
    parser.error('Error: path to test data must be specified. Pass --path to command line')

config_output_filename = options.config_filename

with open(config_output_filename, 'rb') as f_in:
    C = pickle.load(f_in)

if C.network == 'resnet50':
    import keras_frcnn.resnet as nn
elif C.network == 'vgg':
    import keras_frcnn.vgg as nn

# turn off any data augmentation at test time
C.use_horizontal_flips = False
C.use_vertical_flips = False
C.rot_90 = False

img_path = options.test_path


def format_img_size(img, C):
    """ formats the image size based on config """
    img_min_side = float(C.im_size)
    (height, width, _) = img.shape

    if width <= height:
        ratio = img_min_side / width
        new_height = int(ratio * height)
        new_width = int(img_min_side)
    else:
        ratio = img_min_side / height
        new_width = int(ratio * width)
        new_height = int(img_min_side)
    img = cv2.resize(img, (new_width, new_height), interpolation=cv2.INTER_CUBIC)
    return img, ratio


def format_img_channels(img, C):
    """ formats the image channels based on config """
    img = img[:, :, (2, 1, 0)]
    img = img.astype(np.float32)
    img[:, :, 0] -= C.img_channel_mean[0]
    img[:, :, 1] -= C.img_channel_mean[1]
    img[:, :, 2] -= C.img_channel_mean[2]
    img /= C.img_scaling_factor
    img = np.transpose(img, (2, 0, 1))
    img = np.expand_dims(img, axis=0)
    return img


def format_img(img, C):
    """ formats an image for model prediction based on config """
    img, ratio = format_img_size(img, C)
    img = format_img_channels(img, C)
    return img, ratio


# Method to transform the coordinates of the bounding box to its original size
def get_real_coordinates(ratio, x1, y1, x2, y2):
    real_x1 = int(round(x1 // ratio))
    real_y1 = int(round(y1 // ratio))
    real_x2 = int(round(x2 // ratio))
    real_y2 = int(round(y2 // ratio))

    return (real_x1, real_y1, real_x2, real_y2)


class_mapping = C.class_mapping

if 'bg' not in class_mapping:
    class_mapping['bg'] = len(class_mapping)

class_mapping = {v: k for k, v in class_mapping.items()}
print(class_mapping)
class_to_color = {class_mapping[v]: np.random.randint(0, 255, 3) for v in class_mapping}
C.num_rois = int(options.num_rois)

if C.network == 'resnet50':
    num_features = 1024
elif C.network == 'vgg':
    num_features = 512

if K.image_dim_ordering() == 'th':
    input_shape_img = (3, None, None)
    input_shape_features = (num_features, None, None)
else:
    input_shape_img = (None, None, 3)
    input_shape_features = (None, None, num_features)

img_input = Input(shape=input_shape_img)
roi_input = Input(shape=(C.num_rois, 4))
feature_map_input = Input(shape=input_shape_features)

# define the base network (resnet here, can be VGG, Inception, etc)
shared_layers = nn.nn_base(img_input, trainable=True)

# define the RPN, built on the base layers
num_anchors = len(C.anchor_box_scales) * len(C.anchor_box_ratios)
rpn_layers = nn.rpn(shared_layers, num_anchors)

# nn is resnet
classifier = nn.classifier(feature_map_input, roi_input, C.num_rois, nb_classes=len(class_mapping), trainable=True)

model_rpn = Model(img_input, rpn_layers)
model_classifier_only = Model([feature_map_input, roi_input], classifier)

model_classifier = Model([feature_map_input, roi_input], classifier)

print('Loading weights from {}'.format(C.model_path))
model_rpn.load_weights(C.model_path, by_name=True)
model_classifier.load_weights(C.model_path, by_name=True)

model_rpn.compile(optimizer='sgd', loss='mse')
model_classifier.compile(optimizer='sgd', loss='mse')

all_imgs = []

classes = {}

# origin is 0.8
bbox_threshold = 0.8

visualise = True

total = 0
exact_right_cnt = 0
having_right_cnt = 0
having_answer_cnt = 0

for idx, img_name in enumerate(sorted(os.listdir(img_path))):
    if not img_name.lower().endswith(('.bmp', '.jpeg', '.jpg', '.png', '.tif', '.tiff')):
        continue

    total += 1

    print('Index ' + str(idx) + ' : ' + img_name),
    st = time.time()
    file_path = os.path.join(img_path, img_name)

    img = cv2.imread(file_path)

    # img:(1200, 1600, 3) -> X:(600, 800, 3), ratio:0.5
    X, ratio = format_img(img, C)

    if K.image_dim_ordering() == 'tf':
        X = np.transpose(X, (0, 2, 3, 1))

    # get the feature maps and output from the RPN
    # Y1(rpn_layer):(1, 38, 50, 9), Y2(regr_layer):(1, 38, 50, 36), F(1, 38, 50, 1024)
    # F可能是Feature Maps
    [Y1, Y2, F] = model_rpn.predict(X)

    # R:(300, 4) 根据Y1得到一个概率留下前300个框
    R = roi_helpers.rpn_to_roi(Y1, Y2, C, K.image_dim_ordering(), overlap_thresh=0.7, file_path=file_path)

    # convert from (x1, y1, x2, y2) to (x, y, w, h)
    R[:, 2] -= R[:, 0]
    R[:, 3] -= R[:, 1]

    # apply the spatial pyramid pooling to the proposed regions
    bboxes = {}
    probs = {}

    for jk in range(R.shape[0] // C.num_rois + 1):
        # - ROIs:(1, 32, 4) 含义是分R为300 / 32 + 1 = 10组 每组32个ROI进行处理
        ROIs = np.expand_dims(R[C.num_rois * jk:C.num_rois * (jk + 1), :], axis=0)
        if ROIs.shape[1] == 0:
            break

        if jk == R.shape[0] // C.num_rois:
            # pad R
            curr_shape = ROIs.shape
            target_shape = (curr_shape[0], C.num_rois, curr_shape[2])
            ROIs_padded = np.zeros(target_shape).astype(ROIs.dtype)
            ROIs_padded[:, :curr_shape[1], :] = ROIs
            ROIs_padded[0, curr_shape[1]:, :] = ROIs[0, 0, :]
            ROIs = ROIs_padded

        # - P_cls:(1,32,5) P_regr:(1,32,16)
        [P_cls, P_regr] = model_classifier_only.predict([F, ROIs])

        for ii in range(P_cls.shape[1]):

            # - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! test ROI
            # cur_img = cv2.imread(file_path)
            # (x_test, y_test, w_test, h_test) = ROIs[0, ii, :]
            # x1_test = x_test * C.rpn_stride
            # y1_test = y_test * C.rpn_stride
            # x2_test = (x_test + w_test) * C.rpn_stride
            # y2_test = (y_test + h_test) * C.rpn_stride
            # (real_x1_test, real_y1_test, real_x2_test, real_y2_test) = get_real_coordinates(ratio, x1_test, y1_test,
            #                                                                                 x2_test, y2_test)
            # cv2.rectangle(cur_img, (real_x1_test, real_y1_test), (real_x2_test, real_y2_test), (255, 0, 0), 4)
            # textLabel = '{} - {} - {} - {} - {}'.format(int(100 * P_cls[0, ii, 0]), int(100 * P_cls[0, ii, 1]), int(100 * P_cls[0, ii, 2]), int(100 * P_cls[0, ii, 3]), int(100 * P_cls[0, ii, 4]))
            # cv2.putText(cur_img, textLabel, (real_x1_test, real_y1_test + 20), cv2.FONT_HERSHEY_DUPLEX, 0.5, (0, 0, 255), 1)
            # if int(P_cls[0, ii, 0] * 100) > 0 or int(P_cls[0, ii, 1] * 100) > 0 or int(P_cls[0, ii, 2] * 100) > 0 or int(P_cls[0, ii, 3] * 100) > 0:
            #     cv2.imwrite('./test_results/R_{}_{}_sp.jpg'.format(jk, ii), cur_img)
            #     print 'R_{}_{}_sp.jpg'.format(jk, ii)
            # else:
            #     cv2.imwrite('./test_results/R_{}_{}.jpg'.format(jk, ii), cur_img)
            # continue

            if np.max(P_cls[0, ii, :]) < bbox_threshold or np.argmax(P_cls[0, ii, :]) == (P_cls.shape[2] - 1):
                continue

            cls_name = class_mapping[np.argmax(P_cls[0, ii, :])]

            if cls_name not in bboxes:
                bboxes[cls_name] = []
                probs[cls_name] = []

            (x, y, w, h) = ROIs[0, ii, :]

            cls_num = np.argmax(P_cls[0, ii, :])
            try:
                (tx, ty, tw, th) = P_regr[0, ii, 4 * cls_num:4 * (cls_num + 1)]
                tx /= C.classifier_regr_std[0]
                ty /= C.classifier_regr_std[1]
                tw /= C.classifier_regr_std[2]
                th /= C.classifier_regr_std[3]
                x, y, w, h = roi_helpers.apply_regr(x, y, w, h, tx, ty, tw, th)
            except:
                pass
            bboxes[cls_name].append(
                [C.rpn_stride * x, C.rpn_stride * y, C.rpn_stride * (x + w), C.rpn_stride * (y + h)])
            probs[cls_name].append(np.max(P_cls[0, ii, :]))

    # - !!!!!!!!!!!!!!测试用
    # break

    all_dets = []
    isRight = True
    hasRightAnswer = False

    for key in bboxes:
        bbox = np.array(bboxes[key])

        new_boxes, new_probs = roi_helpers.non_max_suppression_fast(bbox, np.array(probs[key]), overlap_thresh=0.5)
        for jk in range(new_boxes.shape[0]):
            (x1, y1, x2, y2) = new_boxes[jk, :]

            # 0.5的映射关系
            (real_x1, real_y1, real_x2, real_y2) = get_real_coordinates(ratio, x1, y1, x2, y2)

            cv2.rectangle(img, (real_x1, real_y1), (real_x2, real_y2),
                          (int(class_to_color[key][0]), int(class_to_color[key][1]), int(class_to_color[key][2])), 2)

            textLabel = '{}: {}'.format(key, int(100 * new_probs[jk]))

            if options.defect != 'none' and options.defect != key:
                isRight = False

            if options.defect != 'none' and options.defect == key:
                hasRightAnswer = True

            all_dets.append((key, 100 * new_probs[jk]))

            (retval, baseLine) = cv2.getTextSize(textLabel, cv2.FONT_HERSHEY_COMPLEX, 1, 1)
            textOrg = (real_x1, real_y1 - 0)

            # green
            cv2.rectangle(img, (textOrg[0] - 5, textOrg[1] + baseLine - 5),
                          (textOrg[0] + retval[0] + 5, textOrg[1] - retval[1] - 5), (0, 255, 0), 2)

            # red
            cv2.rectangle(img, (textOrg[0] - 5, textOrg[1] + baseLine - 5),
                          (textOrg[0] + retval[0] + 5, textOrg[1] - retval[1] - 5), (0, 0, 255), -1)

            # blue
            cv2.putText(img, textLabel, textOrg, cv2.FONT_HERSHEY_DUPLEX, 1, (255, 0, 0), 1)

    if len(all_dets) != 0:
        print(all_dets),

        having_answer_cnt += 1

        if isRight:
            exact_right_cnt += 1

        if hasRightAnswer:
            having_right_cnt += 1

        # cv2.imshow('img', img)
        # cv2.waitKey(0)
        # removed png
        cv2.imwrite('./test_results/{}_{}'.format(options.defect, img_name), img)

    print ''
    print 'Rate of having answer = ' + str(having_answer_cnt / float(total))
    print 'Rate of having right answer = ' + str(having_right_cnt / float(total))
    print 'Acc = ' + str(exact_right_cnt / float(total))
    print('Elapsed time = {}'.format(time.time() - st))

print ''
print 'Total = ' + str(total)
print 'Having Answer Count = ' + str(having_answer_cnt)
print 'Having Right Answer Count = ' + str(having_right_cnt)
print 'Exact Right Count = ' + str(exact_right_cnt)
