-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Yafei
authored and
Yafei
committed
Oct 17, 2024
1 parent
f92b47b
commit ecd477b
Showing
10 changed files
with
668 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
|
||
import numpy as np | ||
import cv2 | ||
import matplotlib.pyplot as plt | ||
from scipy.ndimage import binary_fill_holes, distance_transform_edt | ||
|
||
def convex_hull(binary_mask): | ||
"""Compute the convex hull of a binary mask.""" | ||
points = np.argwhere(binary_mask) | ||
if len(points) < 3: | ||
return binary_mask.copy() # Not enough points to form a convex hull | ||
hull = cv2.convexHull(points) | ||
hull_mask = np.zeros_like(binary_mask, dtype=np.uint8) | ||
cv2.fillConvexPoly(hull_mask, hull, 1) | ||
return hull_mask | ||
|
||
def find_deepest_concavity(distance_map): | ||
"""Find the deepest concavity in the distance transform.""" | ||
return np.unravel_index(np.argmax(distance_map), distance_map.shape) | ||
|
||
def cut_region(binary_mask, cut_point, direction): | ||
"""Cut the region based on the cut_point and direction, returning the smaller part.""" | ||
y, x = cut_point | ||
height, width = binary_mask.shape | ||
|
||
# Create a mask for the entire region | ||
mask = np.zeros_like(binary_mask, dtype=np.uint8) | ||
|
||
# Define the slope from the direction | ||
dy, dx = np.sin(direction), np.cos(direction) | ||
|
||
# Create a half-plane mask | ||
for i in range(height): | ||
for j in range(width): | ||
# Calculate the position relative to the cut point | ||
if (dy * (j - x) - dx * (i - y)) > 0: # Above the line | ||
mask[i, j] = 1 | ||
|
||
# The smaller part of the original object is where the mask overlaps with the binary mask | ||
smaller_part = mask & binary_mask | ||
|
||
return smaller_part | ||
|
||
def maximal_inscribed_convex_set(binary_mask): | ||
"""Compute the maximal inscribed convex set.""" | ||
# Fill holes in the binary mask | ||
# filled_mask = binary_fill_holes(binary_mask).astype(np.uint8) | ||
filled_mask = binary_mask.astype(np.uint8) | ||
|
||
# Compute the convex hull | ||
hull = convex_hull(filled_mask) | ||
|
||
# Compute convex deficiency D | ||
deficiency = hull - filled_mask | ||
|
||
while True: | ||
# Calculate the distance transform of the deficiency | ||
distance_map = distance_transform_edt(deficiency) | ||
|
||
# Find the deepest concavity | ||
deepest_concavity = find_deepest_concavity(distance_map) | ||
print('deepest_concavity', deepest_concavity, distance_map[deepest_concavity]) | ||
|
||
# Check if the deepest concavity is within acceptable bounds | ||
if distance_map[deepest_concavity] <= 3: | ||
break | ||
|
||
# Generate cuts in 8 directions | ||
cuts = [cut_region(filled_mask, deepest_concavity, n * np.pi / 4) for n in range(8)] | ||
|
||
# Evaluate the size of the resulting sub-regions | ||
sub_regions = [filled_mask & cut for cut in cuts] | ||
areas = [np.sum(region) for region in sub_regions] | ||
|
||
# Remove the smallest sub-region if its area is greater than 0 | ||
valid_areas = [area for area in areas if area > 0] | ||
print('valid_areas', valid_areas) | ||
if not valid_areas: | ||
break # Exit if no valid areas are found | ||
|
||
min_area_index = np.argmin(valid_areas) | ||
filled_mask = filled_mask & ~sub_regions[min_area_index] | ||
|
||
# Recompute the convex hull and deficiency | ||
hull = convex_hull(filled_mask) | ||
deficiency = hull - filled_mask | ||
|
||
return filled_mask | ||
|
||
# Example usage | ||
if __name__ == "__main__": | ||
# Create a sample binary mask (connected region) | ||
region = np.zeros((1000, 1000)) | ||
cv2.rectangle(region, (30, 30), (600, 600), 1, -1) | ||
region = cv2.rectangle(region, (30, 30), (400, 400), 0, -1) # Create an overlapping rectangle | ||
|
||
# Compute the maximal inscribed convex set | ||
convex_set = maximal_inscribed_convex_set(region) | ||
|
||
# Visualization | ||
plt.figure(figsize=(10, 5)) | ||
plt.subplot(1, 2, 1) | ||
plt.title("Original Region") | ||
plt.imshow(region, cmap='gray') | ||
|
||
plt.subplot(1, 2, 2) | ||
plt.title("Maximal Inscribed Convex Set") | ||
plt.imshow(convex_set, cmap='gray') | ||
|
||
plt.show() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
|
||
import numpy as np | ||
import cv2 | ||
import os | ||
import json | ||
import time | ||
import random | ||
import skimage.measure | ||
from tqdm import tqdm | ||
from sklearn.metrics.pairwise import euclidean_distances | ||
from skimage.morphology import convex_hull_image | ||
from utils.generate_convex_appearance import generate_convex_appearance_for_bg | ||
from utils.remove_small_object import remove_small_object | ||
def create_bgC_dataset( | ||
source_image_folder, | ||
source_mask_folder, | ||
dest_image_folder | ||
): | ||
if not os.path.exists(dest_image_folder): | ||
os.makedirs(dest_image_folder) | ||
for fname in tqdm(os.listdir(source_mask_folder), ncols=90, desc=dest_image_folder): | ||
source_image = cv2.imread(os.path.join(source_image_folder, fname)) | ||
source_mask = cv2.imread(os.path.join(source_mask_folder, fname), cv2.IMREAD_GRAYSCALE) | ||
out_image = np.zeros_like(source_image) | ||
bg_image = source_image * np.array(source_mask==0)[:,:,None] | ||
bg_pixels = np.array(source_mask==0).astype(np.uint8).sum() | ||
if bg_pixels == 0: | ||
cv2.imwrite(os.path.join(dest_image_folder, fname), source_image) | ||
continue | ||
avg_bg_image = np.ones_like(bg_image) | ||
avg_bg_image[:,:,0] *= int(bg_image[:,:,0].sum() / bg_pixels) | ||
avg_bg_image[:,:,1] *= int(bg_image[:,:,1].sum() / bg_pixels) | ||
avg_bg_image[:,:,2] *= int(bg_image[:,:,2].sum() / bg_pixels) | ||
out_image += avg_bg_image * np.array(source_mask==0).astype(np.uint8)[:,:,None] + source_image * np.array(source_mask!=0).astype(np.uint8)[:,:,None] | ||
cv2.imwrite(os.path.join(dest_image_folder, fname), out_image) | ||
|
||
|
||
def create_bgT_dataset( | ||
source_image_folder, | ||
source_mask_folder, | ||
dest_image_folder | ||
): | ||
if not os.path.exists(dest_image_folder): | ||
os.makedirs(dest_image_folder) | ||
style_image_fname_list = os.listdir('replaced_texture/processed') | ||
style_image_fname_list.sort() | ||
for fname in tqdm(os.listdir(source_mask_folder), ncols=90, desc=dest_image_folder): | ||
source_image = cv2.imread(os.path.join(source_image_folder, fname)) | ||
source_mask = cv2.imread(os.path.join(source_mask_folder, fname), cv2.IMREAD_GRAYSCALE) | ||
out_image = np.zeros_like(source_image) | ||
fg_image = source_image * np.array(source_mask!=0)[:,:,None] ## [128, 128, 3] | ||
fg_avg_color = fg_image.sum(0).sum(0) / np.array(source_mask!=0).sum() | ||
largest_color_dist = 0 | ||
for style_image_fname in style_image_fname_list: | ||
style_image = cv2.imread(os.path.join('replaced_texture/processed', style_image_fname)) | ||
style_image_avg_color = style_image.sum(0).sum(0) / (128 * 128) | ||
dist = euclidean_distances([style_image_avg_color], [fg_avg_color])[0] | ||
if dist > largest_color_dist: | ||
selected_fname = style_image_fname | ||
largest_color_dist = dist | ||
style_image = cv2.imread(os.path.join('replaced_texture/processed', selected_fname)) | ||
out_image += style_image * np.array(source_mask==0)[:,:,None] | ||
out_image += source_image * np.array(source_mask!=0)[:,:,None] | ||
cv2.imwrite(os.path.join(dest_image_folder, fname), out_image) | ||
|
||
|
||
def create_bgCT_dataset( | ||
source_image_folder, | ||
source_mask_folder, | ||
dest_image_folder | ||
): | ||
if not os.path.exists(dest_image_folder): | ||
os.makedirs(dest_image_folder) | ||
for fname in tqdm(os.listdir(source_mask_folder), ncols=90, desc=dest_image_folder): | ||
source_image = cv2.imread(os.path.join(source_image_folder, fname)) | ||
source_mask = cv2.imread(os.path.join(source_mask_folder, fname), cv2.IMREAD_GRAYSCALE) | ||
out_image = np.zeros_like(source_image) | ||
fg_image = source_image * np.array(source_mask!=0)[:,:,None] ## [128, 128, 3] | ||
fg_avg_color = fg_image.sum(0).sum(0) / np.array(source_mask!=0).sum() | ||
|
||
corner_color = [ | ||
[0, 0, 0], | ||
[255, 0, 0], | ||
[0, 255, 0], | ||
[0, 0, 255], | ||
[255, 255, 0], | ||
[255, 0, 255], | ||
[0, 255, 255], | ||
[255, 255, 255], | ||
] | ||
largest_color_dist = 0 | ||
for color in corner_color: | ||
dist = euclidean_distances([color], [fg_avg_color])[0] | ||
if dist > largest_color_dist: | ||
selected_color = color | ||
largest_color_dist = dist | ||
new_bg_img = np.ones_like(source_image) * np.array(selected_color)[None, None, :].astype(np.uint8) | ||
out_image += new_bg_img * np.array(source_mask==0)[:,:,None] | ||
out_image += source_image * np.array(source_mask!=0)[:,:,None] | ||
cv2.imwrite(os.path.join(dest_image_folder, fname), out_image) | ||
|
||
def create_bgS_dataset( | ||
source_image_folder, | ||
source_mask_folder, | ||
dest_image_folder, | ||
dest_mask_folder, | ||
image_dim=128 | ||
): | ||
if not os.path.exists(dest_image_folder): | ||
os.makedirs(dest_image_folder) | ||
if not os.path.exists(dest_mask_folder): | ||
os.makedirs(dest_mask_folder) | ||
fname_list = os.listdir(source_image_folder) | ||
fname_list.sort() | ||
for fname in tqdm(fname_list, ncols=90, desc=dest_image_folder): | ||
source_image = cv2.imread(os.path.join(source_image_folder, fname)) | ||
source_mask = cv2.imread(os.path.join(source_mask_folder, fname), cv2.IMREAD_GRAYSCALE) | ||
source_fg_mask = np.array(source_mask!=0).astype(np.uint8) | ||
connected_component_mask = skimage.measure.label(source_fg_mask) | ||
|
||
# out_image = np.zeros((image_dim, image_dim, 3)) | ||
out_image = source_image | ||
out_mask = np.zeros((image_dim, image_dim)) | ||
for component_idx in np.unique(connected_component_mask): | ||
if component_idx == 0: | ||
continue | ||
source_component_mask = np.array(connected_component_mask==component_idx).astype(np.uint8) | ||
source_component_image = source_component_mask[:,:,None] * source_image | ||
kernel = np.ones((3, 3), dtype=np.uint8) | ||
convex_component_mask = convex_hull_image(source_component_mask).astype(np.uint8) | ||
timeout = 5 | ||
timeout_start = time.time() | ||
## we erode the convex shape to if it is too large | ||
# while convex_component_mask.sum() > 128 * 128 * 0.3 and time.time() < timeout_start + timeout: | ||
# kernel = np.ones((3, 3), dtype=np.uint8) | ||
# convex_component_mask = cv2.erode(convex_component_mask, kernel, iterations=1) | ||
|
||
convex_component_image, convex_component_mask, convex_obj_mask = generate_convex_appearance_for_bg( | ||
source_component_image=source_component_image, | ||
source_component_mask=source_component_mask, | ||
source_obj_mask=source_component_mask * source_mask, | ||
target_component_mask=convex_component_mask) | ||
|
||
out_image = out_image * (1-convex_component_mask[:, :, None]) + convex_component_image | ||
# out_mask = out_mask * (1-convex_component_mask) + convex_obj_mask | ||
out_mask = out_mask * (1-convex_component_mask) + convex_component_mask | ||
out_mask = source_mask + out_mask * (1-source_fg_mask) * (7) | ||
cv2.imwrite(os.path.join(dest_image_folder, fname), out_image) | ||
cv2.imwrite(os.path.join(dest_mask_folder, fname), out_mask) | ||
|
||
if __name__ == "__main__": | ||
|
||
create_bgCT_dataset( | ||
source_image_folder='/media/HDD1/kubric/MOVi-C_128/train/image_bgS', | ||
source_mask_folder='/media/HDD1/kubric/MOVi-C_128/train/mask_bgS', | ||
dest_image_folder='/media/HDD1/kubric/MOVi-C_128/train/image_bgCST', | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.