-
Notifications
You must be signed in to change notification settings - Fork 0
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
1 parent
1dec645
commit e2fec8f
Showing
9 changed files
with
1,124 additions
and
413 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,4 @@ | ||
#!/bin/bash | ||
cd work_image_data | ||
shopt -s extglob | ||
rm !(test.png) |
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,316 @@ | ||
"""ブロックでとレジャーのエリア情報を取得するモジュール | ||
参考資料: | ||
NOTE:HSV空間についての参考ページ | ||
https://www.peko-step.com/html/hsv.html | ||
NOTE:HSV値調べる | ||
https://yanohirota.com/image-spuit/ | ||
実行コマンド: | ||
rear_camera/ 直下で実行することを想定 | ||
$ make get_area | ||
""" | ||
import numpy as np | ||
import cv2 | ||
import os | ||
from enum import Enum | ||
|
||
script_dir = os.path.dirname(os.path.abspath(__file__)) # /src | ||
PROJECT_DIR_PATH = os.path.dirname(script_dir) # /rear_camera_py | ||
|
||
|
||
class Color(Enum): | ||
"""色クラス. | ||
色はBGR | ||
""" | ||
BLACK = [0, 0, 0] | ||
RED = [0, 0, 255] | ||
YELLOW = [0, 255, 255] | ||
GREEN = [0, 255, 0] | ||
BLUE = [255, 0, 0] | ||
WHITE = [255, 255, 255] | ||
|
||
|
||
class GetAreaInfo: | ||
RED1 = [(0, 15), (90, 255), (100, 255)] | ||
YELLOW = [(16, 30), (50, 255), (150, 255)] | ||
GREEN = [(31, 100), (60, 255), (40, 255)] | ||
BLUE = [(101, 150), (110, 255), (70, 255)] | ||
RED2 = [(151, 180), (80, 255), (90, 255)] | ||
|
||
# OpenCVのHSV空間の上限はどれも255 | ||
# WHITE_threshold = [100, 50, 145] # 彩度 HSV (以上,以下,以上) | ||
BLACK = [255, 110, 135] # HSV値の上限 | ||
|
||
def __init__(self, image_name, image_dir_path) -> None: | ||
"""GetAreaInfoのコンストラクタ.""" | ||
self.image_name = image_name | ||
self.image_dir_path = image_dir_path | ||
self.course_info = np.zeros(16).reshape(4, 4) | ||
self.basis_angle_deg = None | ||
self.block_area_height = 240 | ||
|
||
def detect_black(self, hsv_img): | ||
"""黒線を抽出する関数. | ||
Args: | ||
hsv_img: hsvに変換した画像 | ||
""" | ||
# 処理結果を保持する配列を宣言(色を白で初期化) | ||
black_line_img = np.full_like(hsv_img, 255, dtype=np.uint8) | ||
|
||
# self.BLACK 以下のHSV値をColor.BLACK.valueに変換 | ||
black_line_img[np.where((hsv_img[:, :, 1] <= self.BLACK[1]) & ( | ||
hsv_img[:, :, 2] <= self.BLACK[2]))] = Color.BLACK.value | ||
|
||
return black_line_img | ||
|
||
def change_color(self, hsv_img, changed_color_img, search_color, color_value): | ||
"""一定の範囲のHSV値を指定したHSV値に統一する関数. | ||
Args: | ||
hsv_img : HSV値に変換した画像 | ||
changed_color_img: 統一した値を書き込む画像 | ||
search_color: 一定の範囲を示すHSV値 (self.RED1などを指定) | ||
color_value: 統一するHSV値 (Color.RED.valueなど) | ||
""" | ||
changed_color_img[np.where((search_color[0][0] <= hsv_img[:, :, 0]) & | ||
(hsv_img[:, :, 0] <= search_color[0][1]) & | ||
(search_color[1][0] <= hsv_img[:, :, 1]) & | ||
(hsv_img[:, :, 1] <= search_color[1][1]) & | ||
(search_color[2][0] <= hsv_img[:, :, 2]) & | ||
(hsv_img[:, :, 2] <= search_color[2][1])) | ||
] = color_value | ||
return changed_color_img | ||
|
||
def detect_line(self, img, save_img_name, length_threshold=200): | ||
"""線分検出を行う関数. | ||
Args: | ||
img: 線分検出を行う画像 | ||
save_img_name: 線分検出を行った保存画像の名前 | ||
Return: | ||
lines: 検出したライン | ||
max_index: linesの中の最長ラインのインデックス | ||
""" | ||
## 線分検出 ## | ||
fast_line_detector = cv2.ximgproc.createFastLineDetector( | ||
length_threshold=length_threshold, | ||
distance_threshold=1.41421356, | ||
canny_th1=50, | ||
canny_th2=50, | ||
canny_aperture_size=3, | ||
do_merge=False) | ||
|
||
# グレースケール化 | ||
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) | ||
|
||
lines = fast_line_detector.detect(gray) | ||
if lines is None: | ||
return None, None | ||
|
||
# 検出した線を描画する配列を作成(色を白で初期化) | ||
result_line_img = np.full_like(img, 255, dtype=np.uint8) | ||
|
||
# 最も長い線分を見つける | ||
max_length = 0 | ||
max_length_index = -1 | ||
for i, line in enumerate(lines): | ||
x1, y1, x2, y2 = map(int, line[0]) | ||
length = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) | ||
if length > max_length: | ||
max_length = length | ||
max_length_index = i | ||
|
||
# 検出した線の描画 | ||
for i, line in enumerate(lines): | ||
x1, y1, x2, y2 = map(int, line[0]) | ||
if i == max_length_index: | ||
# 最も長い線は青で表示 | ||
cv2.line(result_line_img, (x1, y1), (x2, y2), (255, 0, 0), 3) | ||
else: | ||
# 他の線を赤で表示 | ||
cv2.line(result_line_img, (x1, y1), (x2, y2), (0, 0, 255), 3) | ||
save_path = os.path.join(self.image_dir_path, save_img_name) | ||
cv2.imwrite(save_path, result_line_img) | ||
|
||
return lines, max_length_index | ||
|
||
def predict_course_info(self, black_lines, changed_color_img): | ||
"""エリア情報を生成する関数. | ||
Args: | ||
black_lines: 線分を格納した配列(x1, y1, x2, y2) | ||
changed_color_img: 6色画像 | ||
""" | ||
angle_threshold = 3 | ||
print("changed_color_img.shape", changed_color_img.shape) | ||
black_lines = black_lines[:, 0] | ||
|
||
print("black_lines.shape", black_lines.shape) | ||
|
||
save_img = np.zeros_like(changed_color_img, dtype=np.uint8) | ||
|
||
print("self.basis_angle_deg", self.basis_angle_deg) | ||
|
||
y_max = 0 | ||
front_line_index = 0 | ||
for i, line in enumerate(black_lines): | ||
x1, y1, x2, y2 = map(int, line) | ||
# 度数を求める | ||
a = ((y2 - y1) / (x2 - x1)) | ||
angle = np.degrees(np.arctan2(a, 1)) | ||
if np.abs(angle - self.basis_angle_deg) < angle_threshold: | ||
y_max = max(y1, y2) | ||
front_line_index = i | ||
|
||
for i, line in enumerate(black_lines): | ||
x1, y1, x2, y2 = map(int, line) | ||
if i == front_line_index: | ||
cv2.line(changed_color_img, (x1, y1), (x2, y2), (255, 0, 0), 3) # BGR | ||
else: | ||
cv2.line(changed_color_img, (x1, y1), (x2, y2), (0, 0, 255), 3) # BGR | ||
|
||
x1, y1, x2, y2 = black_lines[front_line_index] | ||
# 直線の方程式 y = ax + b を解く | ||
a = ((y2 - y1) / (x2 - x1)) | ||
# 度数を求める | ||
b = y1 - a * x1 | ||
mask_width = 80 | ||
for x in range(changed_color_img.shape[1]-mask_width): | ||
y = int(a * x + b) | ||
changed_color_img[y-20:y+20, x:x+mask_width] = Color.GREEN.value | ||
|
||
mask = changed_color_img[y-20:y+20, x:x+mask_width] | ||
black_num = np.where( | ||
(mask[:, :, 0] == Color.BLACK.value[0]) & | ||
(mask[:, :, 1] == Color.BLACK.value[1]) & | ||
(mask[:, :, 2] == Color.BLACK.value[2])) | ||
print(f"len(black_num): {len(black_num)}") | ||
break | ||
|
||
save_path = os.path.join(self.image_dir_path, "area_info_"+self.image_name) | ||
cv2.imwrite(save_path, changed_color_img) | ||
|
||
# for x in range(changed_color_img.shape[1]-mask_width): | ||
# y = int(a * x + b) | ||
# changed_color_img[int(a * x + b + 20), x] = Color.GREEN.value | ||
# break | ||
|
||
def start(self) -> None: | ||
"""画像を6色画像に変換する関数. | ||
Args: | ||
game_area_img (cv2.Mat): ゲームエリア画像 | ||
save_path (str): 出力画像ファイルの保存パス | ||
""" | ||
# 画像読み込み | ||
image_path = os.path.join(self.image_dir_path, self.image_name) | ||
course_img = cv2.imread(image_path) | ||
|
||
# BGR色空間からHSV色空間への変換 | ||
game_area_img = cv2.cvtColor(course_img, cv2.COLOR_BGR2HSV) | ||
|
||
# 処理結果を保持する配列を宣言(色を白で初期化) | ||
processed_img = np.full_like(game_area_img, 255, dtype=np.uint8) | ||
|
||
# 緑検出 | ||
processed_img = self.change_color( | ||
game_area_img, processed_img, self.GREEN, Color.BLACK.value) | ||
|
||
# 緑の場所を検出 | ||
save_path = os.path.join(self.image_dir_path, "green_area_"+self.image_name) | ||
# cv2.imwrite(save_path, processed_img) | ||
|
||
# 線分を検出 | ||
lines, max_index = self.detect_line(processed_img, | ||
"detected_line_green_area_"+self.image_name, | ||
length_threshold=200) | ||
x1, y1, x2, y2 = map(int, lines[max_index][0]) # x1, y1, x2, y2 | ||
# 直線の方程式 y = ax + b を解く | ||
a = ((y2 - y1) / (x2 - x1)) | ||
# 度数を求める | ||
self.basis_angle_deg = np.degrees(np.arctan2(a, 1)) | ||
b = y1 - a * x1 - 5 | ||
|
||
# 直線より下の部分を黒にする | ||
for x in range(course_img.shape[1]): | ||
course_img[int(a * x + b):, x] = Color.WHITE.value # 黒に変換 | ||
course_img[:int(a * x + b - self.block_area_height), x] = Color.WHITE.value # 黒に変換 | ||
save_path = os.path.join(self.image_dir_path, "unnecessary_area_"+self.image_name) | ||
cv2.imwrite(save_path, course_img) | ||
|
||
# BGR色空間からHSV色空間への変換 | ||
game_area_img = cv2.cvtColor(course_img, cv2.COLOR_BGR2HSV) | ||
|
||
# # 処理結果を保持する配列を宣言(色を白で初期化) | ||
changed_color_img = np.full_like(game_area_img, 255, dtype=np.uint8) | ||
|
||
# 処理結果を保持する配列を宣言(色を黒で初期化) | ||
# changed_color_img = np.zeros_like(game_area_img, dtype=np.uint8) | ||
|
||
# 赤1,2 | ||
changed_color_img = self.change_color( | ||
game_area_img, changed_color_img, self.RED1, Color.BLACK.value) | ||
changed_color_img = self.change_color( | ||
game_area_img, changed_color_img, self.RED2, Color.BLACK.value) | ||
|
||
# 黄 | ||
changed_color_img = self.change_color( | ||
game_area_img, changed_color_img, self.YELLOW, Color.BLACK.value) | ||
|
||
# 緑 | ||
changed_color_img = self.change_color( | ||
game_area_img, changed_color_img, self.GREEN, Color.BLACK.value) | ||
|
||
# 青 | ||
changed_color_img = self.change_color( | ||
game_area_img, changed_color_img, self.BLUE, Color.BLACK.value) | ||
|
||
# 6色画像を保存 | ||
save_path = os.path.join(self.image_dir_path, "changed_color_"+self.image_name) | ||
cv2.imwrite(save_path, changed_color_img) | ||
# """ | ||
|
||
# ブロックエリアの黒ラインを検出 | ||
black_line_img = self.detect_black(game_area_img) | ||
save_path = os.path.join(self.image_dir_path, "black_line_"+self.image_name) | ||
cv2.imwrite(save_path, black_line_img) | ||
|
||
black_lines, max_index = self.detect_line(black_line_img, | ||
"detected_line_black_img.png", | ||
length_threshold=140) | ||
|
||
# エリア情報を生成 | ||
self.predict_course_info(black_lines, changed_color_img) | ||
|
||
return self.course_info | ||
|
||
|
||
if __name__ == "__main__": | ||
# 処理時間計測用 | ||
import time | ||
start = time.time() | ||
|
||
# 作業用の読み込みや保存用のディレクトリパス | ||
work_dir_path = os.path.join(PROJECT_DIR_PATH, "work_image_data") | ||
work_dir_path2 = os.path.join(PROJECT_DIR_PATH, "work_image_data2") | ||
|
||
# 画像ファイル名 | ||
image_name = "test.png" | ||
|
||
cc = GetAreaInfo(image_name, work_dir_path) | ||
cc.start() | ||
|
||
# """ | ||
cc2 = GetAreaInfo(image_name, work_dir_path2) | ||
cc2.start() | ||
# """ | ||
|
||
# 処理時間計測用 | ||
execute_time = time.time() - start | ||
print(f"実行時間: {str(execute_time)[:5]}s") | ||
|
||
print("完了!") |
Oops, something went wrong.