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

    :return: None
    """

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

    print_arg_vals()

    num_ncs_devs = number_of_devices

    # Calculate the number of number of inferences to be made per thread
    total_number_of_threads = threads_per_dev * num_ncs_devs   
    inferences_per_thread = int(number_of_inferences / total_number_of_threads)
    # This total will be the total number of inferences to be made
    inferences_per_thread = int(inferences_per_thread / simultaneous_infer_per_thread) * simultaneous_infer_per_thread
    
    # Total number of threads that need to be spawned
    total_number_threads = num_ncs_devs * threads_per_dev
    
    # Lists and queues to hold data
    infer_result_queue = queue.Queue(INFER_RES_QUEUE_SIZE)
    infer_time_list = [None] * (total_number_threads)
    thread_list = [None] * (total_number_threads)
    
    # Threading barrier to sync all thread processing
    start_barrier = threading.Barrier(total_number_threads + 1)
    end_barrier = threading.Barrier(total_number_threads + 1)

    ie = IECore()
    
    # Create the network object 
    net = IENetwork(model=model_xml_fullpath, weights=model_bin_fullpath)
    # Get the input and output blob names and the network input information
    input_blob = next(iter(net.inputs))
    output_blob = next(iter(net.outputs))
    n, c, h, w = net.inputs[input_blob].shape

    # Get a list of all the .mp4 files in the image directory and put them in a list
    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.")

    # Preprocess all images in the list
    preprocessed_image_list = [None]*len(image_filename_list)
    preprocessed_image_index = 0
    for one_image_filename in image_filename_list:
        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

    # Number of images to be inferred per thread
    images_per_thread = int(len(preprocessed_image_list) / total_number_threads)
    
    exec_net_list = [None] * num_ncs_devs

    # creates an executable network for each device
    for dev_index in range(0, num_ncs_devs):
        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 (3) for each executable network (one executable network per device)
        for dev_thread_index in range(0, threads_per_dev):
            # divide up the images to be processed by each thread
            # device thread index starts at 0. device indexes start at 0. 3 threads per device. (0, 1, 2)
            total_thread_index = dev_thread_index + (threads_per_dev * dev_index)
            # Find out which index in preprocessed_image_list to start from
            first_image_index = int(total_thread_index * images_per_thread)
            # Find out which index in preprocessed_image_list to stop at
            last_image_index = int(first_image_index + images_per_thread - 1)

            if (run_async):
                dev_thread_req_id = dev_thread_index * simultaneous_infer_per_thread
                thread_list[total_thread_index] = threading.Thread(target=infer_async_thread_proc,
                                                                              args=[net, exec_net_list[dev_index], dev_thread_req_id,
                                                                                    preprocessed_image_list,
                                                                                    first_image_index, last_image_index,
                                                                                    inferences_per_thread,
                                                                                    infer_time_list, total_thread_index,
                                                                                    start_barrier, end_barrier, simultaneous_infer_per_thread,
                                                                                    infer_result_queue, input_blob, output_blob], daemon = True)
            else:
                print("run_async=false not yet supported")
                exit(-1)

    del net

    # Start the threads
    try:
        for one_thread in thread_list:
            one_thread.start()
    except (KeyboardInterrupt, SystemExit):
        cleanup_stop_thread()
        sys.exit()
        
    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
    total_number_inferences = total_number_threads * inferences_per_thread
    
    # Report intermediate results
    while (result_counter < total_number_inferences):
        # Get the number of completed inferences
        infer_res = infer_result_queue.get(True, QUEUE_WAIT_SECONDS)

        infer_res_index = infer_res[0]
        infer_res_probability = infer_res[1]

        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
                print(str(result_counter) + " inferences completed. Current fps: " + '{0:.1f}'.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 all threads to finish
    for one_thread in thread_list:
        one_thread.join()

    total_thread_fps = 0.0
    total_thread_time = 0.0
    # Calculate total time and fps
    for thread_index in range(0, (num_ncs_devs*threads_per_dev)):
        total_thread_time += infer_time_list[thread_index]
        total_thread_fps += (inferences_per_thread / infer_time_list[thread_index])

    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

    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/benchmark_ncs/benchmark_ncs.py