yes, 3 files below.
(service.py, image_basic_processing.py, segment_anything.py)
-------------service.py---------------
import numpy as np
import bentoml
from
bentoml.io import JSON, Multipart
from pathlib import Path
import time
from runners.image_basic_processing import BasicPreprocessingRunnable
from runners.segment_anything import SegmentAnythingRunner
# Setting runner, service
basic_preprocessing_runner = bentoml.Runner(BasicPreprocessingRunnable, name=‘basic_preprocessor’)
segment_anything_runner = bentoml.Runner(SegmentAnythingRunner, name=‘segment_anything_runner’)
svc = bentoml.Service(“embryo_segment”, runners=[segment_anything_runner, basic_preprocessing_runner])
@svc.api(input=JSON(), output=JSON())
async def predict(input_data: JSON()):
“”"
url : 이미지 url
embryo_num : 요청 바운딩 박스 개수 (or 배아 개수)
“”"
with bentoml.monitor(“embryo_segment”) as mon:
### 이미지 데이터 처리
url = input_data.get(“url”, None)
image = await basic_preprocessing_runner.processing.async_run(url)
# cropped_image_list = await microscope_processing_runner.img_cropping.async_run(image) ## rule-based cropping
start = time.time()
masks = await segment_anything_runner.segment.async_run(image)
# embryo_num = 1 # embryo_num
embryo_num = input_data.get(“embryo_num”, None)
mon.log(url, name=‘url’, role=‘image_path’, data_type=“str”)
mon.log(embryo_num, name=‘embryo_num’, role=‘request bbox count’, data_type=“numerical”)
if embryo_num is None or embryo_num == 0:
return ‘request bbox count is fault... required bbox count >= 1’
if type(embryo_num) is not int:
embryo_num = int(embryo_num)
if embryo_num > 4:
embryo_num = 4 # max
bbox_from_sam = await segment_anything_runner.find_the_segmentation.async_run(embryo_num, masks)
### cropped image save for test
# await basic_preprocessing_runner.save_cropped_image.async_run(url, image, bbox_from_sam)
end = time.time()
print(‘segmentation time: ’,end - start)
predict_result = {
# ‘preg’: output_tensor.tolist()[0][0]
‘bbox_coordinates’: bbox_from_sam
}
print(‘predict_result:’, predict_result)
mon.log(bbox_from_sam, name=“pred”, role=“prediction”, data_type=“list”)
# print(‘predict_result: ’, predict_result)
return predict_result
-------------segment_anything.py-----------
import bentoml
import numpy as np
import cv2
from segment_anything import sam_model_registry, SamAutomaticMaskGenerator
class SegmentAnythingRunner(bentoml.Runnable):
SUPPORTED_RESOURCES = (“cpu”,“
nvidia.com/gpu”,)
SUPPORTS_CPU_MULTI_THREADING = True
def __init__(self):
# SegmentAnythingRunner는 서버를 띄울 때, 1회만 호출 됨
self.device = “cuda:0”
self.sam_checkpoint = “./SAM_weights”
self.model_type = “vit_h”
self.sam = sam_model_registry[self.model_type](checkpoint=self.sam_checkpoint)
self.sam.to(self.device)
self.kernel = np.ones((5,5), np.uint8)
self.mask_generator = SamAutomaticMaskGenerator(
model=self.sam,
points_per_side=10,
pred_iou_thresh=0.90,
stability_score_thresh=0.90,
crop_n_layers=1,
crop_n_points_downscale_factor=2,
min_mask_region_area=100000, # Requires open-cv to run post-processing
)
@bentoml.Runnable.method(batchable=False)
def segment(self, input_tensor: np.ndarray):
# arr = np.asarray(input_tensor, dtype=np.uint8) #
return self.mask_generator.generate(input_tensor)
@bentoml.Runnable.method(batchable=False)
def find_the_segmentation(self, embryo_num, masks):
“”"
CASE 1. 당장 배포, 단일 배아 전제, 크롭하기
- 1인 경우 1번 인덱스만 리턴함.
CASE 2. 멀티 배아인 경우 크롭하기 (소프트웨어팀 화면개발이 완료되면)
- 아래 embryo_num if else 구문은 필요 없어짐
- embryo 개수를 입력으로 받고, 그때는 embryo_num + 1 로 리턴한다.
“”"
def make_bbox(index_):
sorted_anns = sorted(masks, key=(lambda x: x[‘area’]), reverse=True)
# print(‘the segmentation area has {} pixels’.format(sorted_anns[index_][‘area’]))
mask = np.expand_dims(sorted_anns[index_][‘segmentation’],axis=-1)
mask = mask.astype(‘uint8’).copy()
# dilate = cv2.dilate(mask, kernel, iterations=20)
dilate = cv2.dilate(mask, self.kernel, iterations=3)
cnts,_ = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#print(cnts[0])
h,w = dilate.shape[:2]
for c in cnts:
x,y,w,h = cv2.boundingRect(c)
# ROI = image[y:y+h, x:x+w] # 이미지 Cropping
# bbox_list.append([x,y,x+w,y+h])
return [x,y,x+w,y+h]
#### 실행 로직 ####
# if embryo_num == 1:
# index_list = [1]
# else:
# index_list = [x for x in range(embryo_num + 1)]
index_list = [x for x in range(embryo_num + 1)]
bbox_list = []
for index_ in index_list:
try:
bbox_coordinate = make_bbox(index_)
bbox_list.append(bbox_coordinate)
except IndexError:
# index_: 0 은 무조건 있다고 가정함
# 추후 소프트웨어 개발팀에서 배아 선택 화면이 개발이 되면 indexError가 났을 때, 바로 bbox_list를 return 해도 됨.
# 왜냐하면 0 index 부터 반복되고 0인 항상 있기 때문에
if len(bbox_list) == 0:
print(‘embryo_num가 1인경우 index error’)
bbox_coordinate = make_bbox(0)
bbox_list.append(bbox_coordinate)
return bbox_list
return bbox_list
return bbox_list
--------------image_basic_processing.py--------
import cv2
import numpy as np
import bentoml
from urllib.request import urlopen
from urllib.error import URLError, HTTPError
import os
from datetime import datetime
class BasicPreprocessingRunnable(bentoml.Runnable):
# SUPPORTED_RESOURCES = (“
nvidia.com/gpu”,)
SUPPORTED_RESOURCES = (“cpu”)
SUPPORTS_CPU_MULTI_THREADING = True
@bentoml.Runnable.method(batchable=False)
def processing(self, url) -> np.ndarray:
try:
resp = urlopen(url)
encoded_img = np.fromstring(resp.read(), dtype = np.uint8)
img = cv2.imdecode(encoded_img, cv2.IMREAD_COLOR)
# img = PILImage.open(urlopen(url)).convert(‘RGB’) # img.size 로 찍으면 (w,h) 로만 나옴 (channel 정보가 안보임)
# img = img.resize((224,224), PILImage.BICUBIC)
if len(img.shape) == 3:
h, w, c = img.shape
if c == 4:
img = img[
,,:-1]
if len(img.shape) == 4:
# img = img[
,,:-1]
img = img[
,,:,0]
rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
### cropping 알고리즘에서 원본영상으로 처리해서 여기서 resize 안함
# img = cv2.resize(img, (224,224))
# print(‘ulr processing: ’, type(img), img.shape)
return rgb_img
except URLError as e:
print(e.reason)
return e