質問: お世話になっております。 今回は、python3 + opencv + tensorflow にて以下のようなことをやりたいと考えているのですが どのようにすれば実現できるのか分からない為、ご質問させて下さい。 **(やりたいこと)** 1.USBカメラで撮影した静止画の中にある色の付いたシールの個数を色別にカウントしたい。 ⇒赤:4、青:3、黄:3、緑:3、白:3 など ![イメージ説明](https://teratail.storage.googleapis.com/uploads/contributed_images/2c5691e8218f842872809b764effc829.jpeg) 2.1.の静止画でシールを貼ってある対象の太さを知りたい(画像はシールを貼ってあるものと仮定する) ![イメージ説明](https://teratail.storage.googleapis.com/uploads/contributed_images/9a472f2fd56c9d395295fcca2d16ebc1.jpeg) イメージとしては 撮影⇒設備配管の径とその本数、太さ、シールの色が知りたいです。 シールの色は配管の太さによって変えます 赤:100mm 青:80mm などです。 OpenCVとtensorflowを使えば出来そうだと思っておりますが OpenCVを殆ど使ったことがない為、どういった具合で処理をかけばいいのかさっぱり分かりません。 こんな関数あるよとか、こんなライブラリ使えば直ぐできるよとか、こんな参考HPあるよといった ありましたら、ご教授頂ければと思います。 また、tensorflowもこう言ったニューラルネットワーク構築すると精度上がるよなどアドバイス頂けますと 大変助かります。 どうぞよろしくお願い致します。 10/03(水)追記致しました。 ---------------- 用途イメージとしては、こちらの画像の赤の数をカウントしたいと考えております。 ↓これならば赤4つ、赤マーキングが付いているので太さは100mmなど ![イメージ説明](https://teratail.storage.googleapis.com/uploads/contributed_images/7531ccf9a706be56509eca8279b119cb.jpeg)
回答: 課題1について シールの色は決まっているので、単純が画像処理でいいと思います。 ### 2値化する。 ``` import cv2 import matplotlib.pyplot as plt from matplotlib.patches import Polygon img = cv2.imread('image1.jpg') # 画像を読み込む。 # 色基準で2値化する。 hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # HSV 色空間に変換 red = cv2.inRange(hsv, np.array(145, 70, 0]), np.array([180, 255, 255])) yellow = cv2.inRange(hsv, np.array([10, 80, 0]), np.array([50, 255, 255])) green = cv2.inRange(hsv, np.array([30, 190, 0]), np.array([90, 255, 255])) blue = cv2.inRange(hsv, np.array([108, 121, 0]), np.array([120, 255, 255])) white = cv2.inRange(hsv, np.array([108, 21, 0]), np.array([255, 70, 255])) # 白だけゴミがあるので、収縮演算 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) white = cv2.erode(white, kernel) bin_imgs = {'red': red, 'yellow': yellow, 'green': green, 'blue': blue, 'white': white} # 2値化結果を可視化する。 #################################################### fig, axes_list = plt.subplots(2, 3, figsize=(10, 6)) axes_list[1, 2].remove() for ax, (label, bin_img) in zip(axes_list.ravel(), bin_imgs.items()): ax.axis('off') ax.set_title(label) ax.imshow(bin_img, cmap=plt.cm.gray) plt.show() ``` ![イメージ説明 ### 輪郭抽出して、輪郭の数を数える。 ``` fig, ax = plt.subplots(figsize=(8, 5)) ax.axis('off') ax.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) # 輪郭検出し、数を求める。 ############################################## for label, bin_img in bin_imgs.items(): _, contours, _ = cv2.findContours( bin_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 輪郭を構成する頂点数で誤検出を除く。 contours = list(filter(lambda cnt: len(cnt) > 30, contours)) count = len(contours) print('color: {}, conunt: {}'.format(label, count)) # 描画する。 for cnt in contours: cnt = np.squeeze(cnt, axis=1) # (N, 1, 2) -> (N, 2) ax.add_patch(Polygon(cnt, fill=None, lw=2., color=label)) plt.show() ``` !イメージ説明 ``` color: green, conunt: 3 color: blue, conunt: 3 color: red, conunt: 4 color: white, conunt: 3 color: yellow, conunt: 3 ``` 課題2について ``` 2.1.の静止画でシールを貼ってある対象の太さを知りたい(画像はシールを貼ってあるものと仮定する) ``` 配管に貼ってあるシールで大きさを決めるということでしょうか? それとも、配管の太さは別途検出するということでしょうか。 後者の場合、撮影する距離が固定とか限定された条件がない限り、画像からは実際のスケールはわからないので無理です。 追記 CNN で物体の検出、分類を行う場合、以下のアプローチがあります。 * クラス分類: 与えた画像からクラスを分類する。 * 物体検出: 与えた画像から物体の位置及びクラスを矩形で検出する。 * Semantic Segmentation: 与えた画像から物体の位置及びクラスをピクセル単位で分類する。 * Instance Segmentation: 与えた画像から物体の位置及びインスタンスをピクセル単位で分類する。(同じクラスの物体が複数写っている場合、それぞれ区別する。) 難易度としては後者ほど難しくなります。 また物体検出や Segmentation は、大量の画像に対して人手でアノテーションを行わないといけないので、データセット作成にかなり労力がかかります。 !イメージ説明 引用元 Semantic segmentation2