質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Q&A

2回答

3285閲覧

曲線に対して垂直な線を書きたいです。

退会済みユーザー

退会済みユーザー

総合スコア0

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

0グッド

0クリップ

投稿2021/01/15 08:55

編集2021/01/18 09:01

曲線に対して垂直な線を書きたいです。
プログラミング初心者です。
何かアイデアがあると教えていただきたいです。
オレンジの曲線に青ように直線を書きたいです。場所はどこでもいいですが、線が3箇所程度欲しいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2021/01/15 09:05 編集

曲線とは? どのようなデータですか? 数式ですか? それとも,「見た目に曲線っぽい形をしている絵」とかですか? その他? 垂直な線を書く話と,バウンディングボックスとの間にはどのような関係性があるのですか?
退会済みユーザー

退会済みユーザー

2021/01/15 09:34

すみません、バウンディングボックスは関係ないでrすね、、 情報不足でした。
fana

2021/01/15 09:37

そのオレンジの線というのはどうやって引かれたのですか? 「その線を描画するのに必要となる何らかのデータが手元にあって,それを用いてオレンジ色で描画した」のでしょうか? それとも,「白背景にオレンジの線がある画像」が出発点なのでしょうか?
tiitoi

2021/01/15 09:43

「曲線に対して垂直な線を引く」より前までの処理のコードがあるなら、そのコードを質問に提示してください。
退会済みユーザー

退会済みユーザー

2021/01/15 09:46

オレンジの線は元々ある画像です。ここに青のような線を描きたいんですけど、、 青の線は分かりやすいかなと思いパワポで作りました。
tiitoi

2021/01/15 09:57 編集

曲線の座標が得られてるなら、それに対して垂直な線を引くのは簡単ですが、オレンジの線の座標自体が不明で画像処理でオレンジの線の座標を求めるとなると、背景が複雑だったりすると、オレンジの線の抽出が困難なので、できるかどうかは実際の画像次第な気がします
退会済みユーザー

退会済みユーザー

2021/01/15 10:20

座標は端と終わりだけは分かってます! もし背景が黒一色だけだったらできますかね、、
退会済みユーザー

退会済みユーザー

2021/01/15 13:54

import numpy as np import cv2 from scipy import optimize from skimage.morphology import skeletonize # Read image img = cv2.imread("pic.png") # Make grayscale image img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # Make binarized image ret,img_th = cv2.threshold(img_gray,200,255,cv2.THRESH_BINARY) img_th = 255-img_th # Detect skeleton skeleton = (skeletonize(img_th/255)*255).astype(np.uint8) cv2.imshow("skeleton",skeleton) cv2.waitKey(0) とりあえずこれで線の中心と思しき場所までは特定できます。参考まで。。
退会済みユーザー

退会済みユーザー

2021/01/16 14:47

うまくいきそうです。24h以内に回答を投稿します。
fana

2021/01/18 09:46

何故画像を抹消したのか? これだと,質問や既存の回答内で用いられている「オレンジ」だの「青」だのいう話の意味が分からなくなってしまう.
guest

回答2

0

イメージ説明

処理の要点だけ書きます。

  1. 何とかして曲線部分だけ切り出しましょう。背景にいろいろな色があって分離困難な場合、HSV空間にもっていって、オレンジ色だけ切り出すと良いかもしれません。たくさん曲線があるようであれば、OpenCVのcontour毎に分離させる手法を取ってください。
  2. 式で近似させるためになんとかして線ののっかる座標を拾いましょう。座標の割り出しにはskeletonが便利です。「オレは近似式なんぞ使わず、画素の並び具合から傾きを計算する!」というマッチョであれば以下無視してください。
  3. 何らかの形でx,yから傾きを得るために、近似式を出す方法としてsplineを使いました。式が定義できるのであればscipyで最小二乗法をしたらいいと思います(私にはムリでした)。遺伝的アルゴリズムをコメントに書きましたが、計算が遅いのでお勧めできません。splineが現実的かと思います。
  4. 上記3において、splineの場合あるxに対して1つのyしか存在が許されません。あるx上に複数の解yが存在するのであれば、何らかの方法で曲線を分離してxに対して1つのyしか存在しないようにしてください。私の場合は重複チェックをかませてx,yの値を固有の値にしましました。これだと「初めに出てきた対を機械的に生かす」だけですので少なくとも数pxくらいズレてしまいます。そんなザコい処理は許されないというのであれば、skeletonのskeletonを探したり、gaussianでぼかして濃いところだけ探す、cv2.erodeなりでもっとあたりを探ったら良いと思います。splineで曲線が相当最適化されると思いますので、上記の中心を割り出す処理をした上で、x,yの対の90%くらいを切り捨ててしまってから式を割り出すsplineをしてもいいかもしれません。
  5. 傾きから直交する線を描くのはもっとスマートな方法があると思います。例えばtiitoiさんの回答みたいなもののほうがカッコいいですよね?

Python3

1import numpy as np 2import cv2 3import sys 4from scipy import interpolate 5from skimage.morphology import skeletonize 6 7def images_to_pos(img_thresh): 8 # Detect skeleton 9 skeleton = (skeletonize(img_th/255)*255).astype(np.uint8) 10 # cv2.imshow("skeleton",skeleton) 11 12 pos = np.asarray(list(zip(*np.where(skeleton > 100)))) 13 pos_x,pos_y = pos[:,1], pos[:,0] 14 15 # Remove duplicates 16 x_used = list() 17 pos_x_updated = list() 18 pos_y_updated = list() 19 for x,y in zip(pos_x,pos_y): 20 if x in x_used: 21 pass 22 else: 23 x_used.append(x) 24 pos_x_updated.append(x) 25 pos_y_updated.append(y) 26 27 return pos_x_updated,pos_y_updated 28 29def calc_slope(spline_func,pos_x,target_x,shift_value): 30 # Calc x (low, high) 31 target_x_l = target_x - shift_value 32 target_x_h = target_x + shift_value 33 34 # Limit the range 35 target_x_l = np.clip(target_x_l, np.min(pos_x),np.max(pos_x)) 36 target_x_h = np.clip(target_x_h, np.min(pos_x),np.max(pos_x)) 37 38 # Calc y (low, high) 39 target_y_l = spline_func(target_x_l) 40 target_y_h = spline_func(target_x_h) 41 42 slope= (target_y_h - target_y_l) / (target_x_h - target_x_l) 43 return slope 44 45def calc_orthogonal(slope,intersection): 46 target_slope = (-1) / slope 47 target_intercept = intersection[1] / (target_slope *intersection[0]) 48 49 return target_slope, target_intercept 50 51def draw_line(img,spline_func,pos_x,target_x): 52 # x = --> y = 53 target_x = target_x 54 target_y = spline_func(target_x) 55 center = (int(target_x),int(target_y)) 56 57 shift_value_horizontal = 30 58 shift_value_vertical = 999 59 ####################################### 60 # Calc slope on the point 61 target_slope = calc_slope(spline_func,pos_x,target_x,shift_value_horizontal) 62 63 # Left 64 img = cv2.line(img,center,(int(target_x - shift_value_horizontal), int(target_y - (target_slope * shift_value_horizontal))),(128,255,192),3) 65 66 # Right 67 img = cv2.line(img,center,(int(target_x + shift_value_horizontal), int(target_y + (target_slope * shift_value_horizontal))),(128,192,255),3) 68 69 ####################################### 70 # Calc orthogonal params 71 orth_slope,orth_intercept = calc_orthogonal(target_slope,(target_x,target_y)) 72 73 # Top 74 img = cv2.line(img,center,(int(target_x + shift_value_vertical), int(target_y + (orth_slope * shift_value_vertical) + orth_intercept)),(255,0,0),3) 75 76 # Bottom 77 img = cv2.line(img,center,(int(target_x - shift_value_vertical), int(target_y - (orth_slope * shift_value_vertical) + orth_intercept)),(255,192,192),3) 78 79 ####################################### 80 # Center 81 img = cv2.circle(img,center, 3, (32,32,32), -1) 82 return img 83 84if __name__ == "__main__": 85 # Read image 86 img = cv2.imread("pic.png") 87 88 # Make grayscale image 89 img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 90 91 # Make binarized image 92 ret,img_th = cv2.threshold(img_gray,200,255,cv2.THRESH_BINARY) 93 img_th = 255-img_th 94 95 # binary image to positional data 96 pos_x, pos_y = images_to_pos(img_th) 97 98 # Calc spline 99 spline_func = interpolate.interp1d(pos_x, pos_y,kind="cubic") 100 101 # # Debug 102 # try: 103 # for x in range(np.min(pos_x),np.max(pos_x),1): 104 # y = spline_func(x) 105 # img = cv2.circle(img,(int(x),int(y)), 15, (128,128,255), 1) 106 # 107 # except Exception as err: 108 # print("x",x) 109 # print("ERR:",err) 110 # sys.exit() 111 112 img = draw_line(img,spline_func,pos_x,100) 113 img = draw_line(img,spline_func,pos_x,150) 114 img = draw_line(img,spline_func,pos_x,200) 115 img = draw_line(img,spline_func,pos_x,300) 116 img = draw_line(img,spline_func,pos_x,500) 117 118 cv2.imshow("img",img) 119 cv2.imwrite("img.png",img) 120 cv2.waitKey(0)

投稿2021/01/17 03:24

編集2021/01/17 03:27
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

●仮定

対象の曲線が描かれた画像に対して,何らかの処理を行うことで,
「曲線を構成する画素 と それ以外」という2値の画像を得ることができるのだとする.


●方策1

曲線の形状の自由度がある程度既知である場合,
曲線を構成する画素群に対して適当な自由度の曲線の式をフィッティングするなどする.
これができるならば,その曲線上の任意の位置での法線は求められるだろう(:求められるような曲線の式を使えばいい)から,目的の直線が描画できるハズ.

●方策1’

曲線上のある位置(青線を引きたい位置)に関して,その位置を中心とする小領域を考え,その小領域内の曲線を構成する画素群に対して直線を当てはめる.
で,その直線と直交する線を描く.


●方策2

前記2値画像に関して,Sobelフィルタを適用することで,曲線の輪郭位置における輝度勾配方向(≒曲線に垂直な方向)を得て,それを用いて直線を描画する.

投稿2021/01/15 10:26

編集2021/01/15 10:31
fana

総合スコア11675

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/01/18 02:18

ありがとうございます! ぜひ参考にさせていただきます!
fana

2021/01/18 02:25

提示された絵のように,線にちょっとした太さがあるようであれば, 私ならまずは「方策2」で済ませることができないかを試します(簡単な処理でやれるならその方が楽なので).
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問