mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-27 15:01:08 +00:00
ADD the algorithms of image augmentation (#5792)
* ADD the algorithms of image augmentation * ADD the algorithms of image augmentation * ADD the algorithms of image augmentation * ADD the algorithms of image augmentation * ADD the algorithms of image augmentation * ADD the algorithms of image augmentation * UPDATE format code * UPDATE format and recode structure * UPDATE format import library * UPDATE code structure * Fix all checks have failded * FIX variable format * FIX variable format * FIX variable format * FIX code structure * FIX code structure * FIX code structure * FIX code structure
This commit is contained in:
parent
a98465230f
commit
2f6a7ae1fa
131
computer_vision/flip_augmentation.py
Normal file
131
computer_vision/flip_augmentation.py
Normal file
|
@ -0,0 +1,131 @@
|
|||
import glob
|
||||
import os
|
||||
import random
|
||||
from string import ascii_lowercase, digits
|
||||
|
||||
import cv2
|
||||
|
||||
"""
|
||||
Flip image and bounding box for computer vision task
|
||||
https://paperswithcode.com/method/randomhorizontalflip
|
||||
"""
|
||||
|
||||
# Params
|
||||
LABEL_DIR = ""
|
||||
IMAGE_DIR = ""
|
||||
OUTPUT_DIR = ""
|
||||
FLIP_TYPE = 1 # (0 is vertical, 1 is horizontal)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""
|
||||
Get images list and annotations list from input dir.
|
||||
Update new images and annotations.
|
||||
Save images and annotations in output dir.
|
||||
>>> pass # A doctest is not possible for this function.
|
||||
"""
|
||||
img_paths, annos = get_dataset(LABEL_DIR, IMAGE_DIR)
|
||||
print("Processing...")
|
||||
new_images, new_annos, paths = update_image_and_anno(img_paths, annos, FLIP_TYPE)
|
||||
|
||||
for index, image in enumerate(new_images):
|
||||
# Get random string code: '7b7ad245cdff75241935e4dd860f3bad'
|
||||
letter_code = random_chars(32)
|
||||
file_name = paths[index].split(os.sep)[-1].rsplit(".", 1)[0]
|
||||
file_root = f"{OUTPUT_DIR}/{file_name}_FLIP_{letter_code}"
|
||||
cv2.imwrite(f"/{file_root}.jpg", image, [cv2.IMWRITE_JPEG_QUALITY, 85])
|
||||
print(f"Success {index+1}/{len(new_images)} with {file_name}")
|
||||
annos_list = []
|
||||
for anno in new_annos[index]:
|
||||
obj = f"{anno[0]} {anno[1]} {anno[2]} {anno[3]} {anno[4]}"
|
||||
annos_list.append(obj)
|
||||
with open(f"/{file_root}.txt", "w") as outfile:
|
||||
outfile.write("\n".join(line for line in annos_list))
|
||||
|
||||
|
||||
def get_dataset(label_dir: str, img_dir: str) -> tuple[list, list]:
|
||||
"""
|
||||
- label_dir <type: str>: Path to label include annotation of images
|
||||
- img_dir <type: str>: Path to folder contain images
|
||||
Return <type: list>: List of images path and labels
|
||||
>>> pass # A doctest is not possible for this function.
|
||||
"""
|
||||
img_paths = []
|
||||
labels = []
|
||||
for label_file in glob.glob(os.path.join(label_dir, "*.txt")):
|
||||
label_name = label_file.split(os.sep)[-1].rsplit(".", 1)[0]
|
||||
with open(label_file) as in_file:
|
||||
obj_lists = in_file.readlines()
|
||||
img_path = os.path.join(img_dir, f"{label_name}.jpg")
|
||||
|
||||
boxes = []
|
||||
for obj_list in obj_lists:
|
||||
obj = obj_list.rstrip("\n").split(" ")
|
||||
boxes.append(
|
||||
[
|
||||
int(obj[0]),
|
||||
float(obj[1]),
|
||||
float(obj[2]),
|
||||
float(obj[3]),
|
||||
float(obj[4]),
|
||||
]
|
||||
)
|
||||
if not boxes:
|
||||
continue
|
||||
img_paths.append(img_path)
|
||||
labels.append(boxes)
|
||||
return img_paths, labels
|
||||
|
||||
|
||||
def update_image_and_anno(
|
||||
img_list: list, anno_list: list, flip_type: int = 1
|
||||
) -> tuple[list, list, list]:
|
||||
"""
|
||||
- img_list <type: list>: list of all images
|
||||
- anno_list <type: list>: list of all annotations of specific image
|
||||
- flip_type <type: int>: 0 is vertical, 1 is horizontal
|
||||
Return:
|
||||
- new_imgs_list <type: narray>: image after resize
|
||||
- new_annos_lists <type: list>: list of new annotation after scale
|
||||
- path_list <type: list>: list the name of image file
|
||||
>>> pass # A doctest is not possible for this function.
|
||||
"""
|
||||
new_annos_lists = []
|
||||
path_list = []
|
||||
new_imgs_list = []
|
||||
for idx in range(len(img_list)):
|
||||
new_annos = []
|
||||
path = img_list[idx]
|
||||
path_list.append(path)
|
||||
img_annos = anno_list[idx]
|
||||
img = cv2.imread(path)
|
||||
if flip_type == 1:
|
||||
new_img = cv2.flip(img, flip_type)
|
||||
for bbox in img_annos:
|
||||
x_center_new = 1 - bbox[1]
|
||||
new_annos.append([bbox[0], x_center_new, bbox[2], bbox[3], bbox[4]])
|
||||
elif flip_type == 0:
|
||||
new_img = cv2.flip(img, flip_type)
|
||||
for bbox in img_annos:
|
||||
y_center_new = 1 - bbox[2]
|
||||
new_annos.append([bbox[0], bbox[1], y_center_new, bbox[3], bbox[4]])
|
||||
new_annos_lists.append(new_annos)
|
||||
new_imgs_list.append(new_img)
|
||||
return new_imgs_list, new_annos_lists, path_list
|
||||
|
||||
|
||||
def random_chars(number_char: int = 32) -> str:
|
||||
"""
|
||||
Automatic generate random 32 characters.
|
||||
Get random string code: '7b7ad245cdff75241935e4dd860f3bad'
|
||||
>>> len(random_chars(32))
|
||||
32
|
||||
"""
|
||||
assert number_char > 1, "The number of character should greater than 1"
|
||||
letter_code = ascii_lowercase + digits
|
||||
return "".join(random.choice(letter_code) for _ in range(number_char))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
print("DONE ✅")
|
189
computer_vision/mosaic_augmentation.py
Normal file
189
computer_vision/mosaic_augmentation.py
Normal file
|
@ -0,0 +1,189 @@
|
|||
"""Source: https://github.com/jason9075/opencv-mosaic-data-aug"""
|
||||
|
||||
import glob
|
||||
import os
|
||||
import random
|
||||
from string import ascii_lowercase, digits
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
# Parrameters
|
||||
OUTPUT_SIZE = (720, 1280) # Height, Width
|
||||
SCALE_RANGE = (0.4, 0.6) # if height or width lower than this scale, drop it.
|
||||
FILTER_TINY_SCALE = 1 / 100
|
||||
LABEL_DIR = ""
|
||||
IMG_DIR = ""
|
||||
OUTPUT_DIR = ""
|
||||
NUMBER_IMAGES = 250
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""
|
||||
Get images list and annotations list from input dir.
|
||||
Update new images and annotations.
|
||||
Save images and annotations in output dir.
|
||||
>>> pass # A doctest is not possible for this function.
|
||||
"""
|
||||
img_paths, annos = get_dataset(LABEL_DIR, IMG_DIR)
|
||||
for index in range(NUMBER_IMAGES):
|
||||
idxs = random.sample(range(len(annos)), 4)
|
||||
new_image, new_annos, path = update_image_and_anno(
|
||||
img_paths,
|
||||
annos,
|
||||
idxs,
|
||||
OUTPUT_SIZE,
|
||||
SCALE_RANGE,
|
||||
filter_scale=FILTER_TINY_SCALE,
|
||||
)
|
||||
|
||||
# Get random string code: '7b7ad245cdff75241935e4dd860f3bad'
|
||||
letter_code = random_chars(32)
|
||||
file_name = path.split(os.sep)[-1].rsplit(".", 1)[0]
|
||||
file_root = f"{OUTPUT_DIR}/{file_name}_MOSAIC_{letter_code}"
|
||||
cv2.imwrite(f"{file_root}.jpg", new_image, [cv2.IMWRITE_JPEG_QUALITY, 85])
|
||||
print(f"Succeeded {index+1}/{NUMBER_IMAGES} with {file_name}")
|
||||
annos_list = []
|
||||
for anno in new_annos:
|
||||
width = anno[3] - anno[1]
|
||||
height = anno[4] - anno[2]
|
||||
x_center = anno[1] + width / 2
|
||||
y_center = anno[2] + height / 2
|
||||
obj = f"{anno[0]} {x_center} {y_center} {width} {height}"
|
||||
annos_list.append(obj)
|
||||
with open(f"{file_root}.txt", "w") as outfile:
|
||||
outfile.write("\n".join(line for line in annos_list))
|
||||
|
||||
|
||||
def get_dataset(label_dir: str, img_dir: str) -> tuple[list, list]:
|
||||
"""
|
||||
- label_dir <type: str>: Path to label include annotation of images
|
||||
- img_dir <type: str>: Path to folder contain images
|
||||
Return <type: list>: List of images path and labels
|
||||
>>> pass # A doctest is not possible for this function.
|
||||
"""
|
||||
img_paths = []
|
||||
labels = []
|
||||
for label_file in glob.glob(os.path.join(label_dir, "*.txt")):
|
||||
label_name = label_file.split(os.sep)[-1].rsplit(".", 1)[0]
|
||||
with open(label_file) as in_file:
|
||||
obj_lists = in_file.readlines()
|
||||
img_path = os.path.join(img_dir, f"{label_name}.jpg")
|
||||
|
||||
boxes = []
|
||||
for obj_list in obj_lists:
|
||||
obj = obj_list.rstrip("\n").split(" ")
|
||||
xmin = float(obj[1]) - float(obj[3]) / 2
|
||||
ymin = float(obj[2]) - float(obj[4]) / 2
|
||||
xmax = float(obj[1]) + float(obj[3]) / 2
|
||||
ymax = float(obj[2]) + float(obj[4]) / 2
|
||||
|
||||
boxes.append([int(obj[0]), xmin, ymin, xmax, ymax])
|
||||
if not boxes:
|
||||
continue
|
||||
img_paths.append(img_path)
|
||||
labels.append(boxes)
|
||||
return img_paths, labels
|
||||
|
||||
|
||||
def update_image_and_anno(
|
||||
all_img_list: list,
|
||||
all_annos: list,
|
||||
idxs: list[int],
|
||||
output_size: tuple[int, int],
|
||||
scale_range: tuple[float, float],
|
||||
filter_scale: float = 0.0,
|
||||
) -> tuple[list, list, str]:
|
||||
"""
|
||||
- all_img_list <type: list>: list of all images
|
||||
- all_annos <type: list>: list of all annotations of specific image
|
||||
- idxs <type: list>: index of image in list
|
||||
- output_size <type: tuple>: size of output image (Height, Width)
|
||||
- scale_range <type: tuple>: range of scale image
|
||||
- filter_scale <type: float>: the condition of downscale image and bounding box
|
||||
Return:
|
||||
- output_img <type: narray>: image after resize
|
||||
- new_anno <type: list>: list of new annotation after scale
|
||||
- path[0] <type: string>: get the name of image file
|
||||
>>> pass # A doctest is not possible for this function.
|
||||
"""
|
||||
output_img = np.zeros([output_size[0], output_size[1], 3], dtype=np.uint8)
|
||||
scale_x = scale_range[0] + random.random() * (scale_range[1] - scale_range[0])
|
||||
scale_y = scale_range[0] + random.random() * (scale_range[1] - scale_range[0])
|
||||
divid_point_x = int(scale_x * output_size[1])
|
||||
divid_point_y = int(scale_y * output_size[0])
|
||||
|
||||
new_anno = []
|
||||
path_list = []
|
||||
for i, index in enumerate(idxs):
|
||||
path = all_img_list[index]
|
||||
path_list.append(path)
|
||||
img_annos = all_annos[index]
|
||||
img = cv2.imread(path)
|
||||
if i == 0: # top-left
|
||||
img = cv2.resize(img, (divid_point_x, divid_point_y))
|
||||
output_img[:divid_point_y, :divid_point_x, :] = img
|
||||
for bbox in img_annos:
|
||||
xmin = bbox[1] * scale_x
|
||||
ymin = bbox[2] * scale_y
|
||||
xmax = bbox[3] * scale_x
|
||||
ymax = bbox[4] * scale_y
|
||||
new_anno.append([bbox[0], xmin, ymin, xmax, ymax])
|
||||
elif i == 1: # top-right
|
||||
img = cv2.resize(img, (output_size[1] - divid_point_x, divid_point_y))
|
||||
output_img[:divid_point_y, divid_point_x : output_size[1], :] = img
|
||||
for bbox in img_annos:
|
||||
xmin = scale_x + bbox[1] * (1 - scale_x)
|
||||
ymin = bbox[2] * scale_y
|
||||
xmax = scale_x + bbox[3] * (1 - scale_x)
|
||||
ymax = bbox[4] * scale_y
|
||||
new_anno.append([bbox[0], xmin, ymin, xmax, ymax])
|
||||
elif i == 2: # bottom-left
|
||||
img = cv2.resize(img, (divid_point_x, output_size[0] - divid_point_y))
|
||||
output_img[divid_point_y : output_size[0], :divid_point_x, :] = img
|
||||
for bbox in img_annos:
|
||||
xmin = bbox[1] * scale_x
|
||||
ymin = scale_y + bbox[2] * (1 - scale_y)
|
||||
xmax = bbox[3] * scale_x
|
||||
ymax = scale_y + bbox[4] * (1 - scale_y)
|
||||
new_anno.append([bbox[0], xmin, ymin, xmax, ymax])
|
||||
else: # bottom-right
|
||||
img = cv2.resize(
|
||||
img, (output_size[1] - divid_point_x, output_size[0] - divid_point_y)
|
||||
)
|
||||
output_img[
|
||||
divid_point_y : output_size[0], divid_point_x : output_size[1], :
|
||||
] = img
|
||||
for bbox in img_annos:
|
||||
xmin = scale_x + bbox[1] * (1 - scale_x)
|
||||
ymin = scale_y + bbox[2] * (1 - scale_y)
|
||||
xmax = scale_x + bbox[3] * (1 - scale_x)
|
||||
ymax = scale_y + bbox[4] * (1 - scale_y)
|
||||
new_anno.append([bbox[0], xmin, ymin, xmax, ymax])
|
||||
|
||||
# Remove bounding box small than scale of filter
|
||||
if 0 < filter_scale:
|
||||
new_anno = [
|
||||
anno
|
||||
for anno in new_anno
|
||||
if filter_scale < (anno[3] - anno[1]) and filter_scale < (anno[4] - anno[2])
|
||||
]
|
||||
|
||||
return output_img, new_anno, path_list[0]
|
||||
|
||||
|
||||
def random_chars(number_char: int) -> str:
|
||||
"""
|
||||
Automatic generate random 32 characters.
|
||||
Get random string code: '7b7ad245cdff75241935e4dd860f3bad'
|
||||
>>> len(random_chars(32))
|
||||
32
|
||||
"""
|
||||
assert number_char > 1, "The number of character should greater than 1"
|
||||
letter_code = ascii_lowercase + digits
|
||||
return "".join(random.choice(letter_code) for _ in range(number_char))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
print("DONE ✅")
|
Loading…
Reference in New Issue
Block a user