diff --git a/README.md b/README.md new file mode 100644 index 0000000..a35f5ba --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# Lumen Profiler + +Lumen Profiler is a tool for analyzing the lumenal profile from endoscopic movie + +## lumen_profiler.py + +### Installation + +```bash +pip install -r requirements.txt +``` + +### Usage + +```bash +python lumen_profiler.py +``` diff --git a/lumen_profiler.py b/lumen_profiler.py new file mode 100644 index 0000000..54c8e7e --- /dev/null +++ b/lumen_profiler.py @@ -0,0 +1,152 @@ +import csv + +import cv2 +import numpy as np + +area_ratio = 80 +enable_display = True + + +def on_slider(pos): + global area_ratio + area_ratio = pos + + +win_org = "original image" +win_value = "value image" + +cap = cv2.VideoCapture("サンプル動画_気管支鏡.mp4") + +if enable_display: + cv2.namedWindow(win_org, cv2.WINDOW_AUTOSIZE) + cv2.namedWindow(win_value, cv2.WINDOW_AUTOSIZE) + cv2.createTrackbar("area", win_org, area_ratio, 300, on_slider) + +frame_count = 0 +csv_data = [] +while True: + # 画像読み込み + ret, frame = cap.read() + if not ret: + # break + cap.set(cv2.CAP_PROP_POS_FRAMES, 0) + frame_count = 0 + print("rewind") + continue + # 輝度画像生成 + hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) + val_img = hsv[:, :, 2] + # 累積ヒストグラム算出 + hist = cv2.calcHist([val_img], [0], None, [256], [0, 256]) + # acc_hist = np.zeros(256, np.float32) + # acc_hist[0] = hist[0] + # しきい値決定 + thres = -1 + sum = 0 + for i in range(0, 256): + sum += hist[i] + # acc_hist[i] = acc_hist[i - 1] + hist[i] + if thres < 0 and sum > (val_img.size * area_ratio / 1000): + thres = i + break + # 気道のマスク生成 + val_img = cv2.GaussianBlur(val_img, (13, 13), 5.0) + mask = cv2.threshold(val_img, thres, 255, cv2.THRESH_BINARY_INV)[1] + # mask = cv2.threshold(val_img, area_ratio, 255, cv2.THRESH_BINARY_INV)[1] + # kernel = np.ones((5, 5), np.uint8) + # mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) + + retval, labels, stats, centroids = cv2.connectedComponentsWithStats(mask) + target_label = -1 + min_dist = 0 + center = [frame.shape[1] / 2, frame.shape[0] / 2] + # print([stats[i, cv2.CC_STAT_AREA] for i in range(retval)]) + if retval > 1: + for i in range(1, retval): + dist = np.linalg.norm(centroids[i] - center, 2) + if stats[i, cv2.CC_STAT_AREA] > 200 and ( + dist < min_dist or target_label < 0 + ): + min_dist = dist + target_label = i + + selected_mask = np.zeros(mask.shape, np.uint8) + selected_mask[labels == target_label] = 255 + + contours, hierarchy = cv2.findContours( + selected_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE + ) + + etime = frame_count * 1.0 / 30.0 + cv2.putText( + frame, + "frame%4d time %.3fs" % (frame_count, etime), + (10, 25), + cv2.FONT_HERSHEY_TRIPLEX, + 0.7, + (255, 0, 0), + 1, + ) + cv2.putText( + frame, + "area ratio=%.1f %%" % (area_ratio / 10), + (10, 50), + cv2.FONT_HERSHEY_TRIPLEX, + 0.7, + (255, 0, 0), + 1, + ) + + if len(contours) > 0: + # for i in range(len(contours)): + cv2.drawContours(frame, contours, 0, (0, 255, 255), 3) + + area = cv2.contourArea(contours[0]) + perimeter = cv2.arcLength(contours[0], True) + if perimeter > 0: + circle_level = 4.0 * np.pi * area / (perimeter * perimeter) + else: + circle_level = 0 + # print(circle_level) + cv2.putText( + frame, + "circle level=%.1f %%" % (circle_level * 100), + (10, 75), + cv2.FONT_HERSHEY_TRIPLEX, + 0.7, + (255, 0, 0), + 1, + ) + cv2.putText( + hsv, + "threshold=%d" % (thres), + (10, 30), + cv2.FONT_HERSHEY_TRIPLEX, + 0.7, + (0, 0, 0), + 1, + ) + + # frame[labels == target_label] = [0, 0, 0] + + # cv2.imwrite("output/cl_%04d.jpg" % frame_count, frame) + + csv_data.append([frame_count, etime, area_ratio / 10, thres, circle_level * 100]) + + if enable_display: + cv2.imshow(win_org, frame) + cv2.imshow(win_value, hsv[:, :, 2]) + + if cv2.waitKey(30) & 0xFF == 27: + break + frame_count += 1 + +# with open("output/analysis.csv", "w", newline="") as f: +# writer = csv.writer(f) +# writer.writerow( +# ["frame", "time(s)", "area ratio(%)", "threshold", "circle level(%)"] +# ) +# writer.writerows(csv_data) + +cap.release() +cv2.destroyAllWindows() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0dd006b --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +opencv-python