def test_identical_img_bboxes_recovered_using_csv_writer( tmp_path, sample_image, annot_loader, normalized, annot_writer): img, annotations = sample_image bboxes = annotations_to_numpy_array(annotations) with DirectoryWriter(directory=tmp_path, clean_directory=False) as image_writer, annot_writer( annotations_file=os.path.join( tmp_path, "annot.csv"), normalized=normalized) as annotation_writer: image_writer.write_image("image.jpg", img) annotation_writer.write_annotations_for_image("image.jpg", img, annotations) with DirectoryLoader(directory=tmp_path) as image_loader, annot_loader( annotations_file=os.path.join(tmp_path, "annot.csv"), normalized=normalized) as annotation_loader: images = annotation_loader.load_annotated_images(image_loader) _, loaded_annotations = images["image.jpg"] loaded_bboxes = annotations_to_numpy_array(loaded_annotations) assert np.allclose(bboxes, loaded_bboxes), "Annotations not the same after writing"
def test_yolokeras_loader(yolokeras_imageset, yolokeras_equiv_csv_imageset): for image_name in yolokeras_equiv_csv_imageset: _, yk_annotations = yolokeras_imageset[image_name] _, csv_annotations = yolokeras_equiv_csv_imageset[image_name] yk_bboxes = annotations_to_numpy_array(yk_annotations) csv_bboxes = annotations_to_numpy_array(csv_annotations) assert np.allclose( yk_bboxes, csv_bboxes ), "Same bboxes loaded with YOLOKeras, FourCornersCSV not equal." ""
def test_coco_loader(coco_imageset, coco_equiv_csv_imageset): for image_name in coco_equiv_csv_imageset: _, cc_annotations = coco_imageset[image_name] _, csv_annotations = coco_equiv_csv_imageset[image_name] cc_bboxes = annotations_to_numpy_array(cc_annotations) csv_bboxes = annotations_to_numpy_array(csv_annotations) assert np.allclose( cc_bboxes, csv_bboxes ), "Same bboxes loaded with COCO, FourCornersCSV not equal." ""
def test_pascalvoc_loader(pascalvoc_imageset, pascalvoc_equiv_csv_imageset): for image_name in pascalvoc_imageset: _, pv_annotations = pascalvoc_imageset[image_name] _, csv_annotations = pascalvoc_equiv_csv_imageset[image_name] pv_bboxes = annotations_to_numpy_array(pv_annotations) csv_bboxes = annotations_to_numpy_array(csv_annotations) assert np.allclose( pv_bboxes, csv_bboxes ), "Same bboxes loaded with PascalVOC, FourCornersCSV are not equal"
def test_run(snapshot, sample_query, tmp_path): random.seed(1) with open(os.path.join(tmp_path, "query.yml"), "w") as query_file: query_file.write(sample_query) main(['generate', os.path.join(tmp_path, "query.yml")]) os.remove(os.path.join(tmp_path, "query.yml")) if snapshot["update_snapshots"]: if os.path.isdir("./snapshots/run"): shutil.rmtree("./snapshots/run") shutil.copytree(tmp_path, "./snapshots/run") return with FourCornersCSV(annotations_file="./snapshots/run/aug_annotations.csv", normalized=True) as annotation_loader, Directory( directory="./snapshots/run") as image_loader: snapshot_images = annotation_loader.load_annotated_images(image_loader) with FourCornersCSV(annotations_file=os.path.join(tmp_path, "aug_annotations.csv"), normalized=True) as annotation_loader, Directory( directory=tmp_path) as image_loader: run_images = annotation_loader.load_annotated_images(image_loader) for image_name in snapshot_images: snapshot_image, snapshot_annot = snapshot_images[image_name] run_image, run_annot = run_images[image_name] snapshot_bboxes = annotations_to_numpy_array(snapshot_annot) run_bboxes = annotations_to_numpy_array(run_annot) assert np.allclose( snapshot_image, run_image), "Image for {} differs".format(image_name) assert np.allclose( snapshot_bboxes, run_bboxes), "Bounding boxes for {} differ".format(image_name) for image_name in run_images: if image_name not in snapshot_images: assert False, "Image {} not in snapshot".format(image_name)
def test_disco_constructed_augmentation_same_as_factory_constructed( augmentation_name_params, snapshot, tmp_path, sample_image): random.seed(1) if snapshot["update_snapshots"]: raise RuntimeError( "This test must be run after snapshots are generated") name, params = augmentation_name_params aug = getattr(disco, name)(**params) img, annotations = sample_image aug_img, aug_annotations = aug(img, annotations) save_image(os.path.join(tmp_path, "aug_image.jpg"), aug_img) aug_bboxes = annotations_to_numpy_array(aug_annotations) snapshot_bboxes = np.load( os.path.join("./snapshots/augmentations/{}-bboxes.npy".format(name))) assert np.array_equal( aug_bboxes, snapshot_bboxes), "Bounding boxes for {} do not match".format(name) assert filecmp.cmp(os.path.join(tmp_path, "aug_image.jpg"), "./snapshots/augmentations/{}-image.jpg".format( name)), "Images for {} do not match".format(name)
def test_my_augmentation_sepia(): img = np.array(([ [[ 62, 238, 229], [242, 39, 182]], [[167, 153, 41], [130, 63, 162]]])) annotations = [] # Values calculated by hand using formula from <https://www.techrepublic.com/blog/how-do-i/how-do-i-convert-images-to-grayscale-and-sepia-tone-using-c/> expected_red = np.array([[250, 159], [191, 130]]) expected_green = np.array([[223, 141], [170, 115]]) expected_blue = np.array([[173, 110], [132, 90]]) expected_aug_img = np.zeros(img.shape, dtype=np.uint8) expected_aug_img[:, :, 0] = expected_red expected_aug_img[:, :, 1] = expected_green expected_aug_img[:, :, 2] = expected_blue bboxes = annotations_to_numpy_array(annotations) augmentation = Sepia() aug_img, aug_bboxes = augmentation.augment(img.copy(), bboxes.copy()) assert np.allclose( expected_aug_img, aug_img ), "Performing augmentation does not yield expected image" assert np.array_equal( bboxes, aug_bboxes ), "Performing augmentation does not yield original augmentation"
def test_four_corners_width_height_equivalent(sample_image, sample_image_wh_bboxes): img_fc, annot_fc = sample_image img_wh, annot_wh = sample_image_wh_bboxes assert np.array_equal( img_fc, img_wh ), "Same image loaded with FourCornersCSV and WidthHeightCSV is not equal" bboxes_fc = annotations_to_numpy_array(annot_fc) bboxes_wh = annotations_to_numpy_array(annot_wh) assert np.allclose( bboxes_fc, bboxes_wh ), "Same bboxes loaded with FourCornersCSV, WidthHeightCSV are not equal"
def test_augmentation(augmentation, snapshot, tmp_path, sample_image): random.seed(1) img, annotations = sample_image bboxes = annotations_to_numpy_array(annotations) aug_img, aug_bboxes = augmentation.augment(img.copy(), bboxes.copy()) if isinstance(augmentation, ColorAugmentation): assert np.array_equal( bboxes, aug_bboxes ), "{} is a ColorAugmentation, but it modified bboxes".format( augmentation.__name__) with DirectoryWriter(directory=tmp_path, clean_directory=False) as image_writer: np.save( os.path.join(tmp_path, "{}-bboxes.npy".format(augmentation.__name__)), aug_bboxes) image_writer.write_image("{}-image.jpg".format(augmentation.__name__), aug_img) if snapshot["update_snapshots"]: if not os.path.isdir("./snapshots/augmentations"): os.mkdir("./snapshots/augmentations") shutil.copy( os.path.join(tmp_path, "{}-bboxes.npy".format(augmentation.__name__)), "./snapshots/augmentations/{}-bboxes.npy".format( augmentation.__name__)) shutil.copy( os.path.join(tmp_path, "{}-image.jpg".format(augmentation.__name__)), "./snapshots/augmentations/{}-image.jpg".format( augmentation.__name__)) return snapshot_bboxes = np.load( os.path.join("./snapshots/augmentations/{}-bboxes.npy".format( augmentation.__name__))) assert np.array_equal( aug_bboxes, snapshot_bboxes), "Bounding boxes for {} do not match".format( augmentation.__name__) assert filecmp.cmp( os.path.join(tmp_path, "{}-image.jpg".format(augmentation.__name__)), "./snapshots/augmentations/{}-image.jpg".format( augmentation.__name__)), "Images for {} do not match".format( augmentation.__name__)
def test_one_of_no_augmentations(sample_image): img, annotations = sample_image bboxes = annotations_to_numpy_array(annotations) augmentation = OneOf(augmentations=[]) aug_img, aug_bboxes = augmentation.augment(img.copy(), bboxes.copy()) assert np.array_equal(img, aug_img) assert np.array_equal(bboxes, aug_bboxes)
def test_motionblur(sample_image, direction): img, annotations = sample_image bboxes = annotations_to_numpy_array(annotations) augmentation = MotionBlur(kernel_size=10, direction=direction) aug_img, aug_bboxes = augmentation.augment(img.copy(), bboxes.copy()) assert np.array_equal(bboxes, aug_bboxes) assert np.array_equal(img.shape, aug_img.shape)
def test_coarsedropout(sample_image): img, annotations = sample_image bboxes = annotations_to_numpy_array(annotations) augmentation = CoarseDropout(0.1, 25) aug_img, aug_bboxes = augmentation.augment(img.copy(), bboxes.copy()) assert np.allclose( expected_aug_img, aug_img), "Performing augmentation does not yield expected image" assert np.array_equal( bboxes, aug_bboxes ), "Performing augmentation does not yield original augmentation"
def test_augmentation_is_involution(augmentation, sample_image): img, annotations = sample_image bboxes = annotations_to_numpy_array(annotations) aug_img, aug_bboxes = augmentation.augment(img.copy(), bboxes.copy()) re_aug_img, re_aug_bboxes = augmentation.augment(aug_img.copy(), aug_bboxes.copy()) assert np.allclose( img, re_aug_img ), "Performing augmentation twice does not yield origial image" assert np.allclose( bboxes, re_aug_bboxes ), "Performing augmentation twice does not yield original annotations"
def test_my_augmentation_warmer(sample_image): img, annotations = sample_image bboxes = annotations_to_numpy_array(annotations) # Kelvin values smaller than 6600K map to RGB multipliers # acting as warming filters. kelvin_input = random.randint(1000, 6560) augmentation = ColorTemperature(kelvin=kelvin_input) aug_img, aug_bboxes = augmentation.augment(img.copy(), bboxes.copy()) # Warming filters make reds more intensive and blue less. assert np.less_equal(img[:, :, 0], aug_img[:, :, 0]).all( ), "Performing augmentation does not yield expected red channel" assert np.greater_equal(img[:, :, 2], aug_img[:, :, 2]).all( ), "Performing augmentation does not yield expected blue channel" assert np.array_equal( bboxes, aug_bboxes ), "Performing augmentation does not yield original bounding boxes"
def save_image(path, image, annotations=None): """Save an image loaded with load_image or load_image_from_bytes. Keyword Arguments: path - The filename to save the image to. This must include an extension to indicate the format (e.g., .jpg, .png) image - The OpenCV image to save. This should have been originally loaded with load_image or load_image_from_bytes and optionally augmented annotations - An array of BoundingBox objects that will be drawn on top of the image in red, or None if no annotations are to be drawn. """ img_copy = image.copy() if annotations is not None: bboxes = annotations_to_numpy_array(annotations) img_copy = draw_rect(img_copy, bboxes, (255, 0, 0)) img_copy = cv2.cvtColor(img_copy, cv2.COLOR_RGB2BGR) return cv2.imwrite(path, img_copy)
def test_my_augmentation_colder(): img = np.array(([[[62, 238, 229], [242, 39, 182]], [[167, 153, 41], [130, 63, 162]]])) annotations = [] bboxes = annotations_to_numpy_array(annotations) # Kelvin values larger than 6600K map to RGB multipliers # acting as cooling filters. kelvin_input = random.randint(6710, 40000) augmentation = ColorTemperature(kelvin=kelvin_input) aug_img, aug_bboxes = augmentation.augment(img.copy(), bboxes.copy()) # Cooling filters make blues more intensive and reds less. assert np.greater_equal(img[:, :, 0], aug_img[:, :, 0]).all( ), "Performing augmentation does not yield expected red channel" assert np.less_equal(img[:, :, 2], aug_img[:, :, 2]).all( ), "Performing augmentation does not yield expected blue channel" assert np.array_equal( bboxes, aug_bboxes ), "Performing augmentation does not yield original bounding boxes"