fukuyama6421
@fukuyama6421 (好孝 福山)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

初心者です。丸をつくった紐の画像を直線にできますか

Python初心者です。
丸をつくった紐の画像を直線にできますでしょうか?
どのようなソフトをしようしてもかまいません。
宜しくお願い致します

0

はじめまして,具体例などございますでしょうか?

例えば,よく知られている直線or円形検出アルゴリズムとして,Hough Transformというものがあります.これを上手く使って,画像の輪郭検出→2値化→Hough Transform→検出した円の法線方向に切り出し→画像を横並びにする,のような処理をすれば紐が横になりはすると思います.が,紐が真円でなければ直線にはなりません…

他にも画像処理には色んなアルゴリズムがありfukuyamaさんが望まれている変換例を見ないことにはどのようなソフト/アルゴリズムが適切なのか議論致しかねます.

変な話,紐が直線になりゃいいのなら紐のテクスチャを横並びに描画するだけで済んでしまいます.画像処理の意図として,紐の周囲の情報も紐と一緒に歪めたいのではないでしょうか?

いずれにせよ,明確な要件定義をお願いします.手書きの絵でも構いませんので,入出力例を図示してください.

0Like

返信ありがとうございます。
具体例を示します。

これは大腸と骨の画像です。白いのが大腸です。この大腸を抽出し直線に変換して表示はできるのでしょうか。

0Like

そのような2D画像であれば,隠れてしまっている部分は展開時に推定するか不明なままで取り扱うかのどちらかになると思います.それと同時に,どの方向に管が向いているかなどの情報も,線形な画像解析では扱いが難しく,特に下腹部の複雑な構造になっている場所のUnfoldingでは正しく再構成されないなどの問題が発生すると考えます.

上のような画像において大腸のみを抽出するのは既存の手法で簡単にできますが,それを直線上に正しく展開するようなツールは存じ上げません.隠れている部分を正しく推定する独自のモデルが必要になると考えます.

一般に,元々3Dだった情報を画像として2Dに落としてしまっているので,消えてしまった情報量が多く,その画像1枚では復元不可能であると考えるべきです.

というかそもそも,画像を見る限り3Dデータを描画されたようにも見えますが...その描画に使った3Dオブジェクトファイルを弄ってUnfoldingすれば良いのではないでしょうか?

1Like

ごめんなさい,一般の人がわかるように内容が一般化不可能で専門的なのであれば,おそらく私にも答えられないと思います.突き放すようで申し訳ないのですが,ご自身の周囲の人間からあたってみた方が良さそうな内容だと考えられます.

0Like

@PondVillege

申し訳ありません。ここでこのまま教えて頂けないでしょうか。
大腸の絵は忘れてください。ちょうどいい絵かと思ったのですが3Dの様にも見えますのでちょっと例えとして悪かったです。

簡単な
二次元の帯の様なS字の絵をDeeplaerningでまっすぐに伸ばすことはできますか?

すみません。
よろしくお願いいたします。

絵.png

0Like

何らかのアルゴリズムで帯の中心線を求める
帯の中心線がまっすぐになるよう画像を変形する

やったことはないですが可能だと思います。
ただし簡単ではないと思います。

1Like

中心線の求め方について考えてみました。

OpenCVなどで帯の輪郭を抽出
輪郭の曲線を短い直線の集まりで近似
短い直線それぞれについて法線方向にスキャンし、帯の幅を求める
幅の1/2を帯の幅方向の中心とする
それをつなぐことで帯の中心曲線を求める

1Like

@fr0077
返信ありがとうござます。
OpenCVも知らなかったのです。

OpenCVなどで帯の輪郭を抽出
輪郭の曲線を短い直線の集まりで近似
短い直線それぞれについて法線方向にスキャンし、帯の幅を求める
幅の1/2を帯の幅方向の中心とする
それをつなぐことで帯の中心曲線を求める

はPythonで可能でしょうか?

0Like

PythonからOpenCVを操作するライブラリがあったと思うので、それを使えばいけると思います。

0Like

teratailに同様の質問があったので参考に書いてみました.

ライブラリインストール
$ python3 -m pip install opencv-python matplotlib scikit-image

numpyがついでにインストールされるので書いてない

コード
from matplotlib import pyplot as plt
from matplotlib.cm import get_cmap
import numpy as np
import cv2
from itertools import product, permutations
from skimage.morphology import skeletonize

# 画像読み込み
def LoadImage(file: str) -> np.ndarray:
    ret: np.ndarray = cv2.imread(file)
    ret = cv2.cvtColor(ret, cv2.COLOR_BGR2RGB)
    return ret

# 中心線画像を計算する.
# 特にdilateしまくっている箇所はチューニングの余地あり.
def getCenter(img: np.ndarray) -> np.ndarray:
    ret: np.ndarray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    _, ret = cv2.threshold(ret, 0, 255, cv2.THRESH_OTSU)

    for i in range(5):
        kernel = np.ones((5 - i, 5 - i), np.uint8)
        ret = cv2.dilate(ret, kernel, iterations = i + 1)

    kernel = np.eye(2, 2, dtype = np.uint8)
    ret = cv2.dilate(ret, kernel, iterations = 1)
    kernel = 1 - kernel
    ret = cv2.dilate(ret, kernel, iterations = 1)
    return (skeletonize((ret.max() - ret) / 255) * 255).astype(np.uint8)

# 8近傍深さ優先探索
# Skeletonizeで検出した中心線が8近傍で連結しているという仮定のもと動くことに注意
# 幅優先探索の方がいいかも
def dfs(img: np.ndarray) -> np.ndarray:
    w, h = img.shape
    isVisit = img.copy()
    stack, ret = list(), list()
    for y, x in product(range(1, h), range(1, w)):
        if img[y][x] > 127:
            stack.append([x, y]) # DFSのスタート地点を決める
            break
    eight = [e for e in permutations([-1, 0, 1], 2)]
    eight.extend([(-1, -1), (1, 1)])
    while stack:
        x, y = stack.pop()
        isVisit[y][x] = 0
        ret.append([x, y])
        for ex, ey in eight:
            nx = x + ex
            ny = y + ey
            if 0 <= nx < w and 0 <= ny < h and isVisit[ny][nx] > 127:
                stack.append([nx, ny])
    return ret

# 最小二乗法で傾きを計算
def slope(x, y) -> float:
    n: int = len(x)
    return (np.dot(x, y) - y.sum() * x.sum() / n) / ((x ** 2).sum() - x.sum() ** 2 / n)

# 傾きaの直線のうち,(x, y)を通る法線の(傾き, 切片)を計算
def orthogonal(a, x, y) -> tuple[float, float]:
    return -1 / a, x / a + y

# DFSで求めた中心線のリスト(line)から法線(orthogonal)を求める
def orth_vectors(line: np.ndarray) -> np.ndarray:
    ret, n = list(), 5 # n点分の中心線データを使って計算する
    pad: int = n // 2
    x, y = map(np.array, zip(*line))
    for i in range(pad, len(line) - pad, 2):
        xn, yn = x[i - pad: i + pad + n % 2], y[i - pad: i + pad + n % 2]
        ret.append([*orthogonal(slope(xn, yn), xn.sum() / len(xn), yn.sum() / len(yn))])
    return ret

if __name__ == "__main__":
    orig = LoadImage("img.png") # 画像読み込み
    orth = orth_vectors(dfs(getCenter(orig))) # 法線を計算
    x = np.arange(0, orig.shape[1])
    jet = get_cmap('jet')
    for j in range(len(orth)):
        if abs(orth[j][0]) == np.inf or np.isnan(orth[j][0]): # 傾き無限大に関して取り扱わないことにする(本当だったらy軸に並行な法線だから無視してはだめ)
            continue
        orth_line = [(x[i], int(orth[j][0] * x[i] + orth[j][1])) for i in range(len(x))] # 法線の乗る座標を計算
        r, g, b, _ = jet(j / len(orth), bytes = True)
        orig = cv2.polylines(orig, [np.array(orth_line)], False, (int(r), int(g), int(b))) # 法線を描く
    plt.axis("off")
    plt.imshow(orig)
    plt.savefig('out.png', bbox_inches = 'tight', pad_inches = 0)

基本的に法線を求めるというスタンスは一貫して変わりません.
また,法線導出のアルゴリズム次第で,次の画像合成に影響が出るので大事なステップだと考えてください.

  • 画像読み込み(標準でBGRだからRGBにする)
  • RGBからGrayに変換
  • (大津の)2値化
  • 中心線を求める
    • OpenCVのMorphology変換(Dilate): 太線を細くする
    • Scikit-ImageのSkeletonize: 中心線を求める
  • 抽出した中心線が8近傍で連結していると仮定してDFS
    • 左上からピクセルをスキャンして最初に白になったピクセルがスタート地点
  • 中心線のn点から最小二乗法で傾き(slope)を求める
  • 中心線の傾きから,中心線を通る法線(orthogonal)を計算する
  • 画像を横並びにする(未実装)

円形になると最初で言っていたので,Hough Transformがいいのではと申しましたが,先に示していただいた例では役に立ちそうにありませんので,上のようなアルゴリズムに変更してみました.

次の画像に対して法線を描く実験を行ってみました.

img.png

n=5のときの出力結果は

out_5.png

このようになり,n=11にしたら

out_11.png

こうなりました.

nが大きければ大きいほど,中心線の不安定さに影響されなくなります.n=31にしたところ,

out_31.png

このような結果が得られています.がそのnの分だけ元の太線の両端を計算できていません.ここは移動平均の明確な欠点です.また,1次関数を最小二乗法でフィッティングしたので,とる点nが大きければ大きいほど,曲線を認識しなくなります.参考記事では,曲線にフィッティングするためSpline補間をしていました.近似させる関数や補間手法を選定するのも大事ですね.

とりあえず,数理論理的な法線導出は実装しましたので,あとはfukuyamaさんが実装すべきなのは,

  • 中心線と法線の交わる点を中心にして法線に乗っているピクセルを双方向とも取得
  • そのピクセルを縦に変形して横並びにする(一応ここで線が横に並んだ画像ができるが)
  • 横並びにした画像はおそらく鮮明でない
  • 鮮明にするためのアルゴリズムを考える

という画像処理的なところになります.

上に示した画像でも分かると思いますが,曲がり具合の強さによって取ってこれないピクセルも存在します.
線形な手法を使って解決するか,非線形な機械学習的手法を使って解決するかどうかは問題に依存すると思うので任せます.

また,上の画像では帯のような太線でしたが,例示していただいた画像は枠線のようなものだったので,適宜中心線を求めるアルゴリズムを変更する必要があるかもしれません.例えば,枠線が連結であること前提にFloodFillで塗りつぶしてから太線にして上のアルゴリズムを適用するなど.ですかね.

OpenCVを知らないままDeepLearningの手法を求めていたことに驚きなので,とりあえず基礎から学んで欲しいと思い,画像処理100本ノックをお勧めしておきます.

上で使用した2値化Morphology変換,放線方向の画素を縦になおす(回転させる)ために必要なAffine変換などの基礎知識が学べますので目を通しておいてください.

0Like

Your answer might help someone💌