release infer and demo
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
import time
|
||||
import torch
|
||||
import cv2
|
||||
from PIL import Image, ImageDraw, ImageOps
|
||||
import numpy as np
|
||||
from typing import Union
|
||||
from segment_anything import sam_model_registry, SamPredictor, SamAutomaticMaskGenerator
|
||||
import matplotlib.pyplot as plt
|
||||
import PIL
|
||||
from .mask_painter import mask_painter
|
||||
|
||||
|
||||
class BaseSegmenter:
|
||||
def __init__(self, SAM_checkpoint, model_type, device='cuda:0'):
|
||||
"""
|
||||
device: model device
|
||||
SAM_checkpoint: path of SAM checkpoint
|
||||
model_type: vit_b, vit_l, vit_h
|
||||
"""
|
||||
print(f"Initializing BaseSegmenter to {device}")
|
||||
assert model_type in ['vit_b', 'vit_l', 'vit_h'], 'model_type must be vit_b, vit_l, or vit_h'
|
||||
|
||||
self.device = device
|
||||
self.torch_dtype = torch.float16 if 'cuda' in device else torch.float32
|
||||
self.model = sam_model_registry[model_type](checkpoint=SAM_checkpoint)
|
||||
self.model.to(device=self.device)
|
||||
self.predictor = SamPredictor(self.model)
|
||||
self.embedded = False
|
||||
|
||||
@torch.no_grad()
|
||||
def set_image(self, image: np.ndarray):
|
||||
# PIL.open(image_path) 3channel: RGB
|
||||
# image embedding: avoid encode the same image multiple times
|
||||
self.orignal_image = image
|
||||
if self.embedded:
|
||||
print('repeat embedding, please reset_image.')
|
||||
return
|
||||
self.predictor.set_image(image)
|
||||
self.embedded = True
|
||||
return
|
||||
|
||||
@torch.no_grad()
|
||||
def reset_image(self):
|
||||
# reset image embeding
|
||||
self.predictor.reset_image()
|
||||
self.embedded = False
|
||||
|
||||
def predict(self, prompts, mode, multimask=True):
|
||||
"""
|
||||
image: numpy array, h, w, 3
|
||||
prompts: dictionary, 3 keys: 'point_coords', 'point_labels', 'mask_input'
|
||||
prompts['point_coords']: numpy array [N,2]
|
||||
prompts['point_labels']: numpy array [1,N]
|
||||
prompts['mask_input']: numpy array [1,256,256]
|
||||
mode: 'point' (points only), 'mask' (mask only), 'both' (consider both)
|
||||
mask_outputs: True (return 3 masks), False (return 1 mask only)
|
||||
whem mask_outputs=True, mask_input=logits[np.argmax(scores), :, :][None, :, :]
|
||||
"""
|
||||
assert self.embedded, 'prediction is called before set_image (feature embedding).'
|
||||
assert mode in ['point', 'mask', 'both'], 'mode must be point, mask, or both'
|
||||
|
||||
if mode == 'point':
|
||||
masks, scores, logits = self.predictor.predict(point_coords=prompts['point_coords'],
|
||||
point_labels=prompts['point_labels'],
|
||||
multimask_output=multimask)
|
||||
elif mode == 'mask':
|
||||
masks, scores, logits = self.predictor.predict(mask_input=prompts['mask_input'],
|
||||
multimask_output=multimask)
|
||||
elif mode == 'both': # both
|
||||
masks, scores, logits = self.predictor.predict(point_coords=prompts['point_coords'],
|
||||
point_labels=prompts['point_labels'],
|
||||
mask_input=prompts['mask_input'],
|
||||
multimask_output=multimask)
|
||||
else:
|
||||
raise("Not implement now!")
|
||||
# masks (n, h, w), scores (n,), logits (n, 256, 256)
|
||||
return masks, scores, logits
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# load and show an image
|
||||
image = cv2.imread('/hhd3/gaoshang/truck.jpg')
|
||||
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # numpy array (h, w, 3)
|
||||
|
||||
# initialise BaseSegmenter
|
||||
SAM_checkpoint= '/ssd1/gaomingqi/checkpoints/sam_vit_h_4b8939.pth'
|
||||
model_type = 'vit_h'
|
||||
device = "cuda:4"
|
||||
base_segmenter = BaseSegmenter(SAM_checkpoint=SAM_checkpoint, model_type=model_type, device=device)
|
||||
|
||||
# image embedding (once embedded, multiple prompts can be applied)
|
||||
base_segmenter.set_image(image)
|
||||
|
||||
# examples
|
||||
# point only ------------------------
|
||||
mode = 'point'
|
||||
prompts = {
|
||||
'point_coords': np.array([[500, 375], [1125, 625]]),
|
||||
'point_labels': np.array([1, 1]),
|
||||
}
|
||||
masks, scores, logits = base_segmenter.predict(prompts, mode, multimask=False) # masks (n, h, w), scores (n,), logits (n, 256, 256)
|
||||
painted_image = mask_painter(image, masks[np.argmax(scores)].astype('uint8'), background_alpha=0.8)
|
||||
painted_image = cv2.cvtColor(painted_image, cv2.COLOR_RGB2BGR) # numpy array (h, w, 3)
|
||||
cv2.imwrite('/hhd3/gaoshang/truck_point.jpg', painted_image)
|
||||
|
||||
# both ------------------------
|
||||
mode = 'both'
|
||||
mask_input = logits[np.argmax(scores), :, :]
|
||||
prompts = {'mask_input': mask_input [None, :, :]}
|
||||
prompts = {
|
||||
'point_coords': np.array([[500, 375], [1125, 625]]),
|
||||
'point_labels': np.array([1, 0]),
|
||||
'mask_input': mask_input[None, :, :]
|
||||
}
|
||||
masks, scores, logits = base_segmenter.predict(prompts, mode, multimask=True) # masks (n, h, w), scores (n,), logits (n, 256, 256)
|
||||
painted_image = mask_painter(image, masks[np.argmax(scores)].astype('uint8'), background_alpha=0.8)
|
||||
painted_image = cv2.cvtColor(painted_image, cv2.COLOR_RGB2BGR) # numpy array (h, w, 3)
|
||||
cv2.imwrite('/hhd3/gaoshang/truck_both.jpg', painted_image)
|
||||
|
||||
# mask only ------------------------
|
||||
mode = 'mask'
|
||||
mask_input = logits[np.argmax(scores), :, :]
|
||||
|
||||
prompts = {'mask_input': mask_input[None, :, :]}
|
||||
|
||||
masks, scores, logits = base_segmenter.predict(prompts, mode, multimask=True) # masks (n, h, w), scores (n,), logits (n, 256, 256)
|
||||
painted_image = mask_painter(image, masks[np.argmax(scores)].astype('uint8'), background_alpha=0.8)
|
||||
painted_image = cv2.cvtColor(painted_image, cv2.COLOR_RGB2BGR) # numpy array (h, w, 3)
|
||||
cv2.imwrite('/hhd3/gaoshang/truck_mask.jpg', painted_image)
|
||||
@@ -0,0 +1,109 @@
|
||||
import math
|
||||
import os
|
||||
import requests
|
||||
from torch.hub import download_url_to_file, get_dir
|
||||
from tqdm import tqdm
|
||||
from urllib.parse import urlparse
|
||||
|
||||
def sizeof_fmt(size, suffix='B'):
|
||||
"""Get human readable file size.
|
||||
|
||||
Args:
|
||||
size (int): File size.
|
||||
suffix (str): Suffix. Default: 'B'.
|
||||
|
||||
Return:
|
||||
str: Formated file siz.
|
||||
"""
|
||||
for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
|
||||
if abs(size) < 1024.0:
|
||||
return f'{size:3.1f} {unit}{suffix}'
|
||||
size /= 1024.0
|
||||
return f'{size:3.1f} Y{suffix}'
|
||||
|
||||
|
||||
def download_file_from_google_drive(file_id, save_path):
|
||||
"""Download files from google drive.
|
||||
Ref:
|
||||
https://stackoverflow.com/questions/25010369/wget-curl-large-file-from-google-drive # noqa E501
|
||||
Args:
|
||||
file_id (str): File id.
|
||||
save_path (str): Save path.
|
||||
"""
|
||||
|
||||
session = requests.Session()
|
||||
URL = 'https://docs.google.com/uc?export=download'
|
||||
params = {'id': file_id}
|
||||
|
||||
response = session.get(URL, params=params, stream=True)
|
||||
token = get_confirm_token(response)
|
||||
if token:
|
||||
params['confirm'] = token
|
||||
response = session.get(URL, params=params, stream=True)
|
||||
|
||||
# get file size
|
||||
response_file_size = session.get(URL, params=params, stream=True, headers={'Range': 'bytes=0-2'})
|
||||
print(response_file_size)
|
||||
if 'Content-Range' in response_file_size.headers:
|
||||
file_size = int(response_file_size.headers['Content-Range'].split('/')[1])
|
||||
else:
|
||||
file_size = None
|
||||
|
||||
save_response_content(response, save_path, file_size)
|
||||
|
||||
|
||||
def get_confirm_token(response):
|
||||
for key, value in response.cookies.items():
|
||||
if key.startswith('download_warning'):
|
||||
return value
|
||||
return None
|
||||
|
||||
|
||||
def save_response_content(response, destination, file_size=None, chunk_size=32768):
|
||||
if file_size is not None:
|
||||
pbar = tqdm(total=math.ceil(file_size / chunk_size), unit='chunk')
|
||||
|
||||
readable_file_size = sizeof_fmt(file_size)
|
||||
else:
|
||||
pbar = None
|
||||
|
||||
with open(destination, 'wb') as f:
|
||||
downloaded_size = 0
|
||||
for chunk in response.iter_content(chunk_size):
|
||||
downloaded_size += chunk_size
|
||||
if pbar is not None:
|
||||
pbar.update(1)
|
||||
pbar.set_description(f'Download {sizeof_fmt(downloaded_size)} / {readable_file_size}')
|
||||
if chunk: # filter out keep-alive new chunks
|
||||
f.write(chunk)
|
||||
if pbar is not None:
|
||||
pbar.close()
|
||||
|
||||
|
||||
def load_file_from_url(url, model_dir=None, progress=True, file_name=None):
|
||||
"""Load file form http url, will download models if necessary.
|
||||
Ref:https://github.com/1adrianb/face-alignment/blob/master/face_alignment/utils.py
|
||||
Args:
|
||||
url (str): URL to be downloaded.
|
||||
model_dir (str): The path to save the downloaded model. Should be a full path. If None, use pytorch hub_dir.
|
||||
Default: None.
|
||||
progress (bool): Whether to show the download progress. Default: True.
|
||||
file_name (str): The downloaded file name. If None, use the file name in the url. Default: None.
|
||||
Returns:
|
||||
str: The path to the downloaded file.
|
||||
"""
|
||||
if model_dir is None: # use the pytorch hub_dir
|
||||
hub_dir = get_dir()
|
||||
model_dir = os.path.join(hub_dir, 'checkpoints')
|
||||
|
||||
os.makedirs(model_dir, exist_ok=True)
|
||||
|
||||
parts = urlparse(url)
|
||||
filename = os.path.basename(parts.path)
|
||||
if file_name is not None:
|
||||
filename = file_name
|
||||
cached_file = os.path.abspath(os.path.join(model_dir, filename))
|
||||
if not os.path.exists(cached_file):
|
||||
print(f'Downloading: "{url}" to {cached_file}\n')
|
||||
download_url_to_file(url, cached_file, hash_prefix=None, progress=progress)
|
||||
return cached_file
|
||||
@@ -0,0 +1,99 @@
|
||||
import time
|
||||
import torch
|
||||
import cv2
|
||||
from PIL import Image, ImageDraw, ImageOps
|
||||
import numpy as np
|
||||
from typing import Union
|
||||
from segment_anything import sam_model_registry, SamPredictor, SamAutomaticMaskGenerator
|
||||
import matplotlib.pyplot as plt
|
||||
import PIL
|
||||
from .mask_painter import mask_painter as mask_painter2
|
||||
from .base_segmenter import BaseSegmenter
|
||||
from .painter import mask_painter, point_painter
|
||||
import os
|
||||
import requests
|
||||
import sys
|
||||
|
||||
|
||||
mask_color = 3
|
||||
mask_alpha = 0.7
|
||||
contour_color = 1
|
||||
contour_width = 5
|
||||
point_color_ne = 8
|
||||
point_color_ps = 50
|
||||
point_alpha = 0.9
|
||||
point_radius = 15
|
||||
contour_color = 2
|
||||
contour_width = 5
|
||||
|
||||
|
||||
class SamControler():
|
||||
def __init__(self, SAM_checkpoint, model_type, device):
|
||||
'''
|
||||
initialize sam controler
|
||||
'''
|
||||
self.sam_controler = BaseSegmenter(SAM_checkpoint, model_type, device)
|
||||
|
||||
|
||||
# def seg_again(self, image: np.ndarray):
|
||||
# '''
|
||||
# it is used when interact in video
|
||||
# '''
|
||||
# self.sam_controler.reset_image()
|
||||
# self.sam_controler.set_image(image)
|
||||
# return
|
||||
|
||||
|
||||
def first_frame_click(self, image: np.ndarray, points:np.ndarray, labels: np.ndarray, multimask=True,mask_color=3):
|
||||
'''
|
||||
it is used in first frame in video
|
||||
return: mask, logit, painted image(mask+point)
|
||||
'''
|
||||
# self.sam_controler.set_image(image)
|
||||
origal_image = self.sam_controler.orignal_image
|
||||
neg_flag = labels[-1]
|
||||
if neg_flag==1:
|
||||
#find neg
|
||||
prompts = {
|
||||
'point_coords': points,
|
||||
'point_labels': labels,
|
||||
}
|
||||
masks, scores, logits = self.sam_controler.predict(prompts, 'point', multimask)
|
||||
mask, logit = masks[np.argmax(scores)], logits[np.argmax(scores), :, :]
|
||||
prompts = {
|
||||
'point_coords': points,
|
||||
'point_labels': labels,
|
||||
'mask_input': logit[None, :, :]
|
||||
}
|
||||
masks, scores, logits = self.sam_controler.predict(prompts, 'both', multimask)
|
||||
mask, logit = masks[np.argmax(scores)], logits[np.argmax(scores), :, :]
|
||||
else:
|
||||
#find positive
|
||||
prompts = {
|
||||
'point_coords': points,
|
||||
'point_labels': labels,
|
||||
}
|
||||
masks, scores, logits = self.sam_controler.predict(prompts, 'point', multimask)
|
||||
mask, logit = masks[np.argmax(scores)], logits[np.argmax(scores), :, :]
|
||||
|
||||
|
||||
assert len(points)==len(labels)
|
||||
|
||||
painted_image = mask_painter(image, mask.astype('uint8'), mask_color, mask_alpha, contour_color, contour_width)
|
||||
painted_image = point_painter(painted_image, np.squeeze(points[np.argwhere(labels>0)],axis = 1), point_color_ne, point_alpha, point_radius, contour_color, contour_width)
|
||||
painted_image = point_painter(painted_image, np.squeeze(points[np.argwhere(labels<1)],axis = 1), point_color_ps, point_alpha, point_radius, contour_color, contour_width)
|
||||
painted_image = Image.fromarray(painted_image)
|
||||
|
||||
return mask, logit, painted_image
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
import cv2
|
||||
import torch
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import copy
|
||||
import time
|
||||
|
||||
|
||||
def colormap(rgb=True):
|
||||
color_list = np.array(
|
||||
[
|
||||
0.000, 0.000, 0.000,
|
||||
1.000, 1.000, 1.000,
|
||||
1.000, 0.498, 0.313,
|
||||
0.392, 0.581, 0.929,
|
||||
0.000, 0.447, 0.741,
|
||||
0.850, 0.325, 0.098,
|
||||
0.929, 0.694, 0.125,
|
||||
0.494, 0.184, 0.556,
|
||||
0.466, 0.674, 0.188,
|
||||
0.301, 0.745, 0.933,
|
||||
0.635, 0.078, 0.184,
|
||||
0.300, 0.300, 0.300,
|
||||
0.600, 0.600, 0.600,
|
||||
1.000, 0.000, 0.000,
|
||||
1.000, 0.500, 0.000,
|
||||
0.749, 0.749, 0.000,
|
||||
0.000, 1.000, 0.000,
|
||||
0.000, 0.000, 1.000,
|
||||
0.667, 0.000, 1.000,
|
||||
0.333, 0.333, 0.000,
|
||||
0.333, 0.667, 0.000,
|
||||
0.333, 1.000, 0.000,
|
||||
0.667, 0.333, 0.000,
|
||||
0.667, 0.667, 0.000,
|
||||
0.667, 1.000, 0.000,
|
||||
1.000, 0.333, 0.000,
|
||||
1.000, 0.667, 0.000,
|
||||
1.000, 1.000, 0.000,
|
||||
0.000, 0.333, 0.500,
|
||||
0.000, 0.667, 0.500,
|
||||
0.000, 1.000, 0.500,
|
||||
0.333, 0.000, 0.500,
|
||||
0.333, 0.333, 0.500,
|
||||
0.333, 0.667, 0.500,
|
||||
0.333, 1.000, 0.500,
|
||||
0.667, 0.000, 0.500,
|
||||
0.667, 0.333, 0.500,
|
||||
0.667, 0.667, 0.500,
|
||||
0.667, 1.000, 0.500,
|
||||
1.000, 0.000, 0.500,
|
||||
1.000, 0.333, 0.500,
|
||||
1.000, 0.667, 0.500,
|
||||
1.000, 1.000, 0.500,
|
||||
0.000, 0.333, 1.000,
|
||||
0.000, 0.667, 1.000,
|
||||
0.000, 1.000, 1.000,
|
||||
0.333, 0.000, 1.000,
|
||||
0.333, 0.333, 1.000,
|
||||
0.333, 0.667, 1.000,
|
||||
0.333, 1.000, 1.000,
|
||||
0.667, 0.000, 1.000,
|
||||
0.667, 0.333, 1.000,
|
||||
0.667, 0.667, 1.000,
|
||||
0.667, 1.000, 1.000,
|
||||
1.000, 0.000, 1.000,
|
||||
1.000, 0.333, 1.000,
|
||||
1.000, 0.667, 1.000,
|
||||
0.167, 0.000, 0.000,
|
||||
0.333, 0.000, 0.000,
|
||||
0.500, 0.000, 0.000,
|
||||
0.667, 0.000, 0.000,
|
||||
0.833, 0.000, 0.000,
|
||||
1.000, 0.000, 0.000,
|
||||
0.000, 0.167, 0.000,
|
||||
0.000, 0.333, 0.000,
|
||||
0.000, 0.500, 0.000,
|
||||
0.000, 0.667, 0.000,
|
||||
0.000, 0.833, 0.000,
|
||||
0.000, 1.000, 0.000,
|
||||
0.000, 0.000, 0.167,
|
||||
0.000, 0.000, 0.333,
|
||||
0.000, 0.000, 0.500,
|
||||
0.000, 0.000, 0.667,
|
||||
0.000, 0.000, 0.833,
|
||||
0.000, 0.000, 1.000,
|
||||
0.143, 0.143, 0.143,
|
||||
0.286, 0.286, 0.286,
|
||||
0.429, 0.429, 0.429,
|
||||
0.571, 0.571, 0.571,
|
||||
0.714, 0.714, 0.714,
|
||||
0.857, 0.857, 0.857
|
||||
]
|
||||
).astype(np.float32)
|
||||
color_list = color_list.reshape((-1, 3)) * 255
|
||||
if not rgb:
|
||||
color_list = color_list[:, ::-1]
|
||||
return color_list
|
||||
|
||||
|
||||
color_list = colormap()
|
||||
color_list = color_list.astype('uint8').tolist()
|
||||
|
||||
|
||||
def vis_add_mask(image, background_mask, contour_mask, background_color, contour_color, background_alpha, contour_alpha):
|
||||
background_color = np.array(background_color)
|
||||
contour_color = np.array(contour_color)
|
||||
|
||||
# background_mask = 1 - background_mask
|
||||
# contour_mask = 1 - contour_mask
|
||||
|
||||
for i in range(3):
|
||||
image[:, :, i] = image[:, :, i] * (1-background_alpha+background_mask*background_alpha) \
|
||||
+ background_color[i] * (background_alpha-background_mask*background_alpha)
|
||||
|
||||
image[:, :, i] = image[:, :, i] * (1-contour_alpha+contour_mask*contour_alpha) \
|
||||
+ contour_color[i] * (contour_alpha-contour_mask*contour_alpha)
|
||||
|
||||
return image.astype('uint8')
|
||||
|
||||
|
||||
def mask_generator_00(mask, background_radius, contour_radius):
|
||||
# no background width when '00'
|
||||
# distance map
|
||||
dist_transform_fore = cv2.distanceTransform(mask, cv2.DIST_L2, 3)
|
||||
dist_transform_back = cv2.distanceTransform(1-mask, cv2.DIST_L2, 3)
|
||||
dist_map = dist_transform_fore - dist_transform_back
|
||||
# ...:::!!!:::...
|
||||
contour_radius += 2
|
||||
contour_mask = np.abs(np.clip(dist_map, -contour_radius, contour_radius))
|
||||
contour_mask = contour_mask / np.max(contour_mask)
|
||||
contour_mask[contour_mask>0.5] = 1.
|
||||
|
||||
return mask, contour_mask
|
||||
|
||||
|
||||
def mask_generator_01(mask, background_radius, contour_radius):
|
||||
# no background width when '00'
|
||||
# distance map
|
||||
dist_transform_fore = cv2.distanceTransform(mask, cv2.DIST_L2, 3)
|
||||
dist_transform_back = cv2.distanceTransform(1-mask, cv2.DIST_L2, 3)
|
||||
dist_map = dist_transform_fore - dist_transform_back
|
||||
# ...:::!!!:::...
|
||||
contour_radius += 2
|
||||
contour_mask = np.abs(np.clip(dist_map, -contour_radius, contour_radius))
|
||||
contour_mask = contour_mask / np.max(contour_mask)
|
||||
return mask, contour_mask
|
||||
|
||||
|
||||
def mask_generator_10(mask, background_radius, contour_radius):
|
||||
# distance map
|
||||
dist_transform_fore = cv2.distanceTransform(mask, cv2.DIST_L2, 3)
|
||||
dist_transform_back = cv2.distanceTransform(1-mask, cv2.DIST_L2, 3)
|
||||
dist_map = dist_transform_fore - dist_transform_back
|
||||
# .....:::::!!!!!
|
||||
background_mask = np.clip(dist_map, -background_radius, background_radius)
|
||||
background_mask = (background_mask - np.min(background_mask))
|
||||
background_mask = background_mask / np.max(background_mask)
|
||||
# ...:::!!!:::...
|
||||
contour_radius += 2
|
||||
contour_mask = np.abs(np.clip(dist_map, -contour_radius, contour_radius))
|
||||
contour_mask = contour_mask / np.max(contour_mask)
|
||||
contour_mask[contour_mask>0.5] = 1.
|
||||
return background_mask, contour_mask
|
||||
|
||||
|
||||
def mask_generator_11(mask, background_radius, contour_radius):
|
||||
# distance map
|
||||
dist_transform_fore = cv2.distanceTransform(mask, cv2.DIST_L2, 3)
|
||||
dist_transform_back = cv2.distanceTransform(1-mask, cv2.DIST_L2, 3)
|
||||
dist_map = dist_transform_fore - dist_transform_back
|
||||
# .....:::::!!!!!
|
||||
background_mask = np.clip(dist_map, -background_radius, background_radius)
|
||||
background_mask = (background_mask - np.min(background_mask))
|
||||
background_mask = background_mask / np.max(background_mask)
|
||||
# ...:::!!!:::...
|
||||
contour_radius += 2
|
||||
contour_mask = np.abs(np.clip(dist_map, -contour_radius, contour_radius))
|
||||
contour_mask = contour_mask / np.max(contour_mask)
|
||||
return background_mask, contour_mask
|
||||
|
||||
|
||||
def mask_painter(input_image, input_mask, background_alpha=0.5, background_blur_radius=7, contour_width=3, contour_color=3, contour_alpha=1, mode='11'):
|
||||
"""
|
||||
Input:
|
||||
input_image: numpy array
|
||||
input_mask: numpy array
|
||||
background_alpha: transparency of background, [0, 1], 1: all black, 0: do nothing
|
||||
background_blur_radius: radius of background blur, must be odd number
|
||||
contour_width: width of mask contour, must be odd number
|
||||
contour_color: color index (in color map) of mask contour, 0: black, 1: white, >1: others
|
||||
contour_alpha: transparency of mask contour, [0, 1], if 0: no contour highlighted
|
||||
mode: painting mode, '00', no blur, '01' only blur contour, '10' only blur background, '11' blur both
|
||||
|
||||
Output:
|
||||
painted_image: numpy array
|
||||
"""
|
||||
assert input_image.shape[:2] == input_mask.shape, 'different shape'
|
||||
assert background_blur_radius % 2 * contour_width % 2 > 0, 'background_blur_radius and contour_width must be ODD'
|
||||
assert mode in ['00', '01', '10', '11'], 'mode should be 00, 01, 10, or 11'
|
||||
|
||||
# downsample input image and mask
|
||||
width, height = input_image.shape[0], input_image.shape[1]
|
||||
res = 1024
|
||||
ratio = min(1.0 * res / max(width, height), 1.0)
|
||||
input_image = cv2.resize(input_image, (int(height*ratio), int(width*ratio)))
|
||||
input_mask = cv2.resize(input_mask, (int(height*ratio), int(width*ratio)))
|
||||
|
||||
# 0: background, 1: foreground
|
||||
msk = np.clip(input_mask, 0, 1)
|
||||
|
||||
# generate masks for background and contour pixels
|
||||
background_radius = (background_blur_radius - 1) // 2
|
||||
contour_radius = (contour_width - 1) // 2
|
||||
generator_dict = {'00':mask_generator_00, '01':mask_generator_01, '10':mask_generator_10, '11':mask_generator_11}
|
||||
background_mask, contour_mask = generator_dict[mode](msk, background_radius, contour_radius)
|
||||
|
||||
# paint
|
||||
painted_image = vis_add_mask\
|
||||
(input_image, background_mask, contour_mask, color_list[0], color_list[contour_color], background_alpha, contour_alpha) # black for background
|
||||
|
||||
return painted_image
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
background_alpha = 0.7 # transparency of background 1: all black, 0: do nothing
|
||||
background_blur_radius = 31 # radius of background blur, must be odd number
|
||||
contour_width = 11 # contour width, must be odd number
|
||||
contour_color = 3 # id in color map, 0: black, 1: white, >1: others
|
||||
contour_alpha = 1 # transparency of background, 0: no contour highlighted
|
||||
|
||||
# load input image and mask
|
||||
input_image = np.array(Image.open('./test_img/painter_input_image.jpg').convert('RGB'))
|
||||
input_mask = np.array(Image.open('./test_img/painter_input_mask.jpg').convert('P'))
|
||||
|
||||
# paint
|
||||
overall_time_1 = 0
|
||||
overall_time_2 = 0
|
||||
overall_time_3 = 0
|
||||
overall_time_4 = 0
|
||||
overall_time_5 = 0
|
||||
|
||||
for i in range(50):
|
||||
t2 = time.time()
|
||||
painted_image_00 = mask_painter(input_image, input_mask, background_alpha, background_blur_radius, contour_width, contour_color, contour_alpha, mode='00')
|
||||
e2 = time.time()
|
||||
|
||||
t3 = time.time()
|
||||
painted_image_10 = mask_painter(input_image, input_mask, background_alpha, background_blur_radius, contour_width, contour_color, contour_alpha, mode='10')
|
||||
e3 = time.time()
|
||||
|
||||
t1 = time.time()
|
||||
painted_image = mask_painter(input_image, input_mask, background_alpha, background_blur_radius, contour_width, contour_color, contour_alpha)
|
||||
e1 = time.time()
|
||||
|
||||
t4 = time.time()
|
||||
painted_image_01 = mask_painter(input_image, input_mask, background_alpha, background_blur_radius, contour_width, contour_color, contour_alpha, mode='01')
|
||||
e4 = time.time()
|
||||
|
||||
t5 = time.time()
|
||||
painted_image_11 = mask_painter(input_image, input_mask, background_alpha, background_blur_radius, contour_width, contour_color, contour_alpha, mode='11')
|
||||
e5 = time.time()
|
||||
|
||||
overall_time_1 += (e1 - t1)
|
||||
overall_time_2 += (e2 - t2)
|
||||
overall_time_3 += (e3 - t3)
|
||||
overall_time_4 += (e4 - t4)
|
||||
overall_time_5 += (e5 - t5)
|
||||
|
||||
print(f'average time w gaussian: {overall_time_1/50}')
|
||||
print(f'average time w/o gaussian00: {overall_time_2/50}')
|
||||
print(f'average time w/o gaussian10: {overall_time_3/50}')
|
||||
print(f'average time w/o gaussian01: {overall_time_4/50}')
|
||||
print(f'average time w/o gaussian11: {overall_time_5/50}')
|
||||
|
||||
# save
|
||||
painted_image_00 = Image.fromarray(painted_image_00)
|
||||
painted_image_00.save('./test_img/painter_output_image_00.png')
|
||||
|
||||
painted_image_10 = Image.fromarray(painted_image_10)
|
||||
painted_image_10.save('./test_img/painter_output_image_10.png')
|
||||
|
||||
painted_image_01 = Image.fromarray(painted_image_01)
|
||||
painted_image_01.save('./test_img/painter_output_image_01.png')
|
||||
|
||||
painted_image_11 = Image.fromarray(painted_image_11)
|
||||
painted_image_11.save('./test_img/painter_output_image_11.png')
|
||||
@@ -0,0 +1,131 @@
|
||||
import os
|
||||
import re
|
||||
import random
|
||||
import time
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import logging
|
||||
import numpy as np
|
||||
from os import path as osp
|
||||
|
||||
def constant_init(module, val, bias=0):
|
||||
if hasattr(module, 'weight') and module.weight is not None:
|
||||
nn.init.constant_(module.weight, val)
|
||||
if hasattr(module, 'bias') and module.bias is not None:
|
||||
nn.init.constant_(module.bias, bias)
|
||||
|
||||
initialized_logger = {}
|
||||
def get_root_logger(logger_name='basicsr', log_level=logging.INFO, log_file=None):
|
||||
"""Get the root logger.
|
||||
The logger will be initialized if it has not been initialized. By default a
|
||||
StreamHandler will be added. If `log_file` is specified, a FileHandler will
|
||||
also be added.
|
||||
Args:
|
||||
logger_name (str): root logger name. Default: 'basicsr'.
|
||||
log_file (str | None): The log filename. If specified, a FileHandler
|
||||
will be added to the root logger.
|
||||
log_level (int): The root logger level. Note that only the process of
|
||||
rank 0 is affected, while other processes will set the level to
|
||||
"Error" and be silent most of the time.
|
||||
Returns:
|
||||
logging.Logger: The root logger.
|
||||
"""
|
||||
logger = logging.getLogger(logger_name)
|
||||
# if the logger has been initialized, just return it
|
||||
if logger_name in initialized_logger:
|
||||
return logger
|
||||
|
||||
format_str = '%(asctime)s %(levelname)s: %(message)s'
|
||||
stream_handler = logging.StreamHandler()
|
||||
stream_handler.setFormatter(logging.Formatter(format_str))
|
||||
logger.addHandler(stream_handler)
|
||||
logger.propagate = False
|
||||
|
||||
if log_file is not None:
|
||||
logger.setLevel(log_level)
|
||||
# add file handler
|
||||
# file_handler = logging.FileHandler(log_file, 'w')
|
||||
file_handler = logging.FileHandler(log_file, 'a') #Shangchen: keep the previous log
|
||||
file_handler.setFormatter(logging.Formatter(format_str))
|
||||
file_handler.setLevel(log_level)
|
||||
logger.addHandler(file_handler)
|
||||
initialized_logger[logger_name] = True
|
||||
return logger
|
||||
|
||||
|
||||
IS_HIGH_VERSION = [int(m) for m in list(re.findall(r"^([0-9]+)\.([0-9]+)\.([0-9]+)([^0-9][a-zA-Z0-9]*)?(\+git.*)?$",\
|
||||
torch.__version__)[0][:3])] >= [1, 12, 0]
|
||||
|
||||
def gpu_is_available():
|
||||
if IS_HIGH_VERSION:
|
||||
if torch.backends.mps.is_available():
|
||||
return True
|
||||
return True if torch.cuda.is_available() and torch.backends.cudnn.is_available() else False
|
||||
|
||||
def get_device(gpu_id=None):
|
||||
if gpu_id is None:
|
||||
gpu_str = ''
|
||||
elif isinstance(gpu_id, int):
|
||||
gpu_str = f':{gpu_id}'
|
||||
else:
|
||||
raise TypeError('Input should be int value.')
|
||||
|
||||
if IS_HIGH_VERSION:
|
||||
if torch.backends.mps.is_available():
|
||||
return torch.device('mps'+gpu_str)
|
||||
return torch.device('cuda'+gpu_str if torch.cuda.is_available() and torch.backends.cudnn.is_available() else 'cpu')
|
||||
|
||||
|
||||
def set_random_seed(seed):
|
||||
"""Set random seeds."""
|
||||
random.seed(seed)
|
||||
np.random.seed(seed)
|
||||
torch.manual_seed(seed)
|
||||
torch.cuda.manual_seed(seed)
|
||||
torch.cuda.manual_seed_all(seed)
|
||||
|
||||
|
||||
def get_time_str():
|
||||
return time.strftime('%Y%m%d_%H%M%S', time.localtime())
|
||||
|
||||
|
||||
def scandir(dir_path, suffix=None, recursive=False, full_path=False):
|
||||
"""Scan a directory to find the interested files.
|
||||
|
||||
Args:
|
||||
dir_path (str): Path of the directory.
|
||||
suffix (str | tuple(str), optional): File suffix that we are
|
||||
interested in. Default: None.
|
||||
recursive (bool, optional): If set to True, recursively scan the
|
||||
directory. Default: False.
|
||||
full_path (bool, optional): If set to True, include the dir_path.
|
||||
Default: False.
|
||||
|
||||
Returns:
|
||||
A generator for all the interested files with relative pathes.
|
||||
"""
|
||||
|
||||
if (suffix is not None) and not isinstance(suffix, (str, tuple)):
|
||||
raise TypeError('"suffix" must be a string or tuple of strings')
|
||||
|
||||
root = dir_path
|
||||
|
||||
def _scandir(dir_path, suffix, recursive):
|
||||
for entry in os.scandir(dir_path):
|
||||
if not entry.name.startswith('.') and entry.is_file():
|
||||
if full_path:
|
||||
return_path = entry.path
|
||||
else:
|
||||
return_path = osp.relpath(entry.path, root)
|
||||
|
||||
if suffix is None:
|
||||
yield return_path
|
||||
elif return_path.endswith(suffix):
|
||||
yield return_path
|
||||
else:
|
||||
if recursive:
|
||||
yield from _scandir(entry.path, suffix=suffix, recursive=recursive)
|
||||
else:
|
||||
continue
|
||||
|
||||
return _scandir(dir_path, suffix=suffix, recursive=recursive)
|
||||
@@ -0,0 +1,215 @@
|
||||
# paint masks, contours, or points on images, with specified colors
|
||||
import cv2
|
||||
import torch
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import copy
|
||||
import time
|
||||
|
||||
|
||||
def colormap(rgb=True):
|
||||
color_list = np.array(
|
||||
[
|
||||
0.000, 0.000, 0.000,
|
||||
1.000, 1.000, 1.000,
|
||||
1.000, 0.498, 0.313,
|
||||
0.392, 0.581, 0.929,
|
||||
0.000, 0.447, 0.741,
|
||||
0.850, 0.325, 0.098,
|
||||
0.929, 0.694, 0.125,
|
||||
0.494, 0.184, 0.556,
|
||||
0.466, 0.674, 0.188,
|
||||
0.301, 0.745, 0.933,
|
||||
0.635, 0.078, 0.184,
|
||||
0.300, 0.300, 0.300,
|
||||
0.600, 0.600, 0.600,
|
||||
1.000, 0.000, 0.000,
|
||||
1.000, 0.500, 0.000,
|
||||
0.749, 0.749, 0.000,
|
||||
0.000, 1.000, 0.000,
|
||||
0.000, 0.000, 1.000,
|
||||
0.667, 0.000, 1.000,
|
||||
0.333, 0.333, 0.000,
|
||||
0.333, 0.667, 0.000,
|
||||
0.333, 1.000, 0.000,
|
||||
0.667, 0.333, 0.000,
|
||||
0.667, 0.667, 0.000,
|
||||
0.667, 1.000, 0.000,
|
||||
1.000, 0.333, 0.000,
|
||||
1.000, 0.667, 0.000,
|
||||
1.000, 1.000, 0.000,
|
||||
0.000, 0.333, 0.500,
|
||||
0.000, 0.667, 0.500,
|
||||
0.000, 1.000, 0.500,
|
||||
0.333, 0.000, 0.500,
|
||||
0.333, 0.333, 0.500,
|
||||
0.333, 0.667, 0.500,
|
||||
0.333, 1.000, 0.500,
|
||||
0.667, 0.000, 0.500,
|
||||
0.667, 0.333, 0.500,
|
||||
0.667, 0.667, 0.500,
|
||||
0.667, 1.000, 0.500,
|
||||
1.000, 0.000, 0.500,
|
||||
1.000, 0.333, 0.500,
|
||||
1.000, 0.667, 0.500,
|
||||
1.000, 1.000, 0.500,
|
||||
0.000, 0.333, 1.000,
|
||||
0.000, 0.667, 1.000,
|
||||
0.000, 1.000, 1.000,
|
||||
0.333, 0.000, 1.000,
|
||||
0.333, 0.333, 1.000,
|
||||
0.333, 0.667, 1.000,
|
||||
0.333, 1.000, 1.000,
|
||||
0.667, 0.000, 1.000,
|
||||
0.667, 0.333, 1.000,
|
||||
0.667, 0.667, 1.000,
|
||||
0.667, 1.000, 1.000,
|
||||
1.000, 0.000, 1.000,
|
||||
1.000, 0.333, 1.000,
|
||||
1.000, 0.667, 1.000,
|
||||
0.167, 0.000, 0.000,
|
||||
0.333, 0.000, 0.000,
|
||||
0.500, 0.000, 0.000,
|
||||
0.667, 0.000, 0.000,
|
||||
0.833, 0.000, 0.000,
|
||||
1.000, 0.000, 0.000,
|
||||
0.000, 0.167, 0.000,
|
||||
0.000, 0.333, 0.000,
|
||||
0.000, 0.500, 0.000,
|
||||
0.000, 0.667, 0.000,
|
||||
0.000, 0.833, 0.000,
|
||||
0.000, 1.000, 0.000,
|
||||
0.000, 0.000, 0.167,
|
||||
0.000, 0.000, 0.333,
|
||||
0.000, 0.000, 0.500,
|
||||
0.000, 0.000, 0.667,
|
||||
0.000, 0.000, 0.833,
|
||||
0.000, 0.000, 1.000,
|
||||
0.143, 0.143, 0.143,
|
||||
0.286, 0.286, 0.286,
|
||||
0.429, 0.429, 0.429,
|
||||
0.571, 0.571, 0.571,
|
||||
0.714, 0.714, 0.714,
|
||||
0.857, 0.857, 0.857
|
||||
]
|
||||
).astype(np.float32)
|
||||
color_list = color_list.reshape((-1, 3)) * 255
|
||||
if not rgb:
|
||||
color_list = color_list[:, ::-1]
|
||||
return color_list
|
||||
|
||||
|
||||
color_list = colormap()
|
||||
color_list = color_list.astype('uint8').tolist()
|
||||
|
||||
|
||||
def vis_add_mask(image, mask, color, alpha):
|
||||
color = np.array(color_list[color])
|
||||
mask = mask > 0.5
|
||||
image[mask] = image[mask] * (1-alpha) + color * alpha
|
||||
return image.astype('uint8')
|
||||
|
||||
def point_painter(input_image, input_points, point_color=5, point_alpha=0.9, point_radius=15, contour_color=2, contour_width=5):
|
||||
h, w = input_image.shape[:2]
|
||||
point_mask = np.zeros((h, w)).astype('uint8')
|
||||
for point in input_points:
|
||||
point_mask[point[1], point[0]] = 1
|
||||
|
||||
kernel = cv2.getStructuringElement(2, (point_radius, point_radius))
|
||||
point_mask = cv2.dilate(point_mask, kernel)
|
||||
|
||||
contour_radius = (contour_width - 1) // 2
|
||||
dist_transform_fore = cv2.distanceTransform(point_mask, cv2.DIST_L2, 3)
|
||||
dist_transform_back = cv2.distanceTransform(1-point_mask, cv2.DIST_L2, 3)
|
||||
dist_map = dist_transform_fore - dist_transform_back
|
||||
# ...:::!!!:::...
|
||||
contour_radius += 2
|
||||
contour_mask = np.abs(np.clip(dist_map, -contour_radius, contour_radius))
|
||||
contour_mask = contour_mask / np.max(contour_mask)
|
||||
contour_mask[contour_mask>0.5] = 1.
|
||||
|
||||
# paint mask
|
||||
painted_image = vis_add_mask(input_image.copy(), point_mask, point_color, point_alpha)
|
||||
# paint contour
|
||||
painted_image = vis_add_mask(painted_image.copy(), 1-contour_mask, contour_color, 1)
|
||||
return painted_image
|
||||
|
||||
def mask_painter(input_image, input_mask, mask_color=5, mask_alpha=0.7, contour_color=1, contour_width=3):
|
||||
assert input_image.shape[:2] == input_mask.shape, 'different shape between image and mask'
|
||||
# 0: background, 1: foreground
|
||||
mask = np.clip(input_mask, 0, 1)
|
||||
contour_radius = (contour_width - 1) // 2
|
||||
|
||||
dist_transform_fore = cv2.distanceTransform(mask, cv2.DIST_L2, 3)
|
||||
dist_transform_back = cv2.distanceTransform(1-mask, cv2.DIST_L2, 3)
|
||||
dist_map = dist_transform_fore - dist_transform_back
|
||||
# ...:::!!!:::...
|
||||
contour_radius += 2
|
||||
contour_mask = np.abs(np.clip(dist_map, -contour_radius, contour_radius))
|
||||
contour_mask = contour_mask / np.max(contour_mask)
|
||||
contour_mask[contour_mask>0.5] = 1.
|
||||
|
||||
# paint mask
|
||||
painted_image = vis_add_mask(input_image.copy(), mask.copy(), mask_color, mask_alpha)
|
||||
# paint contour
|
||||
painted_image = vis_add_mask(painted_image.copy(), 1-contour_mask, contour_color, 1)
|
||||
|
||||
return painted_image
|
||||
|
||||
def background_remover(input_image, input_mask):
|
||||
"""
|
||||
input_image: H, W, 3, np.array
|
||||
input_mask: H, W, np.array
|
||||
|
||||
image_wo_background: PIL.Image
|
||||
"""
|
||||
assert input_image.shape[:2] == input_mask.shape, 'different shape between image and mask'
|
||||
# 0: background, 1: foreground
|
||||
mask = np.expand_dims(np.clip(input_mask, 0, 1), axis=2)*255
|
||||
image_wo_background = np.concatenate([input_image, mask], axis=2) # H, W, 4
|
||||
image_wo_background = Image.fromarray(image_wo_background).convert('RGBA')
|
||||
|
||||
return image_wo_background
|
||||
|
||||
if __name__ == '__main__':
|
||||
input_image = np.array(Image.open('images/painter_input_image.jpg').convert('RGB'))
|
||||
input_mask = np.array(Image.open('images/painter_input_mask.jpg').convert('P'))
|
||||
|
||||
# example of mask painter
|
||||
mask_color = 3
|
||||
mask_alpha = 0.7
|
||||
contour_color = 1
|
||||
contour_width = 5
|
||||
|
||||
# save
|
||||
painted_image = Image.fromarray(input_image)
|
||||
painted_image.save('images/original.png')
|
||||
|
||||
painted_image = mask_painter(input_image, input_mask, mask_color, mask_alpha, contour_color, contour_width)
|
||||
# save
|
||||
painted_image = Image.fromarray(input_image)
|
||||
painted_image.save('images/original1.png')
|
||||
|
||||
# example of point painter
|
||||
input_image = np.array(Image.open('images/painter_input_image.jpg').convert('RGB'))
|
||||
input_points = np.array([[500, 375], [70, 600]]) # x, y
|
||||
point_color = 5
|
||||
point_alpha = 0.9
|
||||
point_radius = 15
|
||||
contour_color = 2
|
||||
contour_width = 5
|
||||
painted_image_1 = point_painter(input_image, input_points, point_color, point_alpha, point_radius, contour_color, contour_width)
|
||||
# save
|
||||
painted_image = Image.fromarray(painted_image_1)
|
||||
painted_image.save('images/point_painter_1.png')
|
||||
|
||||
input_image = np.array(Image.open('images/painter_input_image.jpg').convert('RGB'))
|
||||
painted_image_2 = point_painter(input_image, input_points, point_color=9, point_radius=20, contour_color=29)
|
||||
# save
|
||||
painted_image = Image.fromarray(painted_image_2)
|
||||
painted_image.save('images/point_painter_2.png')
|
||||
|
||||
# example of background remover
|
||||
input_image = np.array(Image.open('images/original.png').convert('RGB'))
|
||||
image_wo_background = background_remover(input_image, input_mask) # return PIL.Image
|
||||
image_wo_background.save('images/image_wo_background.png')
|
||||
Reference in New Issue
Block a user