Ejemplo n.º 1
0
 def voxel_matches_polygon(self, coordinate_list):
     for voxel_coords in coordinate_list:
         voxel_coords = np.asarray(voxel_coords)
         rmax = voxel_coords[:, 0].max()
         rmin = voxel_coords[:, 0].min()
         zmax = voxel_coords[:, 1].max()
         zmin = voxel_coords[:, 1].min()
         router = 1.5 * rmax
         rinner = 0.5 * rmin
         zupper = 1.5 * zmax if zmax > 0 else 0.5 * zmax
         zlower = 0.5 * zmin if zmin > 0 else 1.5 * zmin
         test_rs = np.linspace(rinner, router, int(50 * (router - rinner)))
         test_zs = np.linspace(zlower, zupper, int(50 * (zupper - zlower)))
         # Test for 0 area: not supported by mesh representation
         x, y = voxel_coords.T
         area = 0.5 * np.abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))
         if area == 0:
             continue
         voxel = AxisymmetricVoxel(voxel_coords, primitive_type='mesh')
         polygon = Polygon(voxel_coords, closed=True).get_path()
         test_verts = list(itertools.product(test_rs, test_zs))
         inside_poly = polygon.contains_points(test_verts)
         inside_voxel = [any(child.contains(Point3D(r, 0, z)) for child in voxel.children)
                         for (r, z) in test_verts]
         # Due to numerical precision, some points may be inside the
         # Matplotlib polygon but not the Mesh. Check in this case that the
         # "failing" points are just very close to the edge of the polygon
         fails = np.nonzero(np.not_equal(inside_voxel, inside_poly))[0]
         for fail in fails:
             if inside_voxel[fail] and not inside_poly[fail]:
                 # Polygon should be made slightly bigger
                 inside_poly[fail] = polygon.contains_point(test_verts[fail], radius=-0.01)
             elif inside_poly[fail] and not inside_voxel[fail]:
                 # Polygon should be made slightly smaller
                 inside_poly[fail] = polygon.contains_point(test_verts[fail], radius=0.01)
         self.assertSequenceEqual(inside_voxel, inside_poly.tolist(),
                                  "Failed for vertices {}".format(voxel_coords))
Ejemplo n.º 2
0
def find_glass_designation(nd, vd):
    """ find the designation and rgb color for the input index and V number

    Args:
        nd: refractive index, d line
        vd: V-number, d line

    Returns:
        designation label, RGBA list
    """
    for key, poly in polygons.items():
        p = Polygon(poly, closed=True)
        if p.contains_point([vd, nd]):
            c = rgb[key]
            return key, c
    return None, None
Ejemplo n.º 3
0
def convert_contour_data_to_roi_indices(contour_data,
                                        aff,
                                        shape,
                                        radius=None,
                                        use_contour_orientation=True):
    """Convert RTSTRUCT 2D polygon contour data to 3D indices

  Parameters
  ----------
  contour_data : list
    of contour data as returned from read_rtstruct_contour_data()

  aff: 2d 4x4 numpy array
    affine matrix that maps from voxel to world coordinates
    of volume where ROIs should be applied

  shape : 3 element tuple
    shape of the of volume where ROIs should be applied

  radius : float, optional
    passed to matplotlib.patches.Polygon.contains_point()
  
  use_contour_orientation: bool
    whether to use the orientation of a contour (clockwise vs counter clockwise)
    to determine whether a contour defines a ROI or a holes "within" an ROI.
    This approach is used by some vendors to store "holes" in 2D slices of 3D segmentations.

  Returns
  -------
  list
    containing the voxel indices of all ROIs

  Note
  ----
  (1) matplotlib.patches.Polygon.contains_point() is used to determine whether
  a voxel is inside a 2D RTSTRUCT polygon. There is ambiguity for voxels that only
  lie partly inside the polygon.
   
  Example
  -------
  dcm = pymirc.fileio.DicomVolume('mydcm_dir/*.dcm')
  vol = dcm.get_data()  

  contour_data = pymirc.fileio.read_rtstruct_contour_data('my_rtstruct_file.dcm')
  roi_inds     = pymirc.fileio.convert_contour_data_to_roi_indices(contour_data, dcm.affine, vol.hape)

  print('ROI name.....:', [x['ROIName']   for x in contour_data])
  print('ROI number...:', [x['ROINumber'] for x in contour_data])
  print('ROI mean.....:', [vol[x].mean()  for x in roi_inds])
  """
    roi_inds = []

    for iroi in range(len(contour_data)):
        contour_points = contour_data[iroi]['contour_points']
        contour_orientations = np.array(
            contour_data[iroi]['contour_orientations'])

        roi_number = int(contour_data[iroi]['Number'])

        roi_inds0 = []
        roi_inds1 = []
        roi_inds2 = []

        # calculate the slices of all contours
        sls = np.array([
            int(round(
                (np.linalg.inv(aff) @ np.concatenate([x[0, :], [1]]))[2]))
            for x in contour_points
        ])
        sls_uniq = np.unique(sls)

        for sl in sls_uniq:
            sl_inds = np.where(sls == sl)[0]

            if np.any(np.logical_not(contour_orientations[sl_inds])):
                # case where we have negative contours (holes) in the slices
                bin_img = np.zeros(shape[:2], dtype=np.int16)
                for ip in sl_inds:
                    cp = contour_points[ip]

                    # get the minimum and maximum voxel coordinate of the contour in the slice
                    i_min = np.floor((np.linalg.inv(aff) @ np.concatenate(
                        [cp.min(axis=0), [1]]))[:2]).astype(int)
                    i_max = np.ceil((np.linalg.inv(aff) @ np.concatenate(
                        [cp.max(axis=0), [1]]))[:2]).astype(int)

                    n_test = i_max + 1 - i_min

                    poly = Polygon(cp[:, :-1], True)

                    contour_orientation = contour_orientations[ip]

                    for i in np.arange(i_min[0], i_min[0] + n_test[0]):
                        for j in np.arange(i_min[1], i_min[1] + n_test[1]):
                            if poly.contains_point(
                                (aff @ np.array([i, j, sl, 1]))[:2],
                                    radius=radius):
                                if use_contour_orientation:
                                    if contour_orientation:
                                        bin_img[i, j] += 1
                                    else:
                                        bin_img[i, j] -= 1
                                else:
                                    bin_img[i, j] += 1
                inds0, inds1 = np.where(bin_img > 0)
                inds2 = np.repeat(sl, len(inds0))

                roi_inds0 = roi_inds0 + inds0.tolist()
                roi_inds1 = roi_inds1 + inds1.tolist()
                roi_inds2 = roi_inds2 + inds2.tolist()

            else:
                # case where we don't have negative contours (holes) in the slices
                for ip in sl_inds:
                    cp = contour_points[ip]

                    # get the minimum and maximum voxel coordinate of the contour in the slice
                    i_min = np.floor((np.linalg.inv(aff) @ np.concatenate(
                        [cp.min(axis=0), [1]]))[:2]).astype(int)
                    i_max = np.ceil((np.linalg.inv(aff) @ np.concatenate(
                        [cp.max(axis=0), [1]]))[:2]).astype(int)

                    n_test = i_max + 1 - i_min

                    poly = Polygon(cp[:, :-1], True)

                    for i in np.arange(i_min[0], i_min[0] + n_test[0]):
                        for j in np.arange(i_min[1], i_min[1] + n_test[1]):
                            if poly.contains_point(
                                (aff @ np.array([i, j, sl, 1]))[:2],
                                    radius=radius):
                                roi_inds0.append(i)
                                roi_inds1.append(j)
                                roi_inds2.append(sl)

        roi_inds.append(
            (np.array(roi_inds0), np.array(roi_inds1), np.array(roi_inds2)))

    return roi_inds
Ejemplo n.º 4
0
def is_point_in_poly(point_x, point_y, polygon: Polygon):
    """Returns True if received polygon object contains received point, False otherwise"""

    return polygon.contains_point([point_x, point_y])
Ejemplo n.º 5
0
def main():
    ok_txts_cnt = 0
    empty_txts_cnt = 0
    missed_txts_cnt = 0
    damaged_txts_cnt = 0
    regions_outside_cnt = 0  # total regions outside
    regions_outside_files_cnt = 0  # total files with regions outside

    # create output dir; nested dirs should be passed sequentially, each as separate parameter
    utility.create_directories(OUTPUT_DIR, OUTPUT_EMPTY_DIR,
                               OUTPUT_DAMAGED_DIR, OUTPUT_REGS_OUTSIDE_DIR)
    slash = utility.get_path_slash()

    # create images list
    img_files_paths = []
    for extension in IMAGES_EXTENSIONS:
        img_files_paths += glob.glob(INPUT_DIR + "*" + extension)

    # check if there any images
    if len(img_files_paths) == 0:
        input(
            INPUT_DIR +
            " contains no files (images) with specified in settings extensions. Press enter to exit:"
        )
        exit(0)

    print("Current progress print frequency is 1 per", PROGRESS_PRINT_RATE,
          "image(s).")
    print("Starting processing", len(img_files_paths), "images...")

    # do cropping and regions conversion for each image file
    for cur_file_no, in_img_path in enumerate(img_files_paths, start=1):
        # load image and do some pre calculations
        in_img = cv.imread(in_img_path)
        in_img_w, in_img_h = in_img.shape[1], in_img.shape[0]

        # make paths
        in_txt_path = in_img_path[:in_img_path.rfind(".")] + ".txt"

        out_img_path_ok = OUTPUT_DIR + in_img_path.split(slash)[-1]
        out_txt_path_ok = OUTPUT_DIR + in_txt_path.split(slash)[-1]

        out_img_path_empty = OUTPUT_EMPTY_DIR + in_img_path.split(slash)[-1]
        out_txt_path_empty = OUTPUT_EMPTY_DIR + in_txt_path.split(slash)[-1]

        out_img_path_damaged = OUTPUT_DAMAGED_DIR + in_img_path.split(
            slash)[-1]
        out_txt_path_damaged = OUTPUT_DAMAGED_DIR + in_txt_path.split(
            slash)[-1]

        out_img_path_reg_out = OUTPUT_REGS_OUTSIDE_DIR + in_img_path.split(
            slash)[-1]
        out_txt_path_reg_out = OUTPUT_REGS_OUTSIDE_DIR + in_txt_path.split(
            slash)[-1]

        # copy img and txt to output dir if img H==W
        if in_img_w == in_img_h:
            # check for txt availability
            if os.path.isfile(in_txt_path):
                # check for txt emptiness
                if len(load_regions(in_txt_path)) == 0:
                    empty_txts_cnt += 1
                    shutil.copyfile(in_img_path, out_img_path_empty)
                    shutil.copyfile(in_txt_path, out_txt_path_empty)
                # TODO: a bug: file is passed as ok if it is damaged (regions are present but len of any region is != 5)
                else:
                    ok_txts_cnt += 1
                    shutil.copyfile(in_img_path, out_img_path_ok)
                    shutil.copyfile(in_txt_path, out_txt_path_ok)
            else:
                missed_txts_cnt += 1
                shutil.copyfile(in_img_path, out_img_path_empty)
                with open(out_txt_path_empty, "w"):
                    pass
            continue

        # else process this image
        out_img_path_cur = out_img_path_ok
        out_txt_path_cur = out_txt_path_ok
        side_reduce_val = int(abs(in_img_w - in_img_h) / 2)

        if in_img_w > in_img_h:
            out_img = in_img[:, side_reduce_val:-side_reduce_val]
            out_img_rect_points = [[side_reduce_val, in_img_h],
                                   [side_reduce_val, 0],
                                   [in_img_w - side_reduce_val, 0],
                                   [in_img_w - side_reduce_val, in_img_h]]
        else:
            out_img = in_img[side_reduce_val:-side_reduce_val, :]
            out_img_rect_points = [[0, in_img_h - side_reduce_val],
                                   [0, side_reduce_val],
                                   [in_img_w, side_reduce_val],
                                   [in_img_w, in_img_h - side_reduce_val]]
        out_img_w, out_img_h = out_img.shape[1], out_img.shape[0]

        # check for regions txt file availability
        if not os.path.isfile(in_txt_path):
            missed_txts_cnt += 1
            cv.imwrite(out_img_path_empty, out_img)
            with open(out_txt_path_empty, "w"):
                pass
            continue

        # load regions and check is txt file empty
        in_regions = load_regions(in_txt_path)
        if len(in_regions) == 0:
            empty_txts_cnt += 1
            cv.imwrite(out_img_path_empty, out_img)
            shutil.copyfile(in_txt_path, out_txt_path_empty)
            continue

        out_regions = []
        out_img_rect = Polygon(out_img_rect_points)
        file_contains_out_regs = False

        # convert regions
        for in_region in in_regions:
            # [object_class, x_center, y_center, width, height]
            in_region_items = [float(item) for item in in_region.split(" ")]
            # check for region values count
            if len(in_region_items) != 5:
                damaged_txts_cnt += 1
                cv.imwrite(out_img_path_damaged, out_img)
                shutil.copyfile(in_txt_path, out_txt_path_damaged)
                break

            # convert to px (for input image size)
            in_reg_x_c_px, in_reg_y_c_px, in_reg_w_px, in_reg_h_px = int(in_region_items[1] * in_img_w), \
                                                                     int(in_region_items[2] * in_img_h), \
                                                                     int(in_region_items[3] * in_img_w), \
                                                                     int(in_region_items[4] * in_img_h)
            in_reg_left, in_reg_right, in_reg_top, in_reg_bot = int(in_reg_x_c_px - in_reg_w_px / 2), \
                                                                int(in_reg_x_c_px + in_reg_w_px / 2), \
                                                                int(in_reg_y_c_px - in_reg_h_px / 2), \
                                                                int(in_reg_y_c_px + in_reg_h_px / 2)
            in_reg_px_pts = [(in_reg_left, in_reg_bot),
                             (in_reg_left, in_reg_top),
                             (in_reg_right, in_reg_top),
                             (in_reg_right, in_reg_bot)]

            # check if px region is not outside cropped image borders
            for point in in_reg_px_pts:
                if not out_img_rect.contains_point(point):
                    if not file_contains_out_regs:
                        file_contains_out_regs = True
                        regions_outside_files_cnt += 1
                    regions_outside_cnt += 1
                    out_img_path_cur = out_img_path_reg_out
                    out_txt_path_cur = out_txt_path_reg_out
                    break
            else:
                # compute output px region
                if in_img_w > in_img_h:
                    out_reg_left, out_reg_right, out_reg_top, out_reg_bot = in_reg_left - side_reduce_val, \
                                                                            in_reg_right - side_reduce_val, \
                                                                            in_reg_top, in_reg_bot
                else:
                    out_reg_left, out_reg_right, out_reg_top, out_reg_bot = in_reg_left, in_reg_right, \
                                                                            in_reg_top - side_reduce_val, \
                                                                            in_reg_bot - side_reduce_val
                # no need to convert to int as these values will be converted to yolo format which are floats
                out_reg_x_c_px, out_reg_y_c_px = out_reg_left + in_reg_w_px / 2, out_reg_top + in_reg_h_px / 2

                # convert to yolo format
                x_center, y_center, width, height = round(out_reg_x_c_px / out_img_w, 6), \
                                                    round(out_reg_y_c_px / out_img_h, 6), \
                                                    round(in_reg_w_px / out_img_w, 6), \
                                                    round(in_reg_h_px / out_img_h, 6)
                # format as text: "object_class_index x_center y_center width height"
                converted_reg = in_region[0] + " " + str(x_center) + " " + str(y_center) + " " + str(width) + " " + \
                                str(height)

                out_regions.append(converted_reg)
        else:
            # save regions and image to defined during processing path
            if out_txt_path_cur == out_txt_path_ok:
                ok_txts_cnt += 1

            cv.imwrite(out_img_path_cur, out_img)
            with open(out_txt_path_cur, "w") as out_txt_file:
                for out_region in out_regions:
                    out_txt_file.write(out_region + "\n")

        # display progress
        if cur_file_no % PROGRESS_PRINT_RATE == 0:
            print("Processed", cur_file_no, "of", len(img_files_paths),
                  "images...")

    print("Processed all of", len(img_files_paths), "images.")
    print("Ok txts:", ok_txts_cnt, "(replaced to", OUTPUT_DIR, "directory)")
    print("Empty txts:", empty_txts_cnt, "(replaced to", OUTPUT_EMPTY_DIR,
          "directory)")
    print("Missed txts:", missed_txts_cnt, "(replaced to", OUTPUT_EMPTY_DIR,
          "directory)")
    print("Damaged txts:", damaged_txts_cnt, "(replaced to",
          OUTPUT_DAMAGED_DIR, "directory)")
    print("Files with regions outside:", regions_outside_files_cnt,
          "(replaced to", OUTPUT_REGS_OUTSIDE_DIR, "directory)")
    print("Regions outside count:", regions_outside_cnt)
    input("Done! Press enter to exit:")
Ejemplo n.º 6
0
def is_point_in_poly(point_x, point_y, polygon: Polygon):
    return polygon.contains_point([point_x, point_y])
Ejemplo n.º 7
0
def main():
    # check input dirs
    if not os.path.isfile(INPUT_JSON_FILE):
        print("Couldn't find json file:", INPUT_JSON_FILE)
        exit(0)
    if not os.path.isfile(INPUT_CLASSES_FILE_PATH):
        print("Couldn't find classes file:", INPUT_CLASSES_FILE_PATH)
        exit(0)
    if not os.path.exists(INPUT_DATASET_IMAGES_DIR):
        print("Couldn't find images directory:", INPUT_DATASET_IMAGES_DIR)
        exit(0)

    # define and create output dirs
    output_jpg_dir = OUTPUT_DATA_DIR + "jpg/"
    output_txt_dir = OUTPUT_DATA_DIR + "txt/"
    utility.create_directories(OUTPUT_DATA_DIR, output_jpg_dir, output_txt_dir)

    # statistics and report data
    img_processed = 0
    regions_processed = 0
    regions_converted = 0
    unsupported_regions_count = 0
    unsupported_regions_files = set()
    regions_outside_prec_area_count = 0
    regions_outside_prec_area_files = set()
    missing_dataset_images_count = 0
    missing_dataset_images_files = set()
    missing_json_region_types_count = 0
    missing_json_region_types_files = set()
    missing_classes_count = 0
    missing_classes = set()

    w_from = SCENE_CENTER_X - PRECISE_ZONE_LEFT
    w_to = SCENE_CENTER_X + PRECISE_ZONE_RIGHT
    h_from = SCENE_CENTER_Y - PRECISE_ZONE_TOP
    h_to = SCENE_CENTER_Y + PRECISE_ZONE_BOTTOM
    precise_zone = Polygon([[w_from, h_from], [w_to, h_from], [w_to, h_to],
                            [w_from, h_to]])

    output_jpg_dir = OUTPUT_DATA_DIR + "jpg/"

    # load json
    with open(INPUT_JSON_FILE, "r") as file:
        data = json.loads(file.read())

    # load classes
    classes = detection.YoloOpenCVDetection.load_class_names(
        INPUT_CLASSES_FILE_PATH)

    # loop over images list in json
    cur_file_no = 1
    for file_key in data["_via_img_metadata"]:
        if cur_file_no % 10 == 0:
            print("Processed", cur_file_no, "of",
                  len(data["_via_img_metadata"]), "files.")
        cur_file_no += 1

        file_name = data["_via_img_metadata"][file_key]["filename"]
        file_path = INPUT_DATASET_IMAGES_DIR + file_name

        # check if image jpg file exists and is not empty
        if os.path.isfile(file_path) and os.stat(file_path).st_size > 0:
            img = cv.imread(file_path)
            img_processed += 1
        else:
            missing_dataset_images_count += 1
            missing_dataset_images_files.add(file_name)
            continue

        reg_img_file_counter = 1

        # loop over regions of current image file
        for region in data["_via_img_metadata"][file_key]["regions"]:
            regions_processed += 1

            # check if region type is present
            if REGION_TYPE_KEY not in region["region_attributes"]:
                missing_json_region_types_count += 1
                missing_json_region_types_files.add(file_name)
                continue
            region_type = region["region_attributes"][REGION_TYPE_KEY]

            # regions whitelist check
            if APPLY_REGIONS_WHITE_LIST and region_type not in REGIONS_WHITE_LIST:
                continue

            # check if type is present in yolo names file
            if region_type not in classes:
                missing_classes_count += 1
                missing_classes.add(region_type)
                continue

            # get region info and convert to rect if needed
            if region["shape_attributes"]["name"] == "rect":
                crop_reg_left = int(region["shape_attributes"]["x"])
                crop_reg_right = int(region["shape_attributes"]["x"] +
                                     region["shape_attributes"]["width"])
                crop_reg_top = int(region["shape_attributes"]["y"])
                crop_reg_bottom = int(region["shape_attributes"]["y"] +
                                      region["shape_attributes"]["height"])
                reg_center_x = int(region["shape_attributes"]["x"] +
                                   region["shape_attributes"]["width"] / 2)
                reg_center_y = int(region["shape_attributes"]["y"] +
                                   region["shape_attributes"]["height"] / 2)
            elif region["shape_attributes"]["name"] == "circle":
                crop_reg_left = int(region["shape_attributes"]["cx"] -
                                    region["shape_attributes"]["r"])
                crop_reg_right = int(region["shape_attributes"]["cx"] +
                                     region["shape_attributes"]["r"])
                crop_reg_top = int(region["shape_attributes"]["cy"] -
                                   region["shape_attributes"]["r"])
                crop_reg_bottom = int(region["shape_attributes"]["cy"] +
                                      region["shape_attributes"]["r"])
                reg_center_x = int(region["shape_attributes"]["cx"])
                reg_center_y = int(region["shape_attributes"]["cy"])
            else:
                # print("Unsupported region shape '", region["shape_attributes"]["name"], "', THIS REGION WON'T BE SHIFTED. See info in file ", MISSED_REGIONS_FILE, sep="")
                unsupported_regions_count += 1
                unsupported_regions_files.add(file_name)
                continue

            # precise zone check
            if APPLY_PRECISE_ZONE and not precise_zone.contains_point(
                [reg_center_x, reg_center_y]):
                regions_outside_prec_area_count += 1
                regions_outside_prec_area_files.add(file_name)
                continue

            # reduce region sizes if allowed
            if APPLY_REGIONS_SIZE_REDUCING:
                yolo_reg_left = crop_reg_left + int(
                    crop_reg_left * REGIONS_SIZE_REDUCING_PERCENT / 2)
                yolo_reg_right = crop_reg_right - int(
                    crop_reg_right * REGIONS_SIZE_REDUCING_PERCENT / 2)
                yolo_reg_top = crop_reg_top + int(
                    crop_reg_top * REGIONS_SIZE_REDUCING_PERCENT / 2)
                yolo_reg_bottom = crop_reg_bottom - int(
                    crop_reg_bottom * REGIONS_SIZE_REDUCING_PERCENT / 2)
            else:
                yolo_reg_left = crop_reg_left
                yolo_reg_right = crop_reg_right
                yolo_reg_top = crop_reg_top
                yolo_reg_bottom = crop_reg_bottom

            # crop and save image region
            reg_img = img[crop_reg_top:crop_reg_bottom,
                          crop_reg_left:crop_reg_right]
            reg_img_name = file_name[:-4] + " " + str(
                reg_img_file_counter) + file_name[-4:]
            cv.imwrite(output_jpg_dir + reg_img_name, reg_img)

            # convert region to yolo format
            reg_center_x = int(
                (yolo_reg_right - yolo_reg_left) /
                2)  # TODO: a bug - not int not none happens here
            reg_center_y = int((yolo_reg_bottom - yolo_reg_top) / 2)
            reg_width = yolo_reg_right - yolo_reg_left
            reg_height = yolo_reg_bottom - yolo_reg_top
            img_x_size = crop_reg_right - crop_reg_left
            img_y_size = crop_reg_bottom - crop_reg_top

            obj_class = classes.index(region_type)
            x_center = round(reg_center_x / img_x_size, 6)
            y_center = round(reg_center_y / img_y_size, 6)
            width = round(reg_width / img_x_size, 6)
            height = round(reg_height / img_y_size, 6)

            # <object-class> <x_center> <y_center> <width> <height>
            record_line = str(obj_class) + " " + str(x_center) + " " + str(y_center) + " " + str(width) + " " + \
                          str(height) + "\n"

            # save converted to yolo region into txt
            with open(
                    output_txt_dir + file_name[:-4] + " " +
                    str(reg_img_file_counter) + ".txt", "w") as txt_file:
                txt_file.write(record_line)

            reg_img_file_counter += 1
            regions_converted += 1

    current_time = utility.get_current_time() + " "

    # save passed unsupported regions info
    if len(unsupported_regions_files) > 0:
        with open(current_time + UNSUPPORTED_REGIONS_LIST_FILE, "w") as file:
            file.write(
                "These images are present in VIA's json project but their regions types are not supported:\n"
            )
            for item in unsupported_regions_files:
                file.write(item + "\n")

    # save missing image files (present in json, absent in dir)
    if len(missing_dataset_images_files) > 0:
        with open(current_time + MISSING_DATASET_IMAGES_LIST_FILE,
                  "w") as file:
            file.write(
                "These images are present in VIA's json project but absent in given images directory (images are required for conversion to YOLO format):\n"
            )
            for item in missing_dataset_images_files:
                file.write(item + "\n")

    # save missing in json region types
    if len(missing_json_region_types_files) > 0:
        with open(current_time + MISSING_JSON_REGION_TYPES_LIST_FILE,
                  "w") as file:
            file.write(
                "These images are added to VIA's json project but their regions have no information about region types:\n"
            )
            for item in missing_json_region_types_files:
                file.write(item + "\n")

    # save missing classes in classes file
    if len(missing_classes) > 0:
        with open(current_time + MISSING_CLASSES_LIST_FILE, "w") as file:
            file.write(
                "These classes are present in given json, but absent in " +
                INPUT_CLASSES_FILE_PATH + " classes file:\n")
            for item in missing_classes:
                file.write(item + "\n")

    # save list of files with regions outside the precise area
    if len(regions_outside_prec_area_files) > 0:
        with open(current_time + REGIONS_OUTSIDE_PRECISE_ZONE_LIST_FILE,
                  "w") as file:
            file.write(
                "List of files whose regions were outside of the precise zone (it's ok):\n"
            )
            for item in regions_outside_prec_area_files:
                file.write(item + "\n")

    # show report
    print("Processed", img_processed, "images")
    print("Processed", regions_processed, "regions")
    print("Successfully converted", regions_converted, "regions")

    conversion_failed = False

    if unsupported_regions_count > 0:
        conversion_failed = True
        print("Passed", unsupported_regions_count,
              "regions due to unsupported shape. See'",
              UNSUPPORTED_REGIONS_LIST_FILE, "'file for details.")

    if missing_dataset_images_count > 0:
        conversion_failed = True
        print("Missing", missing_dataset_images_count,
              "images in given images directory. See'",
              MISSING_DATASET_IMAGES_LIST_FILE, "'file for details.")

    if missing_json_region_types_count > 0:
        conversion_failed = True
        print("Missing", missing_json_region_types_count,
              "regions types in given json file. See'",
              MISSING_JSON_REGION_TYPES_LIST_FILE, "'file for details.")

    if missing_classes_count > 0:
        conversion_failed = True
        print("Missing", missing_classes_count,
              "classes count in given classes file. See'",
              MISSING_CLASSES_LIST_FILE, "'file for details.")

    if regions_outside_prec_area_count > 0:
        print("Passed", regions_outside_prec_area_count,
              "regions which were outside of precise area (it's ok). See'",
              REGIONS_OUTSIDE_PRECISE_ZONE_LIST_FILE, "'file for details.")

    if conversion_failed:
        print(
            "Total: conversion is FAILED! See report files mentioned above for details."
        )
    else:
        print("Total: conversion is SUCCESSFUL!")