def main():
    """Main function for the program.  Everything starts here.

    :return: None
    """

    global quit_flag, pause_flag, total_paused_time

    if (handle_args() != True):
        print_usage()
        exit()

    print_arg_vals()
    # Read in the labels and get the number of ncs devices
    labels_list = read_labels(classes_filename)
    num_ncs_devs = number_of_devices
    
    # calculate numberof inferences that need to be made per thread/device
    inferences_per_thread = int(number_of_inferences / ((threads_per_dev * num_ncs_devs)))
    inferences_per_thread = int(inferences_per_thread / simultaneous_infer_per_thread) * simultaneous_infer_per_thread
    total_number_threads = num_ncs_devs * threads_per_dev

    infer_result_queue = queue.Queue(INFER_RES_QUEUE_SIZE)
    # create a thread list and a results time list to track fps
    result_times_list = [None] * (num_ncs_devs * threads_per_dev)
    thread_list = [None] * (num_ncs_devs * threads_per_dev)
    # barrier to sync all threads
    start_barrier = threading.Barrier(num_ncs_devs*threads_per_dev+1)
    end_barrier = threading.Barrier(num_ncs_devs*threads_per_dev+1)

    canvas_width = display_image_width
    canvas_height = display_image_height
    canvas_image = numpy.zeros((canvas_height, canvas_width, 3), numpy.uint8)
    # OpenCV window initialization
    cv2.namedWindow(cv_window_name)
    cv2.moveWindow(cv_window_name, 10,  10)
    cv2.waitKey(1)
    put_text_on_image(canvas_image, "Loading Images...", 0, int(canvas_height/2) - 20, canvas_width)
    cv2.imshow(cv_window_name, canvas_image)
    cv2.waitKey(1)

    total_paused_time = 0.0

    # load a single plugin for the application
    ie = IECore()
    net = IENetwork(model=model_xml_fullpath, weights=model_bin_fullpath)
    input_blob = next(iter(net.inputs))
    output_blob = next(iter(net.outputs))

    n, c, h, w = net.inputs[input_blob].shape

    # get list of all the .mp4 files in the image directory
    image_filename_list = os.listdir(image_dir)
    image_filename_list = [image_dir + sep + i for i in image_filename_list if (i.endswith('.jpg') or i.endswith(".png"))]
    if (len(image_filename_list) < 1):
        # no images to show
        print('No image files found (.jpg or .png)')
        return 1

    print("Found " + str(len(image_filename_list)) + " images.")
    
    # resizing all of the images in the image list
    display_image_list = [None]*len(image_filename_list)
    preprocessed_image_list = [None]*len(image_filename_list)
    preprocessed_image_index = 0
    for one_image_filename in image_filename_list:
        display_image_list[preprocessed_image_index] = cv2.imread(one_image_filename)
        display_image_list[preprocessed_image_index] = cv2.resize(display_image_list[preprocessed_image_index], (display_image_width, display_image_height))
        one_preprocessed_image = preprocess_image(n,c,h,w,one_image_filename)
        preprocessed_image_list[preprocessed_image_index] = one_preprocessed_image
        preprocessed_image_index += 1
        cv2.waitKey(1)

    images_per_thread = int(len(preprocessed_image_list) / total_number_threads)

    exec_net_list = [None] * num_ncs_devs

    # Load the network for each device and
    for dev_index in range(0, num_ncs_devs):
        # create one executable network for each device in the system
        # create 4 requests for each executable network, two for each NCE
        exec_net_list[dev_index] = ie.load_network(network=net, num_requests=threads_per_dev*simultaneous_infer_per_thread, device_name = inference_device)

        # create threads for each executable network (one executable network per device)
        for dev_thread_index in range(0,threads_per_dev):
            total_thread_index = dev_thread_index + (threads_per_dev*dev_index)
            first_image_index = int(total_thread_index*images_per_thread)
            last_image_index = int(first_image_index + images_per_thread - 1)

            print("first image index: " + str(first_image_index) + "  last_image_index: " + str(last_image_index))
            # Threads will run async inference once they start
            if (run_async):
                thread_list[total_thread_index] = threading.Thread(target=infer_async_thread_proc,
                                                                              args=[exec_net_list[dev_index], dev_thread_index*simultaneous_infer_per_thread,
                                                                                    preprocessed_image_list, image_filename_list, display_image_list,
                                                                                    first_image_index, last_image_index,
                                                                                    inferences_per_thread,
                                                                                    result_times_list, total_thread_index,
                                                                                    start_barrier, end_barrier, simultaneous_infer_per_thread,
                                                                                    infer_result_queue, input_blob, output_blob])
            else:
                pass

    del net

    #start the threads
    for one_thread in thread_list:
        one_thread.start()

    start_barrier.wait()

    # save the main starting time
    main_start_time = time.time()
    interval_start_time = time.time()

    print("Inferences started.")

    cur_fps = 0.0
    result_counter = 0
    accum_fps = 0.0
    frames_since_last_report = 0

    # track the inference results and display the images
    while (result_counter < total_number_threads * inferences_per_thread):
        infer_res = infer_result_queue.get(True, QUEUE_WAIT_SECONDS)
        if (infer_result_queue.qsize() > (frames_to_skip)):
            for extra_res_counter in range(0, frames_to_skip):
                infer_result_queue.task_done()
                result_counter += 1
                frames_since_last_report += 1
                infer_res = infer_result_queue.get(True, QUEUE_WAIT_SECONDS)

        infer_res_filename = infer_res[0]
        infer_res_index = infer_res[1]
        infer_res_probability = infer_res[2]
        infer_res_display_image = infer_res[3]

        fps_to_use = accum_fps
        if (use_interval_fps):
            fps_to_use = cur_fps

        overlay_on_image(infer_res_display_image, labels_list[infer_res_index], fps_to_use)
        # Display image in the window
        cv2.imshow(cv_window_name, infer_res_display_image)
        
        # Handle key events
        raw_key = cv2.waitKey(1)
        if (raw_key != -1 or quit_flag):
            if (handle_keys(raw_key) == False or quit_flag):
                print("Quitting application")
                quit_flag = True
                pause_flag = False
                exception_count = 0
                max_exceptions = 50000
                while(end_barrier.n_waiting < total_number_threads) :
                    try:
                        infer_res = infer_result_queue.get_nowait()
                    except:
                        exception_count += 1
                    if (exception_count >= max_exceptions):
                        break
                print("Exception Count: " + str(exception_count))
                if (exception_count >= max_exceptions):
                    print("Gave up waiting for threads to finish, exiting!")
                    cv2.destroyAllWindows()
                    exit(-1)
                break
            if (pause_flag):
                pause_start_time = time.time()
                while(pause_flag and not quit_flag):
                    raw_key = cv2.waitKey(1)
                    if (raw_key != -1):
                        if (handle_keys(raw_key) == False):
                            quit_flag = True
                pause_end_time = time.time()
                total_paused_time += (pause_end_time - pause_start_time)

        # Report the fps results in intervals
        result_counter += 1
        frames_since_last_report += 1
        if (report_interval > 0):
            if ((frames_since_last_report > report_interval)):
                cur_time = time.time()
                accum_duration = cur_time - main_start_time
                cur_duration = cur_time - interval_start_time

                cur_fps = frames_since_last_report / cur_duration
                accum_fps = result_counter / (accum_duration - total_paused_time)
                print(" " + str(result_counter) + " inferences completed. FPS: " + str("{0:.2f}".format(accum_fps)))
                frames_since_last_report = 0
                interval_start_time = time.time()

        infer_result_queue.task_done()

    # wait for all the inference threads to reach end barrier
    print("main end barrier reached")
    end_barrier.wait()

    # save main end time
    main_end_time = time.time()
    print("Inferences finished.")

    # wait for threads to finish
    for one_thread in thread_list:
        one_thread.join()

    total_thread_fps = 0.0
    total_thread_time = 0.0
    # Calculate overall fps
    for thread_index in range(0, (num_ncs_devs*threads_per_dev)):
        total_thread_time += (result_times_list[thread_index] - total_paused_time)
        total_thread_fps += (inferences_per_thread / (result_times_list[thread_index] - total_paused_time))

    if (quit_flag):
        # adjust the thread fps since didn't do every inference.
        total_thread_fps = result_counter / (total_thread_time / total_number_threads)

    devices_count = str(number_of_devices)

    if (time_threads):
        print("\n------------------- Thread timing -----------------------")
        print("--- Device: " + str(inference_device))
        print("--- Model:  " + model_xml_fullpath)
        print("--- Total FPS: " + '{0:.1f}'.format(total_thread_fps))
        print("--- FPS per device: " + '{0:.1f}'.format(total_thread_fps / num_ncs_devs))
        print("---------------------------------------------------------")

    main_time = (main_end_time - main_start_time) - total_paused_time

    if (time_main):
        main_fps = result_counter / main_time
        print ("\n------------------ Main timing -------------------------")
        print ("--- FPS: " + str(main_fps))
        print ("--- FPS per device: " + str(main_fps/num_ncs_devs))
        print ("--------------------------------------------------------")

    # clean up
    for one_exec_net in exec_net_list:
        del one_exec_net
Main function for the program.  Everything starts here.

    :return: None
    
apps/classifier_flash/classifier_flash.py