🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

最適化

最適化とはメソッドやデザインの最適な処理方法を選択することです。パフォーマンスの向上を目指す為に行われます。プログラミングにおける最適化は、アルゴリズムのスピードアップや、要求されるリソースを減らすことなどを指します。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

2095閲覧

回転行列最適角度について

amayan

総合スコア9

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

最適化

最適化とはメソッドやデザインの最適な処理方法を選択することです。パフォーマンスの向上を目指す為に行われます。プログラミングにおける最適化は、アルゴリズムのスピードアップや、要求されるリソースを減らすことなどを指します。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

1グッド

0クリップ

投稿2020/11/26 10:04

編集2020/11/26 11:57

0度の歯
-20度の歯
目的:上画像の黄色部分の歯をAffine変換による回転により下画像のように縦向きに変換。
-90度から90度まで回転させ最適な角度を出力。

自分で考えたものとしては、2パターンあり、

  1. 矩形で歯を囲みその中の歯以外のピクセルを合計し最小のものを最適とする。
  2. 列ごとに歯部分のピクセルを数え最多のものを最適とする。

この2つだと実際に見てみると最適でないように感じました。
縦向きになったとどのように人が認知しているのか知りたいです。最適な角度を算出するアイデアをご教授願います。

添付フォルダ(tooth_array.zip内に3つnpyファイル、ipynbファイル)

Python

1#coding:utf-8 2import numpy as np 3import matplotlib.pyplot as plt 4 5# アフィン変換で画像配列の回転 6def rotate_affine(src, theta): 7 # 元画像のサイズを取得 8 h, w = src.shape[0], src.shape[1] 9 10 # 出力画像用の配列生成(要素は全て0) 11 dst = np.zeros((h,w)) 12 13 # degreeかrradianに変換 14 rd = np.radians(theta) 15 16 # アフィン変換 17 for y in range(0, h): 18 for x in range(0, w): 19 xi = int((x - int(w/2))*np.cos(rd) - (y - int(h/2))*np.sin(rd) + int(w/2)) 20 yi = int((x - int(w/2))*np.sin(rd) + (y - int(h/2))*np.cos(rd) + int(h/2)) 21 22 # 変換後の座標が範囲外でなければ出力画像配列に画素値を代入 23 if yi < h - 1 and xi < w - 1 and yi > 0 and xi > 0: 24 dst[y][x] = src[yi][xi] 25 26 return dst 27 28def pattern1(src): 29 sum_cnt = 1000000 30 best_theta = 0 31 for theta in range(-90,90+1): 32 dst = rotate_affine(src,theta) 33 vertical_info = np.count_nonzero(dst ==255, axis=0) 34 side_info = np.count_nonzero(dst ==255, axis=1) 35 min_flag = False 36 for i,num in enumerate(vertical_info): 37 if num != 0 and min_flag == False: 38 min_flag = True 39 min_x = i 40 if num == 0 and min_flag == True: 41 max_x = i-1 42 min_flag = False 43 44 for i,num in enumerate(side_info): 45 if num != 0 and min_flag == False: 46 min_flag = True 47 min_y = i 48 if num == 0 and min_flag == True: 49 max_y = i-1 50 min_flag = False 51 tooth_co_info = [min_y,min_x,max_y,max_x] 52 ex_dst = dst[tooth_co_info[0]:tooth_co_info[2]+1,tooth_co_info[1]:tooth_co_info[3]+1] 53 print("theta,sum:{},{}".format(theta,np.count_nonzero(ex_dst ==0, axis=0).sum())) 54 if np.count_nonzero(ex_dst ==0, axis=0).sum() < sum_cnt: 55 sum_cnt = np.count_nonzero(ex_dst ==0, axis=0).sum() 56 best_theta = theta 57 print("best") 58 print(best_theta,sum_cnt) 59 60 61def pattern2(src): 62 max_cnt = 0 63 best_theta = 0 64 for theta in range(-90,90+1): 65 dst = rotate_affine(src,theta) 66 print("theta,count:{},{}".format(theta,np.count_nonzero(dst ==255, axis=0).max())) 67 if np.count_nonzero(dst ==255, axis=0).max() > max_cnt: 68 max_cnt = np.count_nonzero(dst ==255, axis=0).max() 69 best_theta = theta 70 print("best") 71 print("theta,count:{},{}".format(theta,max_cnt)) 72 73if __name__ == '__main__': 74 mask = np.load('mask.npy') 75 bi_mask = np.where(mask==True,255,0) 76 h, w = bi_mask.shape[0], bi_mask.shape[1] 77 src = np.zeros((int((w**2 + h**2)**(1/2)), int((w**2 + h**2)**(1/2)))) 78 dx,dy = int((w**2 + h**2)**(1/2)/2) - int(w/2), int((w**2 + h**2)**(1/2)/2) - int(h/2) 79 80 for y in range(0, h):#平行移動 81 for x in range(0, w): 82 src[y+dy][x+dx] = bi_mask[y][x] 83 84 theta = 0 85 dst = rotate_affine(src,theta) 86 plt.imshow(dst) 87 plt.show() 88 89 pattern1(src) #切り替え必要 90# pattern2(src)
toast-uz👍を押しています

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

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

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

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

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

toast-uz

2020/11/26 10:23 編集

興味深いけど、1と2をPythonコードで出して結果を表示しないと、プログラミングの話題にならないですね。PythonとかNumPyとかタグをつけるべきでもない。
toast-uz

2020/11/26 12:17 編集

コードの提示ありがとうございます。画像の中心でまわすことには、こだわらなくて、よいでしょうか? (まわすだけでなく平行移動もして、より自然な見た目を追求してよいでしょうか?) ちなみに、mainの方で平行移動されているようですが、これは何を基準に平行移動されていますか?
amayan

2020/11/26 13:08

すべての歯に対して最適な角度を算出することができるなら大丈夫です。 元々歯の周りを囲む矩形のみしかなかったのですが、その場で回転させると歯が途切れて見えてしまいます。回転させるためndarrayのサイズを回転させる分大きくし、元々のndarrayの中心と拡張したndarrayの中心を一致させています。
guest

回答1

0

ベストアンサー

scipyをフル活用することで全体的にすっきりと作ってみました。

  • アイデアとしては、まずは歯の形を平面に置いた重心に中心を揃え、その後、歯の形を立ててバランスが取れる回転位置を求める、というものです。
  • バランスが取れるかどうかは、歯全体を重さ密度一定の剛体としてみた場合に、垂直に立てた状態で、中心軸に対する慣性モーメントが最小になる、という定義をしました。
  • 最初の重心、平行移動、回転は、全てscipy.ndimageを使い、モーメント演算だけは見当たらないので自作、そしてモーメント最小を求めるのにscipy.optimizeを使いました。
  • なお、歯の画像は質問者様の最初の画像をキャプチャして作成しました(ですのでピクセルサイズは元とは異なります)。回転によって歯の画像が枠外にはみ出ない前提でいます。

以下がコードと実行結果の図です。-23.36度ほどの回転が最適である、と出ました。質問者様の結果と比較して、ほんの少しだけ右回転しており、とてもバランスが良いように見えます。

※ 当初は、左右で逆向きの回転モーメントを使ってましたが、慣性モーメントを使った方が安定した形になるとわかりました。回転モートメントだと横に伸ばした形でも左右対称であれば安定するのに対して、慣性モーメントだと横をなるべく小さくするような形が有利になります。フィギュアスケートの選手が腕をたたんで高速回転することと、原理は同じです。

Python

1import numpy as np 2from scipy import ndimage, optimize 3import matplotlib.pyplot as plt 4import cv2 5 6# 画像読み込みと2値化 7img = cv2.imread('tooth.png', 0) 8_, img = cv2.threshold(img, 100, 1, cv2.THRESH_BINARY) 9 10# 平行移動して重心を中心にする 11center = ndimage.center_of_mass(img) 12img = ndimage.shift(img, np.array(img.shape)/2-np.array(center)) 13 14# 図形を立てた時の、中心軸に対する慣性モーメント 15# mass:縦軸ピクセル数、radius:中心軸からの左右のピクセル距離 16# 慣性モーメント = Σ mass * radius^2 17def inertia(img): 18 mass = img.sum(axis=0) 19 radius = np.abs(np.arange(-len(mass)//2, len(mass)//2+1)) 20 if len(mass) != len(radius): 21 radius = radius[radius != 0] - 0.5 # 中心位置の補正 22 return (mass * radius * radius).sum() 23 24# 回転させて慣性モーメントを測定する=最小化関数 25def rotated_inertia(degree, img): 26 return inertia(ndimage.rotate(img, degree, reshape=False)) 27 28# 最小化関数の最小値を求めることで、-90〜90度の回転時の最小慣性モーメントを得る 29res = optimize.minimize_scalar( 30 rotated_inertia, bounds=[-90,90], args=(img), method='Bounded') 31 32# 最小値の時の回転角度を求める 33print(res.x) 34 35# 最小値の時の回転を実際に行う 36img = ndimage.rotate(img, res.x, reshape=False) 37 38plt.imshow(img) 39plt.show()

結果

回転結果

投稿2020/11/26 14:20

編集2020/11/27 23:34
toast-uz

総合スコア3266

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

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

amayan

2020/11/28 10:18

慣性モーメントかなりいいですね。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問