森井の卒論日誌
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
|
ログイン
]
開始行:
&aname(Top);
#contents
*卒論 [#v045a40a]
**4月 [#j417c400]
***4月第4週(4/23〜4/30) 手書き数字の判別 [#j417c423]
卒論テーマが決まった。
耐候性鋼橋の耐候性鋼材の錆の現地調査(今年度から)(日本鉄...
-jpg, ppmファイルから1点ごとのRGB値を確認する方法
--1,写真フィルを選択して"gimpで開く"をクリックする。
--2,gimpが開けたら上にあるバーから "ウィンドウ" → "ドッキ...
-MATE端末のコマンド
--1, locate (ファイル名等) → (ファイル名)がどこにあるのか...
--2, gfortran -o (ファイル名) (ファイル名.拡張子) → ある...
--3, history → MATE端末内で過去にどんなコマンドを用いたか...
--4, hg (ファイル名等) → MATE端末内でどの部分に(ファイル...
-明日までにやっておくべきこと
--佐藤さんの卒論日誌をはじめ耐候性鋼材にまつわるウィキを...
-これからしばらくはpythonになれることが必要 Python上で佐...
-【質問内容】
--最初に取り組むべきステップは何か?
機械学習を使うにはどんなデータ整理や前処理が必要かがわか...
--過去の先輩方の研究でどの部分を参考にするのがよいか?
佐藤卒論日誌やRGBメモの中で、現在の作業と関連が深い部分が...
--MATE端末からpythonを開くには → python と入力すれば入る...
--MATE端末から1つ上のディレクトリに上がりたい(戻りたい)場...
-python上で ホーム / hikitugi 内に保存された.pyファイルを...
--MATE端末にもどり( python の状態から脱出する) "python フ...
--hikitugi内には RGBrironti.py , edge.py , henkan.py , ma...
-RGBrironti.py について (コマンド)
--1, MATE端末を開き py ファイルのある hikitugi に移動する...
--2, py ファイルを呼び出す (python3 RGByomitori.py) [3は ...
--3, フォルダーのパス入力が求められるので錆の写真があるフ...
--4, 保存するファイル名を入力する → Enterキー (一例: RGBs...
--5, その後ウィンドウのどこかしらに錆の画像が表示されるの...
画像: ファイル名(.ppm) | クリック位置: (x座標, y座標), R...
と表示される。またフォルダーパスの入力をしたファイル内に4...
--6, 文書ファイルを開くと写真のようにMATE端末に表示されて...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--7, 写真の左下に写っている錆の写真は矢印キー(←, →)でフォ...
--補足:保存するファイル名を前に作成したファイル名と同じに...
-edge.py について(コマンド)
--1, MATE端末を開き py ファイルのある hikitugi に移動する...
--2, py ファイルを呼び出す (python3 edge.py) [3は ver の...
--3, 入力ディレクトリパスの入力が求められる。なお edge.py...
--4, 出力ディレクトリパスの入力が求められる。ここでは3で...
---/edge-syuturyoku0424はエッジ処理したものを入れておくフ...
--5, エッジ処理の閾値1, 2を入力してくださいと表示される
---エッジ処理の閾値1は "Cannyエッジ検出の「低い方のしきい...
---エッジ処理の閾値2は "Cannyエッジ検出の「高い方のしきい...
--6, 閾値1, 2 を入力して Enterキー を押してしばらくすると...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/202...
-明日までにやっておくべきこと
--残りのPythonファイルについて使い方を学ぶ
-svm.py について (コマンド)
--0, はじめに
--- svm = "s"upport-"v"ector-"m"achine のこと
--1, py ファイルを呼び出す前に svm.py ファイルを開く
--2, ファイルを開いたら13行目に書いてある data_dir = 〜 ...
---デフォルトでは data_dir = "edge/250_300_edge" となって...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
この写真の場合 "mk1_pgm"等が入っている1番左にある "250_3...
-henkan.py について (コマンド)
--1, MATE端末を開き py ファイルのある hikitugi に移動する...
--2, py ファイルを呼び出す (python3 henkan.py) [3は ver ...
--3, 変換行列ファイルのパスを入力してください と表示され...
--- 変換行列ファイルは3 ☓ 3行列の .txtファイルで表されて...
---henkanm内にある 1_henkanm ~ 4_henkanmの .txt の名前と ...
--4, 補正したいPPM画像のパスを入力してください と表示され...
---基本的に/home/kouzou/sato24/gr/data内にある ○_ppm や h...
--5, 補正後の画像を保存するパスを入力してください と表示...
---変換行列ファイルはかなり細かい値まで書かれているため...
-matrix.py について (コマンド)
--0, はじめに
このpythonファイルは henkan.py で必要になる変換行列ファ...
--1, MATE端末を開き py ファイルのある hikitugi に移動する...
--2, py ファイルを呼び出す (python3 matrix.py) → Enterキー
--3, 結果を書き込むファイルを入力してください と表示され...
--4, シアン・イエロー・マゼンタの各RGB値を入力してくださ...
---各RGB値を入力するにあたりなにか根拠となるものがあるは...
-来週中にやること
--サポートベクターマシーン(優先)と畳込みニューラルネット...
--このページをまとめる(5つのpythonファイルをどういう順序...
--佐藤さんへの質問をまとめておく
-5つあるpythonファイルの使い方についてとそれぞれの役割
-錆の写真を撮影してそれをサポートベクターマシンを用いるま...
-SVMについて
-佐藤さんが研究していたファイル達(sato2024)は各フォルダー...
-佐藤さんから錆の写真からサポートベクターマシンを用いて用...
--耐候性鋼材のページに上記の事について詳しくまとめてある。
--機械学習のページは何か発見があったら随時更新していく必...
-来週までにやること
--畳み込みニューラルネットワークについて調べる
--佐藤さんのファイル内にある手書き数字を使って機械学習
--それに加え動物の顔(一例)が判別できるかについても機械学...
-畳込みニューラルネットワークを用いて手書き数字の機械学習
--手書き文字の機械学習は mnist という手書き数字を認識する...
--1, MATE端末で "pip install tensorflow" と入力しEnterキ...
--2, エラーが出なかったらmnistの画像データをCNNに読み込ま...
Pythonコード
--3, しばらくすると正答率が表示される。だいたい99%前後の...
-ある手書き文字を読み込ませて判別させる
--1, mnist等から28×28ピクセルで書いた数字のpngやjpgファイ...
Pythonコード
--2, Enterキーを押せば判別してもらえる。
-複数枚の手書き文字を一度に判別させたい場合
--1, 複数枚ある手書き数字をどこか1つのフォルダーにまとめ...
--2, Enterキーを押せば一度に複数枚判別してもらえる。
Pythonコード
---11行目で予め画像を保存していたフォルダー名を入力する
---12行目で画像ファイル形式を入力する
---7と書いた画像(右の写真)を読み込ませたところ0と出力され...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-畳込みニューラルネットワーク(CNN)について
--特に画像認識や画像分類で優れた性能を発揮するディープニ...
-層の用語について
--畳み込み層 … 画像におけるエッジや色の変化といった局所的...
--プーリング層 … 画像の特徴と位置を紐付けてしまうとCNNの...
--全結合層 … 畳み込み層・プーリング層で抽出された特徴から...
,手順 ,実行内容
,1,画像を入力する(ピクセルで)
,2,畳み込み層で画像の特徴を検出する
,3,プーリング層で重要な情報を抽出する
,4,2と3を繰り返す
,5,全結合層で画像の特徴をもとに分類を行う
,6,出力する
-CNNとSVMの違いについて
, 比較項目 , SVM , CNN
, モデル , すでに用意された特徴(数値)をもとにクラス分け...
, 入力 , 数値データ (特徴量ベクトル) → 画像を数値に変換す...
, 特徴の抽出方法 , 手動(人があらかじめ決めた特徴) ,自動...
, モデル構造 , 数学的な式(境界面) ,多層のニューラルネッ...
, 適用例 , 小規模なデータで分類(例:文字分類、小さな画像...
, 学習の難易度 , 比較的簡単(実装も軽い) ,やや難しい(GP...
, 処理速度 , 小規模なら早い ,訓練に時間がかかる(推論は速...
--SVM → 与えられた特徴を元に最適な境界線で分類する方法。...
--CNN → 画像の中から重要な特徴を自分で見つけて分類してく...
**5月 [#j417c500]
***5月第1週(5/1〜5/7) [#j417c502]
-手書き数字の機械学習(CNN, 判別)について
--mnistのテストデータに入っている数字を判別させたところ正...
--mnist以外の数字画像はまだ正確に判別できているとはいえず...
--また mnist.py または mnist-hosei.py を何度もMATE端末上...
-mnistの画像を少し補正
--今までは画像の中心に数字を書いて機械学習を行ってきたが...
,学習方法 ,テストデータ正答率(%) ,判別精度(%)
,そのままの状態で画像を学習 ,99.14 ,44 (4/9)
,画像を回転・移動・拡大・変形させて学習 ,98.49 ,67 (6/9)
-エポック数を増やして学習させてみた
---そのままの状態で画像を学習させた時
,エポック数 ,テストデータ正答率(%) ,判別精度(%)
,10 ,99.20 ,67 (6/9)
,12 ,99.13 ,56 (5/9)
,13 ,99.26 ,89 (8/9)
,14 ,99.19 ,89 (8/9)
,15 ,99.16 ,89 (8/9)
,16 ,99.08 ,89 (8/9)
--エポック数が13を超えると判別精度が向上したがmnistにはな...
--4の画像を識別させたときにどれくらいの確率でどの数字であ...
---画像を回転・移動・拡大・変形させて学習させた時
,エポック数 ,テストデータ正答率(%) ,判別精度(%)
,10 ,95.47 ,56 (5/9)
,12 ,87.01 ,56 (5/9)
,13 ,90.55 ,56 (5/9)
,14 ,96.15 ,56 (5/9)
,15 ,86.66 ,56 (5/9)
,16 ,74.12 ,56 (5/9)
--エポック数の大小に関わらず判別精度は変化せず、特に5, 7,...
-来週にやること
--GW中に手書き数字の機械学習は終わらせたい
---自身で手書き数字を書いてみてそれがどのくらいの精度であ...
--動物の顔(仮)が機械学習を通じて判別できるようになるスク...
---とりあえず多くの動物の顔の写真が載っているサイトを探す。
-MNISTについて
--MNISTを用いた手書き文字の画像判別はどのサイト・ホームペ...
--限りなく100%の正解率に持っていく手法はあるだろうが正解...
-手書き数字の機械学習 (続き)
--とりあえず自身で作成した手書き数字を機械学習に判別させ...
,数字 ,モデル数 ,正答率
,0 ,3 ,3/3
,1 ,5 ,5/5
,2 ,6 ,6/6
,3 ,6 ,6/6
,4 ,2 ,2/2
,5 ,10 ,9/10
,6 ,2 ,1/2
,7 ,3 ,2/3
,8 ,2 ,1/2
,9 ,13 ,9/13
本来は各数字のモデル数が揃っていることが望ましいが、こち...
-来週までにやること
--手書き数字の画像に点や線、枠を入れても正確に判別できる...
--自動的に画像内のどこか適当なところに点や図形を自動的に...
--動物の顔を使った機械学習(できたら)
--自分の研究についてスライドを3枚程度作成する
-明日にやること
--数字に点や線, 図形を加えた結果の表を作成
--使用した画像を載せる
--色を白ではなく他の色にするとどうなるか (エラーを吐き出...
***5月第2週(5/8〜5/14) [#j417c508]
-画像に数字に点や線, 図形を加えてみた。これを判別してみる
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
Pythonコード (Gazou-Kakou.py)
-- 0 ~ 9 の画像数をそれぞれ9枚に統一させた。表の真ん中の...
--表の右側の列は数字に点や線, 図形のすべてを画像に加え数...
,数字 ,正答率 ,正答率
,0 ,9/9 ,9/9
,1 ,8/9 ,1/9
,2 ,9/9 ,6/9
,3 ,9/9 ,6/9
,4 ,9/9 ,0/9
,5 ,9/9 ,5/9
,6 ,5/9 ,2/9
,7 ,8/9 ,4/9
,8 ,6/9 ,6/9
,9 ,6/9 ,0/9
---数字は太くはっきり書いてあること且つ線や図形と被ってい...
---ただ要素を加えすぎたせいか無加工と比べてかなり判別精度...
-改良版
--画像を10枚に統一させてフォントを太くした。以下の表はそ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
,数字 ,正答率
,0 ,10/10
,1 ,10/10
,2 ,10/10
,3 ,10/10
,4 ,10/10
,5 ,10/10
,6 ,10/10
,7 ,10/10
,8 ,10/10
,9 ,10/10
--数字を中心に持ってきたからなのかまたはほとんど形が変わ...
--次に点, 線, 図形のうち1つの要素だけを画像に加え数字ごと...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
,数字 ,点 正答率 ,線 正答率 ,図形 正答率(リスタート前) ,...
,0 ,10/10 ,9/10 ,8/10 ,10/10
,1 ,10/10 ,4/10 ,5/10 ,6/10
,2 ,10/10 ,9/10 ,8/10 ,10/10
,3 ,10/10 ,7/10 ,9/10 ,10/10
,4 ,10/10 ,9/10 ,8/10 ,8/10
,5 ,10/10 ,9/10 ,10/10 ,10/10
,6 ,8/10 ,3/10 ,1/10 ,3/10
,7 ,10/10 ,7/10 ,7/10 ,8/10
,8 ,10/10 ,10/10 ,10/10 ,10/10
,9 ,10/10 ,7/10 ,7/10 ,6/10
--点は1画像あたり50個加えた(Gazou-Kakou-Ten_only.py を...
Pythonコード (Gazou-Kakou-Ten_only.py)
--線は1画像あたり5本加えた(Gazou-Kakou-Sen_only.py を使...
Pythonコード (Gazou-Kakou-Sen_only.py)
--図形は1画像あたり三角形から十角形までの多角形を1〜3...
Pythonコード (Gazou-Kakou-Zukei_only.py)
-判別させたい手書き文字を作成するには
--① GIMPで直接書きたい場合 (推奨)
---1, GIMPを開く
---2, 上のツールバーから ファイル → 新しい画像 の順にクリ...
---3, 左側に下のようなアイコンが現れたと思うがこれは背景...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
---4, アイコンの下にはサイズ, 縦横比, 角度, .... と大きさ...
---5, 文字を書き直したいときはアイコンの上にある消しゴム...
---6, 書き終えたら保存し、その際にどこに保存するかを指定...
---7, 保存はxcfファイルでされるため一度pngに変換する必要...
---※スクリプトでxcfファイルを直接判別してもらえるような文...
--② AIに書かせる場合 (ChatGPT)
--1, ChatGPTに"黒背景に白の線で 0 ~ 9 の数字を各5枚、合計...
--2, 画像が作成されるまで1, 2時間かかる場合があるためその...
--3, 生成された画像は複数の文字(数字)が1枚の画像となって...
--4, 分割できたら各画像を保存 → png に変換 → 28×28ピクセ...
-画像に点, 線, 図形をそれぞれ加えたときの正答率について
--点を加えたときに関してはかなり精度の高い判別ができたの...
--線を加えると判別精度は点の時と比較してやや劣るが、その...
--最後に図形を加えた場合数字によって精度のばらつきがある...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-次回やること
--MNISTで学習させてからある数字の画像を何と判別したかをフ...
→大まかな内容は完了しこれから肉付け(5/11)
--動物の顔を使った機械学習(できたら)
-とりあえずmnistで学習させた後 "判別したい画像を1つのフォ...
-全自動スクリプトにmnistで学習させる作業を入れなかった理...
Pythonスクリプト (Hanbetsu-Full_auto.py)
-上記のスクリプトを実行すると "既存の画像を加工したフォル...
--テキストファイルの中身
,0_7.png 予測: 0 正解: 0 一致: 〇
,1_5.png 予測: 8 正解: 1 一致: ×
,3_5.png 予測: 3 正解: 3 一致: 〇
,8_2.png 予測: 8 正解: 8 一致: 〇
,…
,--- 数字ごとの正答率 ---
,0: 正解 ○ / □ 枚 → 正答率 △ %
,1: 正解 △ / ○ 枚 → 正答率 □ %
, …
--以下の表は全自動スクリプトを3回用いて判別させた数字ごと...
,数字 ,正答率 ,正解率 ,-正答率- ,-正解率- ,--正答率-...
,0 ,10/10 ,100.00% ,10/10 ,100.00% ,10/1...
,1 ,0/10 ,0.00% ,8/10 ,80.00% ...
,2 ,10/10 ,100.00% ,5/10 ,50.00% ,...
,3 ,10/10 ,100.00% ,10/10 ,100.00% ,10...
,4 ,6/10 ,60.00% ,4/10 ,40.00% ...
,5 ,10/10 ,100.00% ,10/10 ,100.00% ,10...
,6 ,0/10 ,0.00% ,0/10 ,0.00% ...
,7 ,10/10 ,100.00% ,1/10 ,10.00% ...
,8 ,10/10 ,100.00% ,10/10 ,100.00% ,10...
,9 ,10/10 ,100.00% ,10/10 ,100.00% ,10...
Pythonスクリプト (Mnist-Hanbetsu-Full_auto.py)
-上記のスクリプトで出来ること
--mnistの数字画像を用いて学習し、その結果をh5ファイルに保...
--判別させたい数字画像に図形を加える等の加工をする。
--加工した画像を見られるように別のフォルダーを作成して保存
--加工した図形を判別させて合っていたか、また数字別の正解...
--→判別させたい画像をフォルダーに入れておき Mnist-Hanbets...
--以下の表は"シン"・全自動スクリプトを3回用いて判別させた...
,数字 ,正答率 ,正解率 ,-正答率- ,-正解率- ,--正答率-...
,0 ,10/10 ,100.00% ,10/10 ,100.00% ,10/10 ,...
,1 ,0/10 ,0.00% ,0/10 ,0.00% ...
,2 ,8/10 ,80.00% ,9/10 ,90.00% ,1...
,3 ,10/10 ,100.00% ,7/10 ,70.00% ,8/10...
,4 ,8/10 ,80.00% ,9/10 ,90.00% ,1...
,5 ,10/10 ,100.00% ,10/10 ,100.00% ,10/10 ...
,6 ,1/10 ,10.00% ,4/10 ,40.00% ,0...
,7 ,2/10 ,20.00% ,7/10 ,70.00% ,...
,8 ,10/10 ,100.00% ,10/10 ,100.00% ,1/10 ...
,9 ,10/10 ,100.00% ,9/10 ,90.00% ,10/...
--3回とも正答率が100%である数字もあれば判別を1回行うごと...
-2種類の全自動スクリプトで判別に使用した画像(画像加工前)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
Pythonスクリプト (Mnist-Hanbetsu-Full_auto2.py)
-上記のスクリプトの違い
--mnistの中にある画像を加工して学習させた (加工方法と内...
--以下の表は Mnist-Hanbetsu-Full_auto2.py を用いて判別さ...
,数字 ,正答率 ,正解率 ,-正答率- ,-正解率- ,--正答率-...
,0 ,15/15 ,100.00% ,15/15 ,100.00% ,15/15 ,100.00%
,1 ,15/16 ,93.75% ,15/16 ,93.75% ,16/16 ,100.00%
,2 ,15/16 ,93.75% ,16/16 ,100.00% ,15/16 ,93.75%
,3 ,16/16 ,100.00% ,16/16 ,100.00% ,16/16 ,100.00%
,4 ,15/16 ,93.75% ,15/16 ,93.75% ,15/16 ,93.75%
,5 ,15/16 ,93.75% ,15/16 ,93.75% ,15/16 ,93.75%
,6 ,12/16 ,75.00% ,6/16 ,37.50% ,8/16 ,50.00%
,7 ,15/16 ,93.75% ,14/16 ,87.50% ,16/16 ,100.00%
,8 ,16/16 ,100.00% ,15/16 ,93.75% ,15/16 ,93.75%
,9 ,15/16 ,93.75% ,16/16 ,100.00% ,15/16 ,93.75%
--今までと比べて作業効率も識別の精度も上がっていると思う...
--どのスクリプトを実行しても"6"も識別が上手くいかない。明...
-明日やること
--日誌に画像を貼り付ける
--mnistから抽出したデータで判別させる
--明後日に向けて自身の研究内容を英語で書いておく
-mnistから抽出したデータで判別
--判別する画像の対象を以下の画像に変更した。(今までよりも...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--以下の表は全自動スクリプトを用いて判別させた数字ごとの...
,数字 ,正答率 ,正解率 ,正答率 ,正解率 ,正答率 ,正解...
,0 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,1 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,2 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,3 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,4 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,5 ,19/20 ,95.00% ,20/20 ,100.00% ,18/20 ,90.00% ...
,6 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,7 ,17/20 ,85.00% ,20/20 ,100.00% ,19/20 ,95.00% ...
,8 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,9 ,19/20 ,95.00% ,18/20 ,90.00% ,19/20 ,95.00% ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--全体的に高い判別精度が得られ0〜4と 6, 8はすべての回で10...
--精度をさらに上げるためには
---ノイズの位置をランダムではなく重要領域を避けるように設...
---Dropoutの実装 (過学習の防止とアンサンブル効果を得るため)
---混同行列を作成しどの数字が判別に弱いのかを確認する
-アンサンブル効果とは
個々の学習モデルにはそれぞれ異なる「誤り」や「予測の癖」...
Pythonスクリプト (Hanbetsu-Full_auto3.py)
--画像中央付近への点の描画を減らした。
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--混同行列ををpng形式で表示・保存できるように設定した。
--以下の表はそのスクリプトを用いて判別させた数字ごとの正...
,数字 ,正答率 ,正解率 ,正答率 ,正解率 ,正答率 ,正解...
,0 ,20/20 ,100.00% ,20/20 ,100.00% ,20...
,1 ,20/20 ,100.00% ,19/20 ,95.00% ,1...
,2 ,20/20 ,100.00% ,20/20 ,100.00% ,19...
,3 ,20/20 ,100.00% ,20/20 ,100.00% ,20...
,4 ,20/20 ,100.00% ,20/20 ,100.00% ,20...
,5 ,20/20 ,100.00% ,19/20 ,95.00% ,1...
,6 ,20/20 ,100.00% ,20/20 ,100.00% ,20...
,7 ,19/20 ,95.00% ,19/20 ,95.00% ...
,8 ,20/20 ,100.00% ,20/20 ,100.00% ,20...
,9 ,20/20 ,100.00% ,19/20 ,95.00% ,...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-上記の行列の見方
--縦軸は True Label (画像に書いてある実際の数字 = 答え), ...
--濃い青で塗りつぶされていない部分は機械が実際の数字とは...
--1番右の Recall は数字別の判別率を表している。
自身の研究内容について
研究テーマ : 耐候性鋼橋の耐候性鋼材の錆の現地調査に参加し...
About his own research
Research theme : While participating in a field survey of...
耐候性鋼材の錆の調査は来週の水曜日に行く予定であり、この...
The investigation of rust on weather resistant steel is s...
1, 画像処理を加えてCNNで学習させたもの
1, The images of mnist, a data set containing nearly 60,0...
使用したデータ Data used(example) &ref(http://www.str.ce...
,数字 ,正答率 ,正解率
,number ,correct response rate ,correct response rate
,0 ,9/9 ,9/9
,1 ,8/9 ,1/9
,2 ,9/9 ,6/9
,3 ,9/9 ,6/9
,4 ,9/9 ,0/9
,5 ,9/9 ,5/9
,6 ,5/9 ,2/9
,7 ,8/9 ,4/9
,8 ,6/9 ,6/9
,9 ,6/9 ,0/9
2, 判別したい画像を以下のように変更し 判別する画像の加工,...
2, Change the image to be discriminated as follows, proce...
,数字 ,正答率 ,正解率
,number ,correct response rate ,correct response rate
,0 ,10/10 ,100.00%
,1 ,0/10 ,0.00%
,2 ,10/10 ,100.00%
,3 ,10/10 ,100.00%
,4 ,6/10 ,60.00%
,5 ,10/10 ,100.00%
,6 ,0/10 ,0.00%
,7 ,10/10 ,100.00%
,8 ,10/10 ,100.00%
,9 ,10/10 ,100.00%
3, 2で用いたスクリプトはmnistをCNNで用いて学習させる機能...
3, The script used in 2, did not have the ability to trai...
,数字 ,正答率 ,正解率
,number ,correct answer rate ,correct answer rate
,1 ,0/10 ,0.00%
,2 ,8/10 ,80.00%
,3 ,10/10 ,100.00%
,4 ,8/10 ,80.00%
,5 ,10/10 ,100.00%
,6 ,1/10 ,10.00%
,7 ,10/10 ,100.00%
,8 ,10/10 ,100.00%
,9 ,10/10 ,100.00%
4, 判別したい画像に加工処理を施すだけでは正答率を100%に近...
4, Since there is a limit to achieving a correct response...
,数字 ,正答率 ,正解率
,number ,correct answer rate ,correct answer rate
,0 ,15/15 ,100.00%
,1 ,15/16 ,93.75%
,2 ,15/16 ,93.75%
,3 ,16/16 ,100.00%
,4 ,15/16 ,93.75%
,5 ,15/16 ,93.75%
,6 ,12/16 ,75.00%
,7 ,15/16 ,93.75%
,8 ,16/16 ,100.00%
,9 ,15/16 ,93.75%
Data used in 2-4 (example)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
5, 判別したい画像をmnistから抽出したデータに変更した。2~4...
5, The image to be discriminated was changed to the data ...
,数字 ,正答率 ,正解率
,number ,correct answer rate ,correct answer rate
,0 ,20/20 ,100.00%
,1 ,20/20 ,100.00%
,2 ,20/20 ,100.00%
,3 ,20/20 ,100.00%
,4 ,20/20 ,100.00%
,5 ,20/20 ,100.00%
,6 ,20/20 ,100.00%
,7 ,20/20 ,100.00%
,8 ,20/20 ,100.00%
,9 ,18/20 ,90.00%
Data used(example)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
6, 数字の書いてある部分(画像の中心部分)になるべく白い点を...
6, I tried to avoid white dots in the numbered area (cent...
,数字 ,正答率 ,正解率
,number ,correct answer rate ,correct answer rate
,0 ,20/20 ,100.00%
,1 ,20/20 ,100.00%
,2 ,20/20 ,100.00%
,3 ,20/20 ,100.00%
,4 ,20/20 ,100.00%
,5 ,20/20 ,100.00%
,6 ,20/20 ,100.00%
,7 ,19/20 ,95.00%
,8 ,20/20 ,100.00%
,9 ,20/20 ,100.00%
Data used(example)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
***5月第3週(5/15〜21) 物体分類判別 [#j417c515]
-判別をするにあたって以下の要素を加えた
--mnistの画像を加工した状態で機械学習を行う
--学習の偏りを避けるため複数の学習モデルを構築 ( h5 → ker...
--過学習抑制のためDropout関数の実装
--エポック数を増やす
--機械学習によてどの数字が判別に弱いのかを知るために混同...
--判別させる画像にも加工を加える
--最良のモデルを保存するためにEarlyStoppingとModelCheckpo...
これら全てを取り込んだのが以下の Ver5.0 = Mnist-Hanbetsu-...
Pythonコード (Mnist-Hanbetsu-Full_auto5.py)
,数字 ,正答率 ,正解率 ,正答率 ,正解率 ,正答率 ,正解...
,0 ,19/20 ,95.00% ,19/20 ,95.00% ,18/20 ,90.00%
,1 ,8/20 ,40.00% ,19/20 ,95.00% ,6/20 ,30.00%
,2 ,19/20 ,95.00% ,19/20 ,95.00% ,19/20 ,95.00%
,3 ,20/20 ,100.00% ,18/20 ,90.00% ,19/20 ,95.00%
,4 ,18/20 ,90.00% ,18/20 ,90.00% ,20/20 ,100.00%
,5 ,19/20 ,95.00% ,18/20 ,90.00% ,20/20 ,100.00%
,6 ,20/20 ,100.00% ,20/20 ,95.00% ,20/20 ,100.00%
,7 ,18/20 ,90.00% ,15/20 ,75.00% ,19/20 ,95.00%
,8 ,17/20 ,85.00% ,20/20 ,100.00% ,17/20 ,85.00%
,9 ,18/20 ,90.00% ,19/20 ,95.00% ,18/20 ,90.00%
Mnist-Hanbetsu-Full_auto4.py を実行したときと比較して明ら...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
下記の内容が判別精度低下の主な原因ではないかと考えている。
--学習方法を同一のCNNモデルから3種類のCNNモデルへ変更(ア...
--間違えて判別した画像を再学習させて精度の向上を図る
---1, 複数の学習方法の統合によるモデルの一貫性の欠如
→アンサンブル学習を行なうため3つの学習モデルで判別を行っ...
---2, 誤判別画像の再学習による過学習や偏り
→誤判別された画像のみを再学習させることで過剰に最適化され...
---3, スクリプトの構成が複雑化したことでデータ処理や前処...
→スクリプトの行が多くなるとどうしても学習データと推論デー...
-使用したCNNモデルについて
,要素 ,モデル① ,モデル② ,モデル③
,畳み込み層数 ,1 ,2 ,2
,カーネルサイズ ,3×3 ,3×3(2回) ,5×5 → 3×3
,プーリング ,あり(1回) ,あり(1回) ,あり(1回)
,Dropout ,なし ,あり(0.3) ,あり(0.25・0.5)
,BatchNorm ,なし ,あり(1回) ,あり(1回)
,全結合層 ,64ユニット ,128ユニット ,64ユニット
以下は全5バージョンのMnist-Hanbetsu-Full_autoを実行したと...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
以上より手書き数字の画像判別はあまり要素を盛り込みすぎる...
物体分類の判別
-以下のサイト (CIFAR-10) にあるデータを用いて画像識別を行...
https://www.cs.toronto.edu/~kriz/cifar.html
-CIFAR-10には飛行機, 自動車, 鳥, 猫, 鹿, 犬, カエル, 馬, ...
-Pythonスクリプトの実装
--とりあえずCIFAR-10上にある画像で学習を行い混同行列を作...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
左側がエポック数20, 右側がエポック数50としたがあまり精度...
-来週やること
--Animal.pyを改良して精度が向上するか確かめる
--今日はただCIFAR-10上にある画像を学習させただけなのでテ...
-物体分類の判別について最終的な目標として
--1, 学習する画像には特に加工を加えることなく
--2, テストデータから何枚か画像を抽出して判別をさせて
--3, 10種類ごとに正答率の割合を算出することを目的とする。
とりあえず
-① 学習画像は無加工とし混同行列を表示させるスクリプトと
-② テストデータから画像を抽出するスクリプト,
-③ 学習データを用いてテストデータを判別させるスクリプト
を組んでみた。3つとも独立(手動)で実行させてみて特に気にな...
--③について①で作成された学習データをもとに判別した結果を...
,タグ ,正答率
,airplane ,90.00 %
,automobile ,100.00 %
,bird ,85.00 %
,cat ,80.00 %
,deer ,95.00 %
,dog ,55.00 %
,frog ,95.00 %
,horse ,100.00 %
,ship ,100.00 %
,truck ,95.00 %
Pythonコード① (Animal.py)
Pythonコード② (Yobidashi.py)
Pythonコード③ (Buttai-Bunrui.py)
-1回の学習 (.py を実行させてから全て出力し終えるまで) で1...
--エポック数を1にして学習させれば20分ぐらいで結果が出力さ...
-その間に明日から本格的に始動する研究に向けて耐候性鋼材の...
目標に向けて大まかなステップ
--1, 100 枚/クラス を目標にデータ収集&CSV ラベリング
--2, .img .jpg形式で整理
--3, 整理した画像を 転移学習+EarlyStopping で試走
--4, 混同行列 と Grad‑CAM で失敗要因を可視化
--5, クラスあたりの枚数を増やし誤判定画像を追加収集 → 再...
目標を達成するために
-1. データ収集 … 構造物の部位や照明条件, 距離を揃えて撮影...
-- → 作業現場でスマホ等で撮影 → PCに画像データを移行する...
-2. 前処理 … 画像を 224×224〜299×299 にリサイズまたは正規...
-- torchvision.transforms (PyTorch をインストールした場合...
-- tf.keras.preprocessing (TensorFlow をインストールした...
-3. データ分割 … Train(訓練用) : Val(検証用) : Test(評価...
-- → sklearn.model_selection.train_test_split を用いて分...
-4. データ拡張 … ランダム左右反転・回転 ±15°
-- → ColorJitter(彩度・明度)や Cutout or RandomErasing ...
-5. モデル … 転移学習が最速
-- 転移学習とは … 1から CNN を学習させるには何千〜何万枚...
-- ResNet‑50 / EfficientNet‑B0 (事前学習済みのCNN)などを ...
--- → PyTorch Lightning / Keras を使用
-6. 学習 … EarlyStopping & ModelCheckpoint を用いる
-7. 評価 … 従来どおり混同行列を表示し学習は上手くいった...
-- 以下のツールで判断根拠を可視化
---① scikit‑learn … Pythonの定番機械学習ライブラリであり...
---② torchmetrics … PyTorch 専用の 評価指標ライブラリであ...
---③ grad‑cam … CNNが画像のどこを見てその点数であると判断...
- 8. 推論 … 重みを .keras (推奨) で保存
-- CLIツールをPython上で作成して判別させたい画像のラベル(...
より高精度・運用向けの改善アイデア
--クラス間で枚数が偏ると “稀な酷い錆(5点)” を取りこぼすの...
-- 回帰併用 … 5 段階を「1〜5 の連続値」として MSELoss も...
-- アンサンブル学習 … EfficientNet + ConvNeXt など 2–3 種...
***5月第4週(5/22〜5/28) 錆画像判別 [#j417c521]
-Animal-2.pyを実行して結果が出力されたので下の表に示す。
,評価項目 ,判別率(学習回数1回) ,判別率(学習回数60回)
,airplane ,74.20% ,86.80%
,automobile ,77.90% ,83.40%
,bird ,43.00% ,80.00%
,cat ,55.10% ,62.80%
,deer ,67.60% ,85.30%
,dog ,45.90% ,76.90%
,frog ,66.40% ,89.40%
,horse ,74.90% ,88.80%
,ship ,76.00% ,92.50%
,truck ,78.70% ,92.00%
,平均 ,65.97% ,83.79%
自作したスクリプトが正常に動作するかどうかを調べたかった...
学習させたデータをもとに cifar10 上にあるテスト用画像を各...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
,評価項目 ,判別率(学習回数1回) ,判別率(学習回数10回) ,判...
,airplane ,73.00 % ,100.00 % ,100.00 % ,88.20 %
,automobile ,84.00 % ,100.00 % ,100.00 % ,74.50 %
,bird ,53.00 % ,100.00 % ,100.00 % ,76.50 %
,cat ,68.10 % ,100.00 % ,100.00 % ,58.90 %
,deer ,71.60 % ,100.00 % ,100.00 % ,86.70 %
,dog ,44.00 % ,100.00 % ,99.00 % ,75.80 %
,frog ,69.00 % ,100.00 % ,100.00 % ,91.10 %
,horse ,79.00 % ,100.00 % ,100.00 % ,90.10 %
,ship ,83.00 % ,100.00 % ,100.00 % ,93.40 %
,truck ,89.70 % ,100.00 % ,100.00 % ,91.10 %
,平均 ,71.30% ,100.00 % ,99.90% ,82.63 %
5/23現在エポック数を60にして判別した結果は記載していない...
-午前中に高専の中島さん達と今後の動きについて話し合った。...
-東北各地から錆のデータが集まるまでまだ時間がかかりそうな...
--はじめに目視で評点を画像に入力するなどした後に機械がど...
錆の一例
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
過去の先輩が残してくださったセロハン試験の画像データがあ...
以下の画像はUMAPという高次元データを低次元空間に圧縮し、...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
この図から分かること
-1. 似た錆画像は近くに配置されている
--UMAPは元の画像の見た目の特徴の類似度を保持したまま、2次...
-2. KMeansクラスタリングにより錆画像が5つのグループに分か...
--色が異なる5つの「クラスタ (= 点)」は特徴的に似た錆画像...
--- 微細な粉状の錆
--- 鱗片状に剥がれた錆
--- 密集した粗い錆
--- 色調の異なる錆(赤錆、黒錆など)
-3. UMAPと2つの軸(UMAP-1とUMAP-2)の意味
--UMAP(Uniform Manifold Approximation and Projection)と...
--両軸は「人間が理解できる物理的意味」は持っておらずただ...
UMAPは錆の画像に評点をつけてから使うこととする。評点をつ...
5/29 やること
-評点を判別させるスクリプトの構築
**6月 [#j417c600]
***6月第1週(5/29〜6/4) [#j417c529]
昨日UMAPによって分類された錆の画像からグループ毎に10枚 合...
,評点 ,枚数
,1 ,6枚
,2 ,12枚
,3 ,15枚
,4 ,9枚
,5 ,8枚
-学習1回目
--錆の画像の一部にラベルをつけて機械学習させた後その結果...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
---紫色 … 評点1 紺色 … 評点2 翠玉色 … 評点3 ...
--評点2と3はある程度グループとして固まっているもののラベ...
-学習2回目
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
---紫色 … 評点1 紺色 … 評点2 翠玉色 … 評点3 ...
--1回目と比較してバラつきは少なくなったものの左上に固まっ...
-明日やること
--学習の繰り返し
-3回目と4回目
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
評点付きの錆画像データを見つけたのでこれをUMAPを用いて分...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-1. 点(クラスタ)の存在
--縦軸と横軸にそれぞれ注目するとUMAP-1(縦軸)で見ると広く...
--画像の中央から左側は縦方向に伸びていることが確認できる...
--左上には4~5の評点が密に集まる領域があり比較的状態が良...
-2. UMAPとスコア(≠評点)について
スコアは完全分離しているわけではないが、スコアごとにある...
--スコア1 : 縦軸に注目すると殆どの点がUMAP2の1〜5に集中し...
--スコア2 : UMAP2の -1〜3に集中している。
--スコア3, 4 : どちらも画像の左側に密集しているもののスコ...
--スコア5 : スコア4と同様な分布となっているが左上にかなり...
2方向のUMAPはそれぞれ何を基に分類を行っているか確認してみ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-錆画像から抽出した各特徴量の意味
, 特徴量名 , 説明 , 数字の大小で何が分かるか
, aspect_ratio , 縦方向エッジ量 ÷ 横方向エッジ量(= Sobel...
, sobel_x , 横方向(左右)の輪郭強度(エッジ) , 値が大き...
, sobel_y , 縦方向(上下)の輪郭強度(エッジ) , 値が大き...
, orientation_std , 画像内の輪郭や模様の方向性のばらつき...
, num_blobs , 二値化画像から検出された「斑点」や「模様の...
, roughness , 周辺の濃淡変化の激しさや表面のざらつき度(...
-UMAP1は sobel_x, sobel_y, roughness, に強い負の相関が見...
-よってUMAP1の場合横・縦方向のエッジ(sobel_x・sobel_y)が...
-UMAP2の場合は模様の方向(orientation_std)がバラバラだとUM...
--UMAPの座標が意味するもの
, 軸 , 表すものの傾向
, UMAP1 , 主に「模様の強さ(エッジ強度)」+「表面の粗さ...
, UMAP2 , 主に「模様の方向性のバラバラ度合い(orientation...
このことから、UMAPプロット上で:
--左下あたりにいる画像:鋭いエッジがあり、ざらざらした錆...
--右上あたりにいる画像:模様がバラバラ・薄く滑らかな錆(...
といった分布傾向が読み取れる。
1回の学習ごとに手動で評点を入力してはの繰り返しだと終わり...
評点付きの錆画像は何枚か同じ画像が存在したため重複してい...
, 処理 , 内容
, 画像の反転 , ただ画像を鏡写しにするだけ
, 画像の回転 , ランダムで ±10°~±30°、±90°、±180°のいずれ...
, 類似画像の削除 , 画像処理分野で画像の類似度を数値化する...
MMLI (=More Machine Learning Images).py … 既存の画像を反...
Hyoutenhuriwake+UMAP2.py … ラベル付きの画像を学習させた...
上記のPythonスクリプトを実行させると24枚しかなかった画像...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
以前よりもクラスタリング精度が向上したのではないか。
-1. 評点ごとの分離性
--この画像ではグループ5(黄色)が密集しており、他の評点と...
--以前のUMAP図では評点ごとの混在が見られたのに対し今回は...
-2. 色の連続性
--カラーバーは評点1〜5の連続スケールとなっているが評点を...
-3. まとめ
--このUMAP可視化では以前よりも評点ごとのクラスタリングが...
-次回やること : CNN×スクリプトの作成
--augmented_imagesをCNNで機械学習させる (3層×3回 や 3通り...
--評点付き教師画像はラベルの先頭が評点となっている。 例) ...
--上記の学習結果を基に判別させたい画像(Fuji2として保存) ...
--また判別した結果を評点別にUMAP + KMeans で可視化 & 伝播...
--画像における2方向(x, y軸)のUMAPはそれぞれ何を基に分類を...
***6月第2週(6/5〜6/11) [#j417c606]
-CNNを用いて錆画像を判別させるスクリプトを作成中
Main.py ーー軽量化ー> Main2.py
-とりあえず評点付き錆画像を3タイプのCNNで判別させた学習結...
3パターンのCNNは以下の通り
# === CNN構造定義(バリエーションを3通り用意) ===
def create_model(version):
model = models.Sequential() ...
model.add(layers.Input(shape=(*IMAGE_SIZE, 3))) ...
if version == 0: (パターン1)
model.add(layers.Conv2D(64, (3,3), activation='r...
model.add(layers.BatchNormalization()) ...
model.add(layers.Conv2D(64, (3,3), activation='r...
model.add(layers.MaxPooling2D((2,2))) ...
model.add(layers.Dropout(0.3)) ...
model.add(layers.Conv2D(128, (3,3), activation='...
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3,3), activation='...
elif version == 1: (パターン2)
model.add(layers.Conv2D(64, (5,5), activation='r...
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Dropout(0.3)) ...
model.add(layers.Conv2D(128, (3,3), activation='...
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3,3), activation='...
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Dropout(0.3)) ...
elif version == 2: (パターン3)
model.add(layers.Conv2D(64, (3,3), activation='r...
model.add(layers.Conv2D(64, (3,3), activation='r...
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Dropout(0.3)) ...
model.add(layers.Conv2D(128, (3,3), activation='...
model.add(layers.Conv2D(128, (3,3), activation='...
model.add(layers.BatchNormalization()) ...
model.add(layers.GlobalAveragePooling2D()) ...
model.add(layers.Dense(128, activation='relu')) ...
model.add(layers.Dropout(0.5)) ...
model.add(layers.Dense(NUM_CLASSES, activation='soft...
model.compile(optimizer='adam', ...
loss='sparse_categorical_crossentropy'...
metrics=['accuracy']) ...
return model
-上記のCNNで学習させると特に評点1と評点2, 評点5の判別が上...
-評点2には人間の目で評点1と捉えられてもおかしくない画像が...
--評点2に保存されていた評点1だと思われる画像(評点2の錆画...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
従来はラベルの付いた画像をそのまま学習させていたが、判別...
RSRC.py (rust_score_regression_cluster.py)
→ 錆画像にラベルをつける際に判断材料となるであろう錆の粒...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-1, Mennsekisa:錆の大きさのバラつき(面積差)
クラスタ2が評点2と完全一致しており非常に良い識別精度とい...
-2, Color_kosa:色の濃さ(グレースケール)
クラスタ2が評点2と完全一致している。クラスタ0と4も比較的...
-3, Mensekihi:錆面積比
錆を黒, それ以外の部分を白と区別させて面積比を求めた。ク...
-4, Sabi_kazu:錆の数
どのクラスタも比較的均等に分散しているため、錆の数と錆の...
-5, Ryukei:錆の大きさ・粒径
クラスタ2が評点2に1.00、クラスタ3が評点2に0.96 評点2, 3...
以上よりクラスタ2のように明確に分類できているものもあった...
-特徴量付き CNN(ハイブリッドモデル)
--画像はCNNで処理しつつ、錆の形状・数などの特徴量を別入力...
--画像から抽出される“感性的”なパターンと、手動で抽出した“...
--CNN単体だと分類が不安定(特に評点1と2)
--面積・数・濃淡などの情報が評価に明らかに効いている場合
rating_cnntrainer.py
上記のスクリプトで処理していることについて
, 項目 , 説明
, 画像入力 , CNNで64×64の画像から特徴を抽出
, 数値特徴量入力 , .npyファイルから読み込まれた錆特徴量(...
, ハイブリッド構造 , CNN特徴と手作り特徴を Concatenateで...
, モデルバージョン , 3種類のCNN構造を用意したアンサンブル...
, 予測 , 3モデルの出力平均で最終スコアを判定 1〜5の評点に...
, 錆特徴量名 , 内容説明 , 特徴量例
, rust_ratio , 錆の占有面積(全体に対する割合) , 平均輝...
, num_blobs , 錆のかたまり数(connected components) , 形...
, np.mean(gray) , グレースケールの平均(明るさ) , GLCM(...
, np.std(gray) , グレースケールの標準偏差(濃淡の変動) ,...
, np.mean(sobel_edges) , エッジ強度(輪郭の激しさ) , Sob...
, area_diff , 最大面積 - 最小面積の錆領域の差 , 形状(面...
ラベル付き画像 (947枚) を学習させてラベルを付けていない画...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
CNN+binning.py
-CNN回帰モデル + binning
--評点(1〜5)を離散ラベルではなく連続的な“点数”と捉えて...
--各評点の境界が曖昧で連続的なスコアで表現した方が自然
--評価者の意識が「点数感覚」に近い(例:2.3点 ≒ 評点2)
-構成内容
--1. 目的
画像を入力しCNNで連続的なスコア(評点1.0〜5.0の0.1点刻み...
そのスコアをbinning(1点〜5点)に変換して評点分類として扱う
--2. モデル構成(従来のCNN分類との違い)
, 項目 , CNN分類 , CNN回帰 + binning
, 出力層 , Dense(NUM_CLASSES. activation='softmax') , Den...
, 損失関数 , sparse_categorical_crossentropy , mean_squar...
, 出力形式 , クラス確率(0〜1) , 連続値スカラー(例:3.7...
, 評点の扱い , クラス分類(0〜4) , binning により 1〜5 ...
---出力層・形式 , 評点の扱い
今までのCNNは NUM_CLASSES(出力次元数 = 何クラスに分類する...
model.add(Dense(5, activation='softmax')) → この場合NU...
とスクリプトに入力すれば [0.1, 0.1, 0.7, 0.05, 0.05] のよ...
model.add(Dense(1))
と入力すれば錆画像の評点は3.27点というように連続スカラー...
---損失関数
分類の場合
loss='sparse_categorical_crossentropy'
で正解クラスだけ1、それ以外0のone-hotラベル (異なるグルー...
回帰の場合
loss='mean_squared_error'
で予測値 3.27 とラベル 3.0 との差 0.27 を最小化し「正解と...
--3. 学習データ(ラベル)の準備
.png などの画像を Image.open → resize → 正規化 の順に変...
--4. binning(スコア→評点)
学習後の推論時に以下のような関数で連続値を離散化する (一例)
, def binning(score):
, if score < 1.5: return 1
, elif score < 2.5: return 2
, elif score < 3.5: return 3
, elif score < 4.5: return 4
, else: return 5
--5. 評価方法
学習時は回帰(MSE = 平均二乗誤差)で最適化されるが、推論...
from sklearn.metrics import accuracy_score, confusion_ma...
y_pred = model.predict(x_val).flatten()
y_binned = [binning(p) for p in y_pred]
acc = accuracy_score(y_val, y_binned)
--6. CNN回帰モデル + binning に追加で工夫できること
工夫 内容
出力に ReLU + offset 出力が1〜5に制限されるようにする(例...
Huber loss に変更 MSEよりも外れ値に強く安定
binning間隔を柔軟化 ヒストグラムを見て等間隔でない分割に...
CNN Backbone
┌───────────┐
│ Conv2D → Pool → ...│
└─────┬─────┘
▼
┌───────────┐
│ Flatten → Dense() │
└─────┬─────┘
▼
分類モデル 回帰モデル
┌─────────┐ ┌─────┐
│ Dense(5, softmax)│ │ Dense(1) │
└─────────┘ └─────┘
↓ ↓
argmax float値 → binning → int
***6月第3週(6/12〜6/18) [#j417c612]
これまでの結果から錆画像を識別するのはCNN回帰 + binning ...
現時点(6/12現在)で使用している錆画像フォルダーとPythonス...
-錆画像フォルダー
--1, Fuji2 … ラベル付けを行っておらず学習結果を基にどのく...
--2, train_images … ラベル付けされた画像のあるフォルダー
--3, augmented_images … train_imagesに入っているラベル付...
--4, Kekka_Reg … 下記の CNN + binning.py を実行した後に出...
-Pythonスクリプト
--1, MMLI.py … train_images 内に保存されている画像に指定...
&ref(http://www....
--2, CNN+binning.py … この学習で核となるスクリプトであり...
回帰スコアから評点の分類を行っていた。スクリプトのデフォ...
, 評点 , 回帰スコア
, 1 , 2.30点以下
, 2 , 2.31点以上3.00点以下
, 3 , 3.01点以上3.40点以下
, 4 , 3.41点以上3.64点以下
, 5 , 3.65点以上
以下回帰スコア別に保存された錆画像を貼っておく
-評点1
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-評点2
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-評点3
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-評点4
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-評点5
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-来週の外国語文献までに原稿と発表スライドを書く。
-CNN+binning.py内のアンサンブル学習に少し手を加えた
CNN+binning3.py
CNN+binning.py内のアンサンブル学習についての変更点(CNN+bi...
-CNNモデルの変更, 今までは名前のない簡単な構造の自作CNNモ...
--1, EfficientNetV2S... 高精度なのに学習が早い・モデルが...
--2, DenseNet121... ResNetよりモデルは軽く錆やひび割れと...
--3, MobileNetV3Large... 特徴の抽出力はそこまで高くないも...
def create_model(version): ...
input_img = Input(shape=(*IMAGE_SIZE, 3)) ...
if version == 0: # EfficientNetV2S ...
base = EfficientNetV2S(include_top=False, weight...
...
...
elif version == 1: ...
base = DenseNet121(include_top=False, weights="im...
elif version == 2: ...
base = MobileNetV3Large(include_top=False, weight...
base.trainable = False ...
x = base.output ...
x = layers.GlobalAveragePooling2D()(x) ...
x = layers.Dense(512, activation='relu')(x) ...
x = layers.BatchNormalization()(x) ...
x = layers.Dropout(0.5)(x) ...
x = layers.Dense(256, activation='relu')(x) ...
x = layers.BatchNormalization()(x)
x = layers.Dropout(0.3)(x)
x = layers.Dense(128, activation='relu')(x) ...
x = layers.Dropout(0.2)(x)
output = layers.Dense(1)(x) ...
model = Model(inputs=input_img, outputs=output) ...
model.compile(optimizer='adam', loss=custom_penalizi...
...
...
return model ...
明日やること
-CNN+binning3.pyの回帰スコア調節
***6月第4週(6/19〜6/25) [#j417c619]
CNN+binning2.py と CNN+binning3.py の比較
, 諸条件 , CNN+binning2.py , CNN+binning3.py
, CNNモデル , 自作CNNモデル3パターン , EfficientNetV2S、D...
--2つのPythonスクリプトで異なる部分はCNNモデルぐらいであ...
-評点と回帰スコアの対応表
, 評点 , 回帰スコア
, 1 , 0.00点以上2.30点未満
, 2 , 2.30点以上3.10点未満
, 3 , 3.10点以上3.40点未満
, 4 , 3.40点以上3.70点未満
, 5 , 3.70点以上5.00点未満
上記の条件でそれぞれ学習させると評点ごとに保存された枚数...
-評点ごとに保存された枚数
, 評点 , CNN+binning2.py , CNN+binning3.py
, 1 , 24 , 13
, 2 , 110 , 119
, 3 , 54 , 123
, 4 , 59 , 146
, 5 , 418 , 264
評点が1, 2 の場合は判別に大きな差は見られなかったが、逆に...
CNN+binning3.pyに
-面積・粒径・Sobelエッジ量などの数値特徴を別途抽出
-CNN出力 + 特徴量ベクトル を結合してDense層に入力 →CN...
を加えたところ回帰スコアが負の値や2桁になってしまったため...
CNN+binning4.py にUMAPを画像で出力することに加えて評点も...
-シグモイド関数 パラメータ
--SIGMOID_SCALE ...
通常の sigmoid(x) は0〜1の値を出力するがそれを 5.0 * sigm...
--SIGMOID_K ...
シグモイド関数の傾きを調整する係数でありあいまいな場合小...
--SIGMOID_X0 ...
シグモイド関数の 中央(変曲点)をどこに置くかを決めるシフ...
CNN+binning5.py
CNN+binning5.pyの内容
-1. CNNを用いた錆画像の評点回帰モデルの構築
EfficientNetV2S / DenseNet121 / MobileNetV3Large の3つの...
-2. 画像+数値特徴のハイブリッドモデル構築
画像からのCNN出力に加え、以下の 数値特徴量(4種類)を自動...
-錆面積割合(rust_ratio)
-粒径(平均・最大)
-Sobelエッジ量
-錆の個数
-3. Optunaによるしきい値の自動最適化
特徴量(粒径, Sobel量など)に基づき評点を補正するためのし...
今まではCNNで評点付き錆画像を学習させた後、判別させたい錆...
例) 錆画像の回帰スコアが3.45点であり、評点と回帰スコアの...
例を読んで理解できただろうか。今回はこの判別方法を行った...
ただUMAPでの分類はそれなりであるものの評点ごとに保存され...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
ちなみにUMAPの理想は同じ色の点が密集しているのが理想であ...
***6月第5週(6/26〜7/2) [#j417c626]
評点を1.0~5.0点の間に変換する方法として シグモイド関数, ...
1, シグモイド関数(+スケーリング)
シグモイド関数 ''sigmoid(x) = 1 / (1 + e^(-x))'' は、出力...
・実装方法
output = Dense(1, activation='sigmoid')(x) #...
output = Lambda(lambda z: 1.0 + 4.0 * z)(raw_output) #...
※PCやバージョンによっては "Lambda" 関数がインストールさ...
SIGMOID_SCALE = 5.0 # 出力を0〜5にスケーリング
SIGMOID_K = 1 # 傾き係数(大きくするとS字...
SIGMOID_X0 = 0 # 切片(出力の中心)
--------------------------------------------------------...
output = layers.Dense(1)(x)
output = ScaledSigmoid(SIGMOID_SCALE, SIGMOID_K, SIGMOID...
メリットとして上記のスクリプトを組み込むだけで1.0~5.0点の...
2, 線形変換(+出力範囲制限)
出力層は線形のまま Dense(1) を用いて範囲外の値(~1.0, 5.0...
・実装方法
output = layers.Dense(1)(x)
output = layers.Lambda(lambda z: tf.clip_by_value(z, 1.0...
※Lambdaを実装しない場合(一例)
# === ClipLayer の定義 ===
@register_keras_serializable()
class ClipLayer(Layer):
def __init__(self, min_val=1.0, max_val=5.0, **kwarg...
super().__init__(**kwargs)
self.min_val = min_val
self.max_val = max_val
def call(self, inputs):
return tf.clip_by_value(inputs, self.min_val, se...
def get_config(self):
config = super().get_config()
config.update({
'min_val': self.min_val,
'max_val': self.max_val
})
return config
--------------------------------------------------------...
output = layers.Dense(1)(x)
output = ClipLayer()(output)
メリットとしてシグモイド関数と同様に実装が簡単であり、ネ...
3, 活性化関数 [tanh(x)] ( + スケーリング)
シグモイド関数と異なる箇所は用いる関数のみで、活性化関数 ...
・実装方法
output = Dense(1, activation='tanh')(x) #...
output = Lambda(lambda z: 3.0 + 2.0 * z)(raw_output) #...
※PCやバージョンによっては "Lambda" 関数がインストールさ...
メリットとして出力が中央(3.0)に対して対称でありシグモイ...
これらをまとめると
, 方法 , 評点の保証 , 出力の自由度 , 実装難易度 , 学習安...
, 1. Sigmoid + スケーリング , ◎ 常に1~5 , △ 中央に寄る ,...
, 2. Clipによる制限 , ◯ 制限あり , ◎ 高い , ◎ 簡単 , △ 勾...
, 3. Tanh + スケーリング , ◎ 常に1~5 , ◯ 対称性あり , ◯ ...
, 制限なし(今まで) , × 保証なし , ◎ 最大自由度 , ◎ 簡単 ,...
どれも課題があるもののその中で学習安定性を重視したいと考...
これから5つのアンサンブル学習(計15のCNNモデル)で精度・時...
画像から評点を出すまでの流れ
1. 前処理・画像とスコアの準備
-指定したフォルダから画像ファイルを読み込み、画像ファイル...
2. モデル構築・学習
-複数のCNNモデル(アンサンブル)を構築させ各モデルはImage...
--この損失関数(カスタム損失関数)では予測値と正解値の絶...
3. 評点の予測
-テスト用画像(未知画像)を同じく前処理し各モデルで回帰ス...
4. モデル評価・可視化
-検証データに対しても同様に予測し回帰スコアと評点(整数)...
-まとめ
--1, 画像を前処理して読み込み、元スコアを取得(前処理→特...
--2, 複数のCNNモデルで回帰学習(1〜5点)(学習)
--3, テスト画像を各モデルで予測し、平均スコアをbinningで1...
--4, 評点ごとに画像を分類・保存し、検証データで精度評価や...
結果
-モデルA ["EfficientNetV2M", "InceptionResNetV2", "DenseN...
--学習時間:4時間15分, 分類:評点1から順番に[2, 32, 365, ...
-UMAP
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-モデルB["EfficientNetB3", "ResNet50", "MobileNetV3Small"...
--学習時間:1時間36分 分類:評点1から順番に[0, 0, 5, 153,...
-UMAP
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-モデルC["InceptionResNetV2", "ResNet101", "EfficientNetV...
--学習時間:7時間32分 分類:評点1から順番に[0, 11, 73, 33...
-UMAP
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-モデルD["DenseNet201", "EfficientNetV2M", "EfficientNetV...
--学習時間:6時間48分 分類:評点1から順番に[0, 15, 84, 46...
-UMAP
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-モデルE["EfficientNetV2S", "EfficientNetV2M", "Efficient...
--学習時間:4時間33分 分類:評点1から順番に[0, 4, 90, 409...
-UMAP
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
**7月 [#j417c700]
***7月第1週(7/3〜7/9) [#j417c701]
5タイプのCNNモデルの結果
-パターンA
クラスタの分離が良好であり特にグループ1・2が図の右側に、...
-パターンB
グループ5は分離はしているものの何箇所にも散らばっている上...
-パターンC
グループ1, 2, 5の分類は明瞭であるもののグループ3, 4がクラ...
-パターンD
全体的に「偏り」が見られ、クラスタ構造自体は悪くないがグ...
-パターンE
グループ1, 2は明瞭な分類ができているものの3, 4は分類が上...
→ パターンAに劣らない分類性能は持っているものの複数のクラ...
-まとめ
, パターン , 分類精度 , 分離度 , 安定性 , 処理速度 , コメ...
, A , ◎ , ◎ , ◎ , ○ , 実用・研究の両面でバランス型 安定...
, B , △ , △ , △ , ◎ , 軽量だが精度不足 軽量で高速な実験...
, C , ◎ , ○ , ◎ , △ , 深層学習として高性能、やや重い 安...
, D , ○ , ○ , △ , ○ , 特徴は良いが評点にはやや曖昧さ
, E , ◎ , ◎ , ◎ , △ , 最強構成、実装コスト高め 最大性能を...
学習時間, 精度の面から元々行っていた CNN+binning3.py を改...
来週までにやること
-評点ごとの平均絶対誤差を算出する
-混同行列を表示する(分類的な観点)
-誤差の大きい画像を抽出する
Pythonスクリプトに 平均絶対誤差 と 混同行列の可視化 を追...
平均絶対誤差 MAE は1枚毎に予測値と正解値のズレの大きさを...
-augmented_imagesに保存されているラベル付き錆画像
評点1:144枚 評点2:1,131枚 評点3:1,004枚 評点4...
CNN+binning3-UMAP.pyで学習させてみた
, -項目- , -条件-
, 評点を付ける画像サイズ(Fuji2) , 334×334ピクセル
, 使用するCNNモデル , EfficientNetV2S / DenseNet121 / Mob...
, 全結合層 , 6層
, Dropout関数の実装 , あり
, 活性化関数 , なし(出力された予測・回帰スコアをbinning...
, 学習中に精度が良くならない状態が続いたら学習をやめるエ...
, エポック数(学習する最大の回数) , 50回
※binningスコアと予測・回帰スコアの違いについて
-予測・回帰スコアとはCNNモデルが出力する ”連続的な数値(...
-binningスコアとは連続的な回帰スコアを "評点1〜5のカテゴ...
# === binning: 予測・回帰スコア → 評点1〜5 ===
def binning(score):
if score < 2.00: return 1 #予測・回帰スコアが2.0...
elif score < 2.50: return 2 #予測・回帰スコアが2.0...
elif score < 3.00: return 3 #予測・回帰スコアが2.5...
elif score < 3.50: return 4 #予測・回帰スコアが3.0...
else: return 5 #予測・回帰スコアが3.5...
上記の条件に当てはめると予測・回帰スコアは以下のように変...
, 予測・回帰スコア , → binningスコア
, 1.96 , → 1
, 2.23 , → 2
, 2.86 , → 3
, 3.48 , → 4
, 4.01 , → 5
-7/4
特徴量を実装してみた
--錆の占める割合…白黒画像の場合 mask(cv2.threshold)とい...
--錆の数… cv2.findContours というツールを用いて錆部分の輪...
--最大錆サイズ…cv2.contourAreaというツールを用いて各輪郭...
--※錆の部分とそうでない部分の色が逆転してしまっているが画...
-7/7
引き続き特徴量の補正関数について試行錯誤を重ねていく 重回...
-7/8 CNNに限らず機械学習(アンサンブル学習)を行なうと学...
--今まで:スタート ⇒既存の .keras ファイルを削除 ⇒ アンサ...
--これから:スタート ⇒既存の .keras ファイルを呼び出す ⇒ ...
-7/9
CNN+binning5.py
上記のスクリプトで行われていること
--ラベルの付いていない錆画像から画像の面積(ピクセル)に...
--3つのCNNモデル(EfficientNetV2S / DenseNet121 / Mobile...
--学習を繰り返している中で最も結果・精度が良くなったとこ...
--学習結果を保存するファイルとして .keras ファイルという...
例)📂 1_001.jpg → 回帰: 2.62 → 補正後: 2.52 → 評点: 2 |...
📂 1_002.jpg → 回帰: 3.14 → 補正後: 3.14 → 評点: 3 |...
📂 1_003.jpg → 回帰: 2.71 → 補正後: 2.71 → 評点: 3 |...
📂 1_004.jpg → 回帰: 3.37 → 補正後: 3.67 → 評点: 4 |...
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜 省略 ...
📂 4_094.jpg → 回帰: 2.58 → 補正後: 2.48 → 評点: 2 |...
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜 省略 ...
平均絶対誤差:0.11544688538164781(0.3を下回っていれ...
--評点の出力について まず3つのCNNモデルで学習した結果を...
--※1 補正したスコアが0.00〜5.00の範囲から外れてしまった...
--※2 補正係数については以下のスクリプトで設定している。
# 特徴量によるスコア補正関数(抜粋)
def adjust_score_by_features(score, rust_ratio, rust_cou...
correction = 0.0
#錆面積割合
if rust_ratio >= 0.70:
correction -= 0.4 #錆面積割合が0.7(7...
#elif 0.50<= rust_ratio < 0.70: #評点の結果に応じて...
# correction -= 0.3
elif rust_ratio < 0.10:
correction += 0.2
#錆の数
if rust_count > 800:
correction -= 0.1
〜〜〜〜〜〜 省略 〜〜〜〜〜〜
#最大錆サイズ
if max_blob_area >= 10000:
correction -= 0.3
〜〜〜〜〜〜 省略 〜〜〜〜〜〜
return score + correction
--※3 特徴量の抽出方法について
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
***7月第2週(7/10〜16) [#j417c710]
-7/10
特徴量補正の有無による評点結果の比較検証
CNNによる回帰スコアに対して画像特徴量(錆割合、錆数、最大...
-手順
--1, CNN+binning4.py を実行しCNNモデルの学習を行った。こ...
--2, 続いてCNN+binning5.py を実行。先ほど CNN+binning4.py...
--3, このとき学習モデルは同一であるため、元の「回帰スコア...
--4, 次にCNN+binning5.py 側で新たに .keras ファイルを出力...
--5, 最後にその CNN+binning5.py で出力した .keras モデル...
以下に結果を示す
-① 学習に "特徴量を使い(青)、使わず(朱)" 評点を算出す...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-② 学習に "特徴量を使わず(緑)、使った(黃)" 評点を算出...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-③ ①と②の分布図をひとまとめにしたグラフを以下に示す
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
, グラフの色 , 学習に用いたpython , 評点算出の際に用いたp...
, 青 , CNN+binning4.py , CNN+binning5.py , 0.1783
, 朱 , CNN+binning4.py , CNN+binning4.py , 0.1756
, 緑 , CNN+binning5.py , CNN+binning5.py , 0.1496
, 黃 , CNN+binning5.py , CNN+binning4.py , 0.1510
-7/11
--UMAPの画像を見比べてみると変化があったのは一部の点の色...
--上記のグラフからわずか4通りの組み合わせで学習を行ったに...
-7/14〜16
O.Cの準備及び外国語文献の発表スライド作成に注力したため進...
***7月第3週(7/17〜23) [#j417c717]
-7/17
今週はMAEに注目して精度向上を狙っていきたい。現時点では学...
***7月第4週(7/24〜30) [#j417c724]
-7/28〜30
CNN+binning6.py
binning5.pyには実装しなかった重回帰分析がメインとなる。今...
-ここ最近はMAEをなるべく0に近づけるようにしている(CNN+b...
, 回 , 錆面積範囲 , 錆数 , 最大錆サイズ , MAE , 評点別画...
, 1 , 定義しない , 定義しない , 定義しない , 0.129 , 13-1...
, 2 , 0.5以上:-0.4 0.35以上0.5未満:-0.15 0.1以下:+0.15...
, 3 , 0.5以上:-0.4 0.35以上0.5未満:-0.15 0.05以下:+0.1...
, 4 , 2と変化なし , 削除(定義しない) , 2と変化なし , 0....
, 5 , 0.5以上:-0.4 0.33以上0.5未満:-0.15 0.05以上0.1未...
, 6 , 0.6以上:-0.5 0.45以上0.6未満:-0.3 0.25以上0.45未...
, 7(重回帰) , ーーー , ーーー , ーーー , 0.126 , 13-125...
特徴量に応じて回帰スコアから加減点を行うことは、定義しな...
データ数を増やすのはもう少し時間がかかりそう。重回帰で学...
-① 特徴付き画像フォルダを読み込む(学習画像 + ラベル = 回...
-② 特徴量(錆割合・数・最大サイズなど)を抽出
-③ CSVに保存(訓練データ・予測用データ兼用)
-④ 特徴量と評点(ラベル)で重回帰学習 → .pkl保存
-⑤ 同じ特徴量データに対して、重回帰を適用 → 評点を補正
-⑥ CNN3種(EffNetV2S/DenseNet/MobileNet)で学習 → アンサ...
-⑦ 評点の最終出力(重回帰補正後の回帰スコア + CNN予測スコ...
-⑧ 結果保存(CSV・UMAP・混同行列など)
を1つのスクリプトにまとめたものをこれから1周間で作成でき...
**8月 [#j417c800]
***8月第1週(7/31〜8/6) [#j417c731]
-8/3
1日から県内に架けられている耐候性鋼橋約90箇所の中から調査...
[["大学"を Google Map で開く>https://www.google.com/maps/...
-8/4
耐候性鋼橋約90箇所が調査可能かどうかを Google map で見な...
機械学習についてはやはり学習元となるセロハン試験で採取さ...
-MAE向上へ
--1, データ量を無意味に水増しするのではなく意味的な水増し...
--2, Soft label にラベル品質スコア導入…手動で「このラベル...
--3, 高次元な非線形補正…重回帰の代わりに LightGBM, XGBoos...
--4, スコア帯(4, 5)の混同行列精度や Confusion Matrix上...
-8/5, 6
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
上記の画像(混同行列)は10,000枚近くあるラベル付きの画像...
--評点4画像の枚数・バリエーション不足 → 水増しによる拡張...
--上記の原因によりモデルが評点4の特徴を学習できていない →...
が考えられる。精度向上のため
-- 評点4の「代表的な画像」を新たに5〜10枚ほど追加 → 判別...
--追学習 → 評点4に関して予測誤差が大きい画像だけを再学習...
を行うことで正答率34%からどれほど上げられるか。とりあえず...
結果
-評点4のラベル付画像を新たに6枚追加した場合のみの混同行列...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
生成された画像が少ないが評点4の正答率が大幅に向上した。(...
***8月第2週(8/7〜8/13) [#j417c807]
-8/7
--全評点のラベル付画像を10枚(評点5だけ9枚)にした場合の...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
画像を水増ししすぎてしまったのか綺麗な対角線を描けなかっ...
-8/8
--1枚あたりの水増しを約60枚にして学習させてみた。
***8月第3, 4週(8/14〜8/27) [#j417c814]
-8/14
一度に複数の条件で学習を行えるようにしたため、学習条件を...
, モデルNo. , 水増し画像枚数 , 水平・鉛直方向への移動 , ...
, 1 , 50 , あり(水平・垂直ともに最大10%移動 他のモデル...
, 2 , 50 , なし , 30回 , 32枚
, 3 , 50 , あり , 50回 , 32枚
, 4 , 50 , なし , 50回 , 32枚
, 5 , 50 , あり , 30回 , 64枚
, 6 , 50 , なし , 30回 , 64枚
, 7 , 50 , あり , 50回 , 64枚
, 8 , 50 , なし , 50回 , 64枚
, 9 , 100 , あり , 30回 , 32枚
, 10 , 100 , なし , 30回 , 32枚
, 11 , 100 , あり , 50回 , 32枚
, 12 , 100 , なし , 50回 , 32枚
, 13 , 100 , あり , 30回 , 64枚
, 14 , 100 , なし , 30回 , 64枚
, 15 , 100 , あり , 50回 , 64枚
, 16 , 100 , なし , 50回 , 64枚
, 17 , 150 , あり , 30回 , 32枚
, 18 , 150 , なし , 30回 , 32枚
, 19 , 150 , あり , 50回 , 32枚
, 20 , 150 , なし , 50回 , 32枚
, 21 , 150 , あり , 30回 , 64枚
, 22 , 150 , なし , 30回 , 64枚
, 23 , 150 , あり , 50回 , 64枚
, 24 , 150 , なし , 50回 , 64枚
-統一した諸条件
, 条件内容 , 条件
, ランダム回転(画像を 最大 ±(記入された数字)° の範囲で...
, 画像を反転させるか , ON
, 画像の明るさ変更(明るさをA倍(暗く)〜B倍(明るく) の...
, 使用するCNNモデル , EfficientNetV2S、DenseNet121、Mobil...
-8/14
--全24パターンのMAEが出力されたのでその結果を貼り付ける。
, モデルNo. , MAE
, 1 , 0.196729463582136
, 2 , 0.243831214856128
, 3 , 0.195313368524824
, 4 , 0.144703118168578
, 5 , 0.205965055494892
, 6 , 0.289931784357343
, 7 , 0.17892047945334
, 8 , 0.230305907920915
, 9 , 0.126362581399022
, 10 , 0.0984963168903273
, 11 , 0.132567102081922
, 12 , 0.0933165281402821
, 13 , 0.162534093126959
, 14 , 0.183336140306628
, 15 , 0.173845792789849
, 16 , 0.0904155796279713
, 17 , 0.108065831782867
, 18 , 0.095051087976313
, 19 , 0.104002416782639
, 20 , 0.0796793378534771
, 21 , 0.204868344141513
, 22 , 0.206347949407539
, 23 , 0.092119121267682
, 24 , 0.256283822351573
--MAEが0.11を切ったモデルのみ抽出(昇順に並び替え)
, モデルNo. , 水増し画像枚数 , 水平・鉛直方向への移動 , ...
, 20 , 150 , なし , 50回 , 32枚 , 0.0796793378534771
, 16 , 100 , なし , 50回 , 64枚 , 0.0904155796279713
, 23 , 150 , あり , 50回 , 64枚 , 0.092119121267682
, 12 , 100 , なし , 50回 , 32枚 , 0.0933165281402821
, 18 , 150 , なし , 30回 , 32枚 , 0.095051087976313
, 10 , 100 , なし , 30回 , 32枚 , 0.0984963168903273
, 19 , 150 , あり , 50回 , 32枚 , 0.104002416782639
, 17 , 150 , あり , 30回 , 32枚 , 0.108065831782867
上記の結果より学習回数(の上限)は50回, 画像の水増し枚数...
-8/18
CNN+binning5-1.py
CNN+binning7.py
今までは画像の水増しと学習を別々に行っていた(始めにMMLI....
-- CNN+binning5-1.py → .keras ファイルが作成されるよう...
-- CNN+binning7.py → 明日までに27通りの学習モデルを学習...
-8/19
--CNN+binning5-1.py … .keras ファイルの出力完了
--CNN+binning7.py … MAE等結果の確認は24日以降 今日中に回...
---上記のスクリプトは画像を生成する際に元画像(長方形)か...
CNN+binning5-1.py で出力された .keras ファイル(MAEは0.07...
-8/25
20日の朝に学習を開始させたところ学習開始約60時間で強制終...
-8/26
午前中の時点で12パターンの途中まで学習が行われていなかっ...
--学習の流れについて
今現在の学習方法を以下に示す
1, オリジナル画像の読み込み
2, 画像をアスペクト比を維持しながらリサイズ・パディング
3, 回転・明るさ調整・シフトなどを加えた画像を複数枚生成...
4, 水増し済み画像の読み込んでラベル(スコア)を抽出
5, 学習用と検証用にそれぞれ総画像枚数 8:2 に分割
6, EfficientNetV2S / DenseNet121 / MobileNetV3Large の 3...
7, モデル( .keras ファイル)が保存済みなら再利用なけれ...
8, 検証用データの結果をもとにMAE(平均絶対誤差), 混同行...
9, ラベルがついていない画像を読み込んで推論値(回帰スコ...
10, 各画像ごとの推論結果, MAE, データ数, タイムスタンプ...
--補正関数について(再掲)
現在実装している補正関数は以下の通り ペナルティ関数と補...
---1, ペナルティ関数(学習時の損失関数)
@register_keras_serializable()
def custom_penalizing_loss(y_true, y_pred): ...
diff = tf.abs(y_true - y_pred)
return tf.reduce_mean(tf.square(diff) * (1.0 + tf.po...
誤差(diff) = |y_true - y_pred| を二乗しつつ(1 + diff^1....
---2, 補正関数(特徴量で補正)
def adjust_score_by_features(score, rust_ratio, rust_cou...
correction = 0.0 #リセット
# 錆面積割合
if rust_ratio >= 0.60:
correction -= 0.5
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜...
# 錆の数
if rust_count >= 1000:
correction -= 0.2
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜...
# 最大錆サイズ
if max_blob_area >= 15000:
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜...
return score + correction #補正前評点+補正関数
錆割合・錆の数・最大錆サイズに基づいてスコアを ±0.1〜0.5 ...
現在使用しているモデル(CNN+binning7.py)と以前使用していた...
--1, 補正前スコア(回帰スコア)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--2, 補正後スコア
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--3, 評点
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
CNN+binning.pyのVerが上がるごとに学習方法も変化してくるの...
-8/27
昨日の夜に学習が強制終了されていた。どうやら学習時にメモ...
***8月第5週(8/28〜9/3) [#j417c828]
-8/28
また強制終了していた。学習の組み合わせを2パターンにして...
-8/31
CNN+binning7-1.py
スクリプトの簡略化と一度に複数回学習を行えるようにした機...
--学習結果
エポック数を1にして回してみたところ終わったので構造に問題...
-9/2
現在全64パターンを学習中 画面が固まるようなことなく無事...
--混同行列…何度も出てきているので説明は割愛 対角線上に数...
--Macro F1…再現率と適合率の調和平均 0.6以上あればモデル...
--単純精度…全体で「正しいクラス(評点1〜5)」に分類できた...
**9月 [#j417c900]
***9月第1週(9/4〜9/10) [#j417c904]
-9/4
39パターン目の学習が終了した時点で強制終了となっていた。...
-9/5
中間発表に向けて準備を進めているが現地調査の結果次第で書...
・各CNNモデルごと(単体)で学習させた結果とアンサンブル学...
・概要の見直し
・概要を書き上げた部分までに該当するスライド作成
-9/7
1枚あたりの生成枚数を200枚固定, 画像の加工処理範囲を変化...
, 明るさ(通常は1.0以下だと暗、1.0以上だと明) , 縦の移動...
, (0.8、1.2) , (-0.05、0.05) , (-0.05、0.05) , 0.123 , 0....
, (0.8、1.2) , (-0.05、0.05) , (-0.1、0.1) , 0.124 , 0.82...
, (0.8、1.2) , (-0.1、0.1) , (-0.05、0.05) , 0.131 , 0.81...
, (0.8、1.2) , (-0.1、0.1) , (-0.1、0.1) , 0.128 , 0.815 ...
, (0.7、1.3) , (-0.05、0.05) , (-0.05、0.05) , 0.134 , 0....
, (0.7、1.3) , (-0.05、0.05) , (-0.1、0.1) , 0.133 , 0.81...
, (0.7、1.3) , (-0.1、0.1) , (-0.05、0.05) , 0.126 , 0.81...
, (0.7、1.3) , (-0.1、0.1) , (-0.1、0.1) , 0.134 , 0.817 ...
結果として水増しする枚数を多くしたりパラメータをいじった...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
各CNNモデル単体とアンサンブル学習の結果比較は9日の午後時...
-9/8
CNNの単一モデルとアンサンブル学習の結果を比較したところ以...
, Model , Accuracy , MAE
, EfficientNetV2S , 0.807 , 0.211
, DenseNet121 , 0.864 , 0.112
, MobileNetV3Large , 0.858 , 0.138
, Ensemble , 0.849 , 0.124
どのモデルも精度には申し分ないのだが、アンサンブルの結果...
--どうやらモデル間の相関が高すぎるとアンサンブルの結果が...
-9/9
--昨日の現地調査で撮影した写真を橋梁ごとにフォルダを作成...
"DenseNet121", "InceptionResNetV2", "Xception" の3モデル...
, Model , Accuracy , MAE
, DenseNet121 , 0.863 , 0.109
, InceptionResNetV2 , 0.862 , 0.109
, Xception , 0.857 , 0.110
, Ensemble , 0.861 , 0.103
***9月第2, 3週(9/11〜9/24) [#j417c911]
-9/11
水増し枚数を50, 100, 150, 200枚にして学習させた結果(混同...
--混同行列(左:50枚 右:100枚)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--混同行列(左:150枚 右:200枚)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
新CNNモデルの組み合わせでそれぞれ学習, アンサンブルを行っ...
--混同行列(左:DenseNet121 右:InceptionResNetV2)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--混同行列(左:Xception 右:アンサンブル)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-9/17
画像の水増し枚数は50枚, 水増しの際に画像にする加工処理は...
-9/18
CNN+binning7-2.py
CNN+binning7-1.py に 誤差解析ツール(MAE, Accuracy, Macro...
-9/22
新旧の両モデルを学習させて結果の比較を行っている 新モデ...
-9/24
今月上旬に行った現地調査で採取した錆付きテープをスキャナ...
***9月第4週(9/25〜10/1) [#j417c925]
調査で採取した錆画像を取り入れたことでラベル付き画像が既...
-元画像→水増し1回→学習
, 精度指標 , 水増し500枚 , 水増し1000枚 , 水増し1500枚 , ...
, MAE(Ensemble) , 2.576 , 2.364 , 1.931 , 3.012 , 3.15
, 正答率 , 0.304 , 0.261 , 0.217 , 0.261 , 0.217
, F1スコア , 0.093 , 0.083 , 0.071 , 0.083 , 0.071
, 各評点ごとのMAE , 水増し500枚 , 水増し1000枚 , 水増し15...
, 評点1 , 1.573 , 1.183 , 0.647 , 1.364 , 1.594
, 評点2 , 2.352 , 2.111 , 1.683 , 2.496 , 2.589
, 評点3 , 2.516 , 2.456 , 1.985 , 3.244 , 3.399
, 評点4 , 3.302 , 3.211 , 2.691 , 4.090 , 4.057
, 評点5 , 4.003 , 3.531 , 3.263 , 4.658 , 4.537
元画像114枚から水増しを1回だけ行った結果、以前に言った通...
**10月 [#j417c1000]
***10月第1, 2週(10/2〜10/15) [#j417c1002]
-10/2
学習が強制終了していたためもう一度回しているが、無事学習...
-10/9
中間発表が終わってやること
--現地調査報告書作成
--県管理の橋梁で調査できそうな耐候性鋼橋の選定
--中間発表で用いた機械学習の中身
--耐候性鋼橋のページに写真を貼り付ける
-10/13
--機械学習の中身についての説明を明日
-10/14
--中間発表で使用したモデル
import os ...
import cv2 ...
import shutil ...
import numpy as np ...
import seaborn as sns ...
import tensorflow as tf ...
from PIL import Image ...
from tensorflow.keras import layers, models, Input, Mode...
from tensorflow.keras.models import load_model ...
from tensorflow.keras.layers import Layer ...
from tensorflow.keras.callbacks import EarlyStopping, Mo...
from sklearn.model_selection import train_test_split ...
from sklearn.metrics import mean_squared_error, confusio...
from tensorflow.keras.applications import DenseNet121, I...
import matplotlib.pyplot as plt ...
import pandas as pd ...
import psutil ...
import datetime ...
import gc ...
# === 基本設定 ===
NUM_MODELS = 3 ...
TRAIN_FOLDER = "train_images" ...
TEST_FOLDER = "Fuji2" ...
BASE_RESULTS_DIR = "results_patterns1-2" ...
IMAGE_SIZE = (256, 256) ...
USE_COLOR = False ...
MODEL_PATHS = [f"ensemble_model_{i+1}.keras" for i in ra...
# === フォルダの削除・作成 ===
if os.path.exists(BASE_RESULTS_DIR): ...
shutil.rmtree(BASE_RESULTS_DIR) ...
print(f"既存のフォルダ '{BASE_RESULTS_DIR}' を削除し...
os.makedirs(BASE_RESULTS_DIR, exist_ok=True) ...
print(f"新しいフォルダ '{BASE_RESULTS_DIR}' を作成しまし...
# === 学習パターンの定義 ===
patterns = [
{'name': 'pattern_1', 'epochs': 50, 'batch_size': 32...
{'name': 'pattern_2', ...
]
#ここでパターンごとに各パラメータを設定する(name:学習...
# === 学習画像とスコアの読み込み関数 ===
def load_images_and_scores(folder):
images, scores = [], [] ...
for fname in sorted(os.listdir(folder)): ...
if fname.endswith(".png") or fname.endswith(".jpg...
try:
score = float(fname[0]) ...
score += np.random.uniform(-0.2, 0.2) ...
if score < 0.00 or score > 5.50: ...
print(f"⚠️ スコア範囲外: {fname} → {s...
continue
path = os.path.join(folder, fname) ...
img = Image.open(path).convert("RGB").res...
images.append(np.array(img) / 255.0) ...
scores.append(score) ...
except Exception as e: ...
print(f"⚠️ 読み込み失敗: {fname}, 理由: {...
return np.array(images), np.array(scores) ...
# === カスタム損失関数 ===
@register_keras_serializable()
def custom_penalizing_loss(y_true, y_pred): ...
diff = tf.abs(y_true - y_pred) ...
return tf.reduce_mean(tf.square(diff) * (1.0 + tf.po...
...
# === CNNモデル構築関数 ===
def create_model(version):
input_img = Input(shape=(*IMAGE_SIZE, 3)) ...
# CNNモデルごとに異なるベースアーキテクチャを使用
if version == 0:
base = DenseNet121(include_top=False, weights="i...
elif version == 1:
base = InceptionResNetV2(include_top=False, weig...
elif version == 2:
base = Xception(include_top=False, weights="imag...
...
base.trainable = False ...
# グローバルプーリングで特徴マップを1次元に圧縮
x = base.output
x = layers.GlobalAveragePooling2D()(x)
# 全結合層(Dense)を追加して非線形特徴を学習
for units, dr in [(2048, 0.5), (1024, 0.4), (512, 0....
x = layers.Dense(units, activation="relu")(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(dr)(x)
output = layers.Dense(1)(x)
#-----------------------展開すると-----------------------↓
x = layers.Dense(2048, activation='relu')(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(0.5)(x) # 過学習防止
x = layers.Dense(1024, activation='relu')(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(0.4)(x)
x = layers.Dense(512, activation='relu')(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(0.3)(x)
x = layers.Dense(256, activation='relu')(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(0.2)(x)
x = layers.Dense(128, activation='relu')(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(0.15)(x)
x = layers.Dense(64, activation='relu')(x)
x = layers.Dropout(0.1)(x)
output = layers.Dense(1)(x) # 最終出力は1次元の連続...
#-------------------------------------------------------...
model = Model(inputs=input_img, outputs=output) ...
model.compile(optimizer='adam', loss=custom_penalizin...
return model ...
# === 錆特徴量抽出関数 ===
def extract_features(image, use_color=False): ...
gray = np.array(image.convert("L")) ...
if use_color: #錆画像をカラーで学習させた場合
hsv = cv2.cvtColor(np.array(image), cv2.COLOR_RG...
lower_rust = np.array([5, 50, 50]) ...
upper_rust = np.array([25, 255, 255]) ...
mask = cv2.inRange(hsv, lower_rust, upper_rust) ...
else: #錆画像を白黒で学習させた場合
_, mask = cv2.threshold(gray, 128, 255, cv2.THRE...
rust_ratio = np.sum(mask > 0) / mask.size ...
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERN...
rust_count = len(contours) # 錆の数
max_blob_area = max([cv2.contourArea(c) for c in con...
return rust_ratio, rust_count, max_blob_area ...
# === CNNの回帰スコアに基づく補正関数 ===(今回は未使用)
def adjust_score_by_features(score, rust_ratio, rust_cou...
correction = 0.0 ...
if score >= 4.5:
return score ...
# 錆面積割合に基づく補正
if rust_ratio >= 0.60: ...
correction -= 0.5 ...
elif 0.45 <= rust_ratio < 0.60:
correction -= 0.3
elif 0.25 <= rust_ratio < 0.45:
correction -= 0.1
elif rust_ratio < 0.05:
correction += 0.3 ...
# 錆の個数による補正
if rust_count >= 1000: ...
correction -= 0.2 ...
elif 500 <= rust_count < 1000:
correction -= 0.1
elif rust_count < 100:
correction += 0.1
# 最大錆サイズによる補正
if max_blob_area >= 15000: ...
correction -= 0.4 ...
elif 8000 <= max_blob_area < 15000:
correction -= 0.2
elif max_blob_area < 100:
correction += 0.2
return score + correction ...
# === スコアを1〜5の離散値に変換 ===(binning関数の設定)
def binning(score):
if score < 1.9: return 1
elif score < 2.6: return 2
elif score < 3.3: return 3
elif score < 4.0: return 4
else: return 5 ...
...
# === 実験を実行するメイン関数 ===
def run_experiment(pattern, run_id): ...
print(f"\n===== 実験 {run_id}: {pattern['name']} 開...
# 出力ディレクトリを作成
output_dir = os.path.join(BASE_RESULTS_DIR, pattern[...
if os.path.exists(output_dir):
shutil.rmtree(output_dir)
os.makedirs(output_dir, exist_ok=True)
for i in range(1, 6):
os.makedirs(os.path.join(output_dir, f"score{i}"...
# 学習データの読み込み
x, y = load_images_and_scores(TRAIN_FOLDER)
total_labeled = len(y) ...
x_train, x_val, y_train, y_val = train_test_split(x,...
# === モデルの学習 or 読み込み ===
models_ensemble = [] ...
for i in range(NUM_MODELS):
model_path = os.path.join(output_dir, f"ensemble...
if os.path.exists(model_path): ...
model = tf.keras.models.load_model(model_pat...
else:# 学習済みモデルが無ければ新規作成
model = create_model(i) ...
callbacks = [
EarlyStopping(patience=5, restore_best_w...
ModelCheckpoint(model_path, save_best_on...
]
model.fit( ...
x_train, y_train,
epochs=pattern["epochs"],
batch_size=pattern["batch_size"],
validation_data=(x_val, y_val),
callbacks=callbacks,
verbose=2
)
model.save(model_path)
models_ensemble.append(model) ...
# === 評価対象(テスト画像)を処理 ===
test_fnames = sorted([f for f in os.listdir(TEST_FOL...
results = [] ...
for fname in test_fnames:
path = os.path.join(TEST_FOLDER, fname)
img = Image.open(path).convert("RGB").resize(IMA...
x_input = np.array(img) / 255.0
x_input = x_input.reshape(1, *IMAGE_SIZE, 3)
# 各モデルの予測を平均化(=アンサンブル)
preds = [m.predict(x_input, verbose=0)[0][0] for...
avg_score = np.mean(preds)
# 錆特徴量で補正
rust_ratio, rust_count, max_blob_area = extract_...
adjusted_score = adjust_score_by_features(avg_sc...
# 範囲外スコアはスキップ
if adjusted_score < 0.00 or adjusted_score > 5.50:
print(f"⚠️ スコア範囲外: {fname} → {adjusted...
continue
final_score = binning(adjusted_score) ...
save_path = os.path.join(output_dir, f"score{fin...
img.save(save_path) ...
# 結果を記録(CSV出力用)
results.append({
"ファイル名": fname,
"回帰スコア": round(avg_score, 3),
"補正後スコア": round(adjusted_score, 3),
"評点": final_score,
"錆割合": round(rust_ratio, 3),
"錆の数": rust_count,
"錆の最大サイズ": round(max_blob_area, 1),
})
# === 予測結果CSVを保存 ===
pd.DataFrame(results).to_csv(os.path.join(output_dir...
...
# === 評価指標の算出 ===
val_preds = [m.predict(x_val, verbose=0).flatten() fo...
y_val_pred = np.mean(val_preds, axis=0) ...
# 予測スコアを1〜5のクラスラベルに変換(分類評価用)
y_val_bin = np.array([binning(s) for s in y_val_pred]...
y_true_bin = np.array([binning(s) for s in y_val])
# === MAE(平均絶対誤差) ===
mae_score = mean_absolute_error(y_val, y_val_pred)
print(f"MAE:{mae_score}") ...
# === Accuracy(単純精度) ===
from sklearn.metrics import accuracy_score, f1_score ...
acc_score = accuracy_score(y_true_bin, y_val_bin) ...
# === Macro平均F1スコア ===
macro_f1 = f1_score(y_true_bin, y_val_bin, average="m...
# === 混同行列の保存(どのクラスを誤分類したか可視化...
pattern_dir = os.path.join("results_patterns", patter...
os.makedirs(pattern_dir, exist_ok=True) ...
cm = confusion_matrix(y_true_bin, y_val_bin, labels=[...
plt.figure(figsize=(10.8, 7.2)) ...
disp = ConfusionMatrixDisplay(confusion_matrix=cm, di...
disp.plot(cmap="Blues", values_format="d") ...
plt.title("Confusion Matrix (Validation Set)") ...
plt.savefig(os.path.join(output_dir, "confusion_matri...
plt.close()
# === UMAP可視化 ===
feature_model = Model(inputs=models_ensemble[0].input...
features_val = feature_model.predict(x_val, verbose=0...
...
umap = UMAP(n_neighbors=15, min_dist=0.1, random_stat...
embedding = umap.fit_transform(features_val) ...
plt.figure(figsize=(10.8, 7.2))
scatter = plt.scatter(embedding[:, 0], embedding[:, 1...
plt.colorbar(scatter, ticks=[1, 2, 3, 4, 5]) ...
plt.title("UMAP Visualization of Validation Features"...
plt.savefig(os.path.join(output_dir, "umap_val.png"))...
plt.close()
# === メモリ使用量の可視化(強制終了の原因調査用) ===
vm = psutil.virtual_memory() ...
total_gb = vm.total / (1024 ** 3) ...
used_gb = (vm.total - vm.available) / (1024 ** 3) ...
percent = vm.percent ...
print(f"💾 System Memory usage after {pattern['name']...
...
# === クラス別MAEと±1精度 ===
def per_class_mae(y_true, y_pred, binning_fn):
y_true_bin = np.array([binning_fn(v) for v in y_t...
y_pred_bin = np.array([binning_fn(v) for v in y_p...
res = {}
for c in np.unique(y_true_bin):
mask = (y_true_bin == c)
if mask.sum() > 0:
res[int(c)] = mean_absolute_error(y_true[...
return res
def within_one_rate(y_true, y_pred, binning_fn):
y_t = np.array([binning_fn(v) for v in y_true])
y_p = np.array([binning_fn(v) for v in y_pred])
return (np.abs(y_t - y_p) <= 1).mean() ...
...
class_mae = per_class_mae(y_val, y_val_pred, binning)
one_acc = within_one_rate(y_val, y_val_pred, binning)
print("クラス別MAE:", class_mae)
print("±1精度:", one_acc)
...
# === 残差プロットと誤差分布 ===
res = y_val_pred - y_val ...
plt.scatter(y_val, y_val_pred, s=6, alpha=0.6)
plt.plot([0, 5], [0, 5], 'r--') ...
plt.xlabel("True"); plt.ylabel("Predicted")
plt.savefig(os.path.join(output_dir, "true_vs_pred.pn...
plt.close()
plt.hist(res, bins=40, color="gray")
plt.title("Residuals")
plt.savefig(os.path.join(output_dir, "residuals.png"))
plt.close()
...
# === モデル間誤差の相関(アンサンブル効果を定量化) ...
if len(val_preds) >= 2:
from scipy.stats import pearsonr ...
errors = [np.abs(y_val - p) for p in val_preds]
corr = np.zeros((len(errors), len(errors))) ...
for i in range(len(errors)):
for j in range(len(errors)):
corr[i, j], _ = pearsonr(errors[i], error...
pd.DataFrame(corr).to_csv(os.path.join(output_dir...
print("誤差相関行列を保存しました")
...
# === モデル・変数を明示的に解放 ===
del models_ensemble ...
del x_train, x_val, y_train, y_val ...
tf.keras.backend.clear_session() ...
gc.collect() ...
print(f"===== 実験 {run_id}: {pattern['name']} 完了 =...
# === モデル・データを完全解放 ===
del models_ensemble
del x_train, x_val, y_train, y_val
tf.keras.backend.clear_session()
gc.collect()
print(f"===== 実験 {run_id}: {pattern['name']} 完了 =...
...
# === 実験条件と評価結果の記録 ===
summary = {
"name": pattern["name"], ...
"epochs": pattern["epochs"], ...
"batch_size": pattern["batch_size"], ...
"num_aug": pattern["num_aug"], ...
"rotation": pattern["rotation"], ...
"brightness": str(pattern["brightness"]), ...
"width_shift": str(pattern["width_shift"]), ...
"height_shift": str(pattern["height_shift"]), ...
"total_train_samples": total_labeled * pattern["n...
"mae": mae_score, ...
"accuracy": acc_score, ...
"macro_f1": macro_f1, ...
"class_mae": str(class_mae), ...
"within_one_acc": one_acc, ...
"datetime": str(datetime.datetime.now()) ...
}
pd.Series(summary).to_csv(os.path.join(output_dir, "s...
return summary ...
# === メイン処理 ===
if __name__ == "__main__":
all_results = [] ...
for idx, pat in enumerate(patterns): ...
summary = run_experiment(pat, idx)
all_results.append(summary)
# === 全実験の集計結果をCSVで保存 ===
df_all = pd.DataFrame(all_results)
df_all.to_csv(os.path.join(BASE_RESULTS_DIR, "all_re...
print(f"✅ 全パターンの集計結果を {os.path.join(BASE_R...
--上記のスクリプトを実行すると以下のファイルが保存される
・results_patterns1-2 # 各学習パターンの結果をまとめ...
|
|__ pattern_1 # 1つの学習パターン(=特定の学...
| |
| |__ ensemble_model_1.keras, ensemble...
| | ...
| | ...
| | ...
| |
| |_ confusion_matrix.png ...
| | ...
| | ...
| |
| |_ error_corr.csv ...
| | ...
| |
| |_ residuals.png ...
| | ...
| | ...
| |_ rust_score_with_features.csv ...
| |
| |_ summary.csv ...
| |
| |_ true_vs_pred.png ...
| | ...
| |
| |_ umap_val.png ...
| ...
| ...
|
|_ pattern_2___ ensemble_model_1.keras, ensemble...
| |_ confusion_matrix.png
| |_ ...
|_ ...
-10/15
ラベル付き画像全114枚から1枚あたり50枚水増ししたのち再度...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--MAEに関して250枚までは改善傾向が見られたが、500枚前後で...
続いて正答率は50枚で約0.893と高く、150〜1000枚あたりでや...
F1スコアについて同様に50枚〜2000枚付近で0.83〜0.87の範囲...
以上より最も良いバランスはMAE=0.074、正答率=0.895、F1=...
--評点別MAEの変化
--⇒評点1・2といった重度錆は全体的に誤差が小さく、0.06〜0....
--⇒評点3の中程度の錆は最もデータが多い領域で誤差が最少 ...
--⇒評点4・5の軽度錆はやや誤差が大きい → 他クラスよりも...
---水増しは1枚あたり500〜2000枚が理想
---3000枚を超えると、類似画像の増加や過学習の影響でわずか...
---2回水増しで水増し1回よりも少ない枚数で良好な結果が得ら...
***10月第3週(10/16〜10/22) [#j417c1016]
-10/16
--.keras ファイルを用いて錆画像の判別を行ってみた。学習元...
・補正値 ...
def adjust_score_by_features(score, rust_ratio, rust_cou...
correction = 0.0 ...
...
if score >= 4.5: ...
# 高スコアはそのまま返す(補正・clipなし) ...
return score ...
...
if rust_ratio >= 0.60: ...
correction -= 0.5 ...
elif 0.45 <= rust_ratio < 0.60: ...
correction -= 0.3 ...
elif 0.25 <= rust_ratio < 0.45: ...
correction -= 0.1 ...
elif rust_ratio < 0.05: ...
correction += 0.3 ...
...
if rust_count >= 1000: ...
correction -= 0.2 ...
elif 500 <= rust_count < 1000: ...
correction -= 0.1 ...
elif rust_count < 100: ...
correction += 0.1 ...
...
if max_blob_area >= 15000: ...
correction -= 0.4 ...
elif 8000 <= max_blob_area < 15000: ...
correction -= 0.2 ...
elif max_blob_area < 100: ...
correction += 0.2 ...
...
return score + correction ...
--画像の名前について
--→頭の数字が評点になっている(例:1-111 ⇒ 評点1, 2-201 ⇒...
--使用画像(カラー 左から評点1, 2, ... 5の順で並んでいる)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--使用画像(モノクロ 左から評点1, 2, ... 5の順で並んでい...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--判別結果
, 画像 , 補正前評点 , 補正後評点 , 回帰スコア , 補正後ス...
, 1-01.png , 3 , 2 , 3.102 , 2.302 , 0.719 , 33 , 87309.0
, 2-01.png , 3 , 2 , 3.151 , 2.451 , 0.379 , 1480 , 20794...
, 3-01.png , 3 , 2 , 3.196 , 2.396 , 0.817 , 12 , 88528.0
, 4-01.png , 4 , 3 , 3.455 , 2.655 , 0.914 , 1 , 89187.5
, 5-01.png , 3 , 2 , 3.105 , 2.305 , 0.953 , 2 , 89268.5
, 1-11.jpg , 1 , 1 , 0.967 , 0.467 , 0.359 , 359 , 19832.0
, 2-11.jpg , 3 , 3 , 2.762 , 2.662 , 0.274 , 451 , 3808.5
, 2-12.jpg , 3 , 3 , 2.882 , 2.682 , 0.266 , 722 , 4102.0
, 3-11.jpg , 4 , 4 , 3.575 , 3.375 , 0.163 , 1348 , 527.5
, 4-11.jpg , 5 , 5 , 4.933 , 4.933 , 0.042 , 707 , 236.5
, 5-11.jpg , 5 , 5 , 4.936 , 4.936 , 0.005 , 178 , 153.5
補正関数を組み込んでも評点が変化することはあまりないこと...
-10/17
現地調査で撮影したカラーの錆画像を台形補正するスクリプト...
[[台形補正で参考にしたページ:https://qiita.com/otofu_dayo...
-10/20
台形変換のスクリプトがとりあえず完成したので貼り付けてお...
◯指定フォルダ内の画像に対して「白いプレート枠を検出して...
1,画像読み込み
2,グレースケール変換と二値化
3,ノイズ除去(モルフォロジー演算+ラベリング)
4,最大矩形領域の抽出
5,結果画像の保存
--------------------------------------------------------...
import cv2
import numpy as np
import os
import shutil
from glob import glob
# === 設定 ===
INPUT_DIR = "drone_images" # ★入力画像フォルダ
OUTPUT_DIR = "processed_patches3" # ★出力フォルダ
REF_WIDTH = 1000 # ★出力画像の横サイズ
REF_HEIGHT = 750 # ★出力画像の縦サイズ
os.makedirs(OUTPUT_DIR, exist_ok=True)
# === フォルダーの削除・作成 ===
folder_name = OUTPUT_DIR
if os.path.exists(folder_name):
shutil.rmtree(folder_name)
print(f"既存のフォルダ '{folder_name}' を削除しまし...
os.makedirs(folder_name, exist_ok=True)
print(f"新しいフォルダ '{folder_name}' を作成しました。")
# === 1. プレート領域のマスク処理 ===
def mask_plate_region(img, threshold_mode="otsu"): ...
"""
撮影画像から白いプレート枠領域を抽出してマスク化する。
"""
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ...
...
blur = cv2.GaussianBlur(gray, (5, 5), 0) ...
...
if threshold_mode == "otsu":
_, binary = cv2.threshold(blur, 0, 255, cv2.THRE...
elif threshold_mode == "adaptive":
binary = cv2.adaptiveThreshold(blur, 255,
cv2.ADAPTIVE_THRE...
cv2.THRESH_BINARY...
else:
raise ValueError("threshold_mode must be 'otsu' ...
# 白黒反転(白枠が白くなるように)
if np.mean(binary) < 127:
binary = cv2.bitwise_not(binary)
return binary ...
# === 2. ノイズ除去処理 ===
def remove_noise(binary_mask):
"""
二値化画像から小さなノイズを除去し、白枠を明確化する。
"""
kernel = np.ones((5, 5), np.uint8) ...
opened = cv2.morphologyEx(binary_mask, cv2.MORPH_OPE...
closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, k...
# ラベリング処理で各領域を集計し、面積500以上の領域...
num_labels, labels, stats, _ = cv2.connectedComponen...
cleaned = np.zeros_like(closed)
for i in range(1, num_labels): ...
area = stats[i, cv2.CC_STAT_AREA]
if area > 500:
cleaned[labels == i] = 255 ...
return cleaned ...
# === 3. 輪郭の取得と近似化 ===
def detect_plate_contour(img, cleaned_mask):
"""
白枠プレートの輪郭を検出し、最も大きな矩形を返す。
"""
contours, _ = cv2.findContours(cleaned_mask, cv2.RET...
if not contours:
print("⚠️ 輪郭が検出されませんでした。")
return img, None
contours = sorted(contours, key=cv2.contourArea, rev...
# 最も大きい輪郭を選択
largest_contour = contours[0]
epsilon = 0.02 * cv2.arcLength(largest_contour, True...
approx = cv2.approxPolyDP(largest_contour, epsilon, ...
# 4頂点に近い輪郭のみ採用し、緑枠で描画し角座標を出...
contour_img = img.copy()
if len(approx) == 4:
cv2.drawContours(contour_img, [approx], -1, (0, ...
pts = approx.reshape(4, 2)
print(f"🟩 四角形の頂点座標: {pts}")
return contour_img, pts
else:
print(f"⚠️ 4頂点ではありません({len(approx)}点...
cv2.drawContours(contour_img, [largest_contour],...
return contour_img, None ...
# === 4. メイン処理 ===
for fname in os.listdir(INPUT_DIR):
if not fname.lower().endswith((".jpg", ".jpeg", ".pn...
continue ...
img_path = os.path.join(INPUT_DIR, fname)
img = cv2.imread(img_path) ...
if img is None:
print(f"⚠️ 読み込み失敗: {fname}")
continue
mask = mask_plate_region(img, threshold_mode="otsu")...
cleaned = remove_noise(mask) ...
contour_img, pts = detect_plate_contour(img, cleaned...
# --- 保存 ---
save_path = os.path.join(OUTPUT_DIR, f"contour_{fnam...
cv2.imwrite(save_path, contour_img)
print(f"✅ 輪郭検出結果を保存しました: {save_path}")
-10/21
--昨日のスクリプトで輪郭の取得と近似化に修正を加えた
# === 3. 輪郭の取得と近似化 ===
def detect_plate_contour(img, cleaned_mask):
"""
白枠プレートの輪郭を検出し、内側スケール矩形のみを抽...
外枠(画像外周)は無視。
条件:
- 面積が 20000〜1450×1060 の範囲
- アスペクト比が 1.0〜a の範囲
- 図形中心が画像の外周b%以内でない
- 各角の角度が c°〜d° の範囲
"""
h, w = img.shape[:2]
max_allowed_area = 1450 * 1060
min_allowed_area = 20000
# --- ① 外枠除外マスク ---
inner_mask = np.zeros_like(cleaned_mask)
margin = int(min(h, w) * 0.12) # 外周12%を無視
inner_mask[margin:h - margin, margin:w - margin] = 255
masked = cv2.bitwise_and(cleaned_mask, inner_mask)
# --- ② 輪郭検出 ---
contours, _ = cv2.findContours(masked, cv2.RETR_EXTE...
if not contours:
print("⚠️ 輪郭が検出されませんでした。")
return img, None
valid_candidates = []
for cnt in contours:
area = cv2.contourArea(cnt)
if area < min_allowed_area or area > max_allowed...
continue
rect = cv2.minAreaRect(cnt)
(cx, cy), (rw, rh), angle = rect
# --- ③ 中心位置チェック(外周に寄りすぎていない...
if cx < margin or cx > (w - margin) or cy < marg...
continue
# --- ④ アスペクト比チェック ---
aspect_ratio = max(rw, rh) / (min(rw, rh) + 1e-6)
if not (1.0 <= aspect_ratio <= 2.5):
continue
# --- ⑤ 近似と角度判定 ---
epsilon = 0.02 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
if len(approx) != 4:
continue
pts = approx.reshape(4, 2)
angles = []
for i in range(4):
p1, p2, p3 = pts[i], pts[(i + 1) % 4], pts[(...
v1, v2 = p1 - p2, p3 - p2
cos_angle = np.dot(v1, v2) / (np.linalg.norm...
angle = np.degrees(np.arccos(np.clip(cos_ang...
angles.append(angle)
if all(69 <= a <= 111 for a in angles):
valid_candidates.append((area, pts))
# --- ⑥ 最も適した候補を選択 ---
if not valid_candidates:
print("⚠️ 有効な内枠が見つかりませんでした。")
return img, None
best_area, best_pts = max(valid_candidates, key=lamb...
contour_img = img.copy()
cv2.drawContours(contour_img, [best_pts.astype(int)]...
print(f"🟩 内枠矩形を検出(面積={best_area:.0f}, ア...
return contour_img, best_pts
上記に示したスクリプトを Scalehosei3.py と定義し撮影した...
-スクリプトの処理内容について
--1, はじめに現地で撮影した錆画像を用意する。多少画面がぶ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--2, Scalehosei3.py を実行すると最初に画像の明るい部分は...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--3, 次に 2 で出力された画像から内枠の認識に影響を及ぼす...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--4, 3 からカラースケールの内枠を抽出して元の画像に出力&...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-10/22
色補正を行う前段階としてカラースケールのカラーを切り抜く...
--今後の色補正スクリプトの作成目標
---1, 切り抜いた画像を純色(255)に変換する 変換過程を混...
---2, 混同行列をもとに画像全体のRGB値を変換する
---3, 色補正と台形補正をどちらから行うかについては後々考...
import cv2
import numpy as np
import os
import shutil ...
INPUT_DIR = "processed_patches3"
OUTPUT_DIR = "processed_patches4"
os.makedirs(OUTPUT_DIR, exist_ok=True) ...
# === フォルダーの削除・作成 ===
folder_name = OUTPUT_DIR
if os.path.exists(folder_name):
shutil.rmtree(folder_name)
print(f"既存のフォルダ '{folder_name}' を削除しまし...
os.makedirs(folder_name, exist_ok=True)
print(f"新しいフォルダ '{folder_name}' を作成しました。")
def detect_and_extract_color_scale(img): ...
h, w = img.shape[:2]
roi = img[:, int(w * 0.65):] ...
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) ...
color_ranges = {
'red1': ((0, 100, 100), (10, 255, 255)),
'red2': ((160, 100, 100), (180, 255, 255)),
'yellow': ((20, 100, 100), (40, 255, 255)),
'green': ((40, 50, 50), (90, 255, 255)), ...
'blue': ((90, 50, 50), (150, 255, 255)), ...
}
mask_red = cv2.bitwise_or(cv2.inRange(hsv, *color_ra...
mask_yellow = cv2.inRange(hsv, *color_ranges['yellow...
mask_green = cv2.inRange(hsv, *color_ranges['green']...
mask_blue = cv2.inRange(hsv, *color_ranges['blue']) ...
combined_mask = cv2.bitwise_or(cv2.bitwise_or(mask_r...
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (...
combined_mask = cv2.morphologyEx(combined_mask, cv2....
contours, _ = cv2.findContours(combined_mask, cv2.RE...
if not contours:
print("カラースケール領域検出なし")
return img, None
candidates = []
for cnt in contours:
rect = cv2.minAreaRect(cnt)
(cx, cy), (width, height), angle = rect ...
if width == 0 or height == 0:
continue ...
aspect_ratio = max(width, height) / min(width, h...
area = width * height ...
if area > 0.001 * roi.shape[0] * roi.shape[1] an...
box = cv2.boxPoints(rect)
box = np.int0(box) ...
candidates.append((area, box, rect)) ...
if not candidates:
print("適合するカラースケール候補がありません")
return img, None
largest_candidate = max(candidates, key=lambda c: c[...
area, box, rect = largest_candidate ...
box[:,0] += int(w * 0.65) ...
img_with_box = img.copy()
cv2.drawContours(img_with_box, [box], 0, (0,255,0), ...
width = int(rect[1][0])
height = int(rect[1][1])
if width == 0 or height == 0:
print("切り出し用サイズ不正")
return img_with_box, None ...
dst_pts = np.array([[0,0],[width-1,0],[width-1,heigh...
src_pts = box.astype(np.float32) ...
M = cv2.getPerspectiveTransform(src_pts, dst_pts) ...
extracted = cv2.warpPerspective(img, M, (width, heig...
extracted = cv2.resize(extracted, (height, width)) ...
return img_with_box, extracted ...
def process_images(in_dir, out_dir): ...
os.makedirs(out_dir, exist_ok=True)
for fn in os.listdir(in_dir):
if fn.lower().endswith(('.png','.jpg','.jpeg','....
path_in = os.path.join(in_dir, fn)
path_out = os.path.join(out_dir, fn)
path_crop = os.path.join(out_dir, "crop_"+fn...
img = cv2.imread(path_in)
if img is None:
print(f"{fn} 読み込み失敗")
continue
img_boxed, crop_img = detect_and_extract_col...
cv2.imwrite(path_out, img_boxed)
print(f"{fn} に枠を描画") ...
if crop_img is not None:
cv2.imwrite(path_crop, crop_img)
print(f"{fn} の切り出し領域を保存") ...
if __name__ == "__main__": #メイン実行部。
process_images(INPUT_DIR, OUTPUT_DIR) ...
まとめると
1, 右側35%領域を「カラースケール候補」として抽出。
2, HSVで赤・黄・緑・青の色領域を検出。
3, 条件(面積・縦横比)を満たす最も大きな矩形をカラース...
4, 緑枠で描画して切り抜き画像を保存。
このスクリプトを通じて画像を変換させると元画像(入力画像...
-1, 元画像
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-2, 枠付き画像
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-3, スケール切り抜き画像
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
***10月第4週(10/23〜10/29) [#j417c1023]
-10/23
スクリプトの案内
-Scalehosei1.py ... モノクロ変換
-Scalehosei2.py ... モノクロ変換+ノイズ除去
-Scalehosei3.py ... モノクロ変換+ノイズ除去+台形補正前...
-Scalehosei4.py ... 色補正前段階・カラースケール抽出
-Scalehosei5.py ... カラースケール補正→変換行列作成・出力
-Scalehosei6.py ... 変換行列で元画像を色補正
とりあえず色補正を行う所までスクリプトを組むことができた...
-10/26
カラースケールを純色にRGBの3×3の行列で変換した後その行列...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-10/27
色補正スクリプトの方でRGB毎に影響の強弱を付けられるように...
-10/29
色補正後の赤みをある程度抑えられ、先に色補正を行ってから...
--1段目:色補正
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--2段目:錆枠抽出
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--3段目:台形変換
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
**11月 [#j417c1100]
***11月第1週(10/30〜11/05) [#j417c1030]
-10/30
--旧モデル
, モデル名 , MAE , 正答率 , F1スコア
, EfficientNetV2S , 0.3029 , 0.7865, 0.7623
, DenseNet121 , 0.0888 , 0.9912 , 0.9891
, MobileNetV3Large , 0.1656 , 0.9438 , 0.9409
, ensemble , 0.1456 , 0.9859 , 0.9827
--新モデル
, モデル名 , MAE , 正答率 , F1スコア
, DenseNet121 , 0.0841 , 0.9859 , 0.9819
, InceptionResNetV2 , 0.1649 , 0.9675 , 0.9651
, Xception , 0.1215 , 0.9772 , 0.9743
, ensemble , 0.0988 , 0.9938 , 0.9923
--新・旧のDenseNet121
, モデル名 , MAE , 正答率 , F1スコア
, 旧モデルDenseNet121 , 0.0888 , 0.9912 , 0.9891
, 新モデルDenseNet121 , 0.0841 , 0.9859 , 0.9819
同じ DenseNet121 を用いているのに結果が微妙に異なるのは学...
◯主原因1:初期化の乱数
TensorFlow / Keras の重み初期値は乱数で生成されることで同...
-対策: スクリプトの先頭で乱数シードを固定(再現性の確保)
◯主原因2:データ分割のランダム性
train_test_split を使うとrandom_state=42 のように固定しな...
-対策:
x_train, x_val, y_train, y_val = train_test_split(x, y, ...
◯主原因3:アンサンブル全体の重み干渉
アンサンブル処理内でモデル学習順序が異なる, または他のモ...
--来週行う内容
---白黒のラベル付き錆画像(114枚)を学習させた結果をもと...
---カラーとドローンで撮影したものは白黒に変換
---カラーの方は256ピクセル四方に切り抜いた後評点判別にか...
--やること
---錆画像の切り抜き → 白黒で学習にかける
---ドローン画像 → 枠で囲まれた部分が評点◯というようなスク...
-10/31
-- 乱数シード固定(tf.random.set_seedなど)
-- 同じデータセット・分割を使う
-- 同じ前処理(正規化やaugmentation)を維持
-- GPU/CPUなど実行環境も統一
上記の条件下で新・旧モデルのDenseNet121を比較した。
-旧モデル
, モデル名 , MAE , 正答率 , F1スコア
, EfficientNetV2S , 0.332241619945153 , 0.687170474516696...
, DenseNet121 , 0.123156605893157 , 0.806678383128295 , 0...
, MobileNetV3Large , 0.19573938891256 , 0.783831282952548...
, Ensemble , 0.177296200058912 , 0.807557117750439 , 0.77...
-新モデル
, モデル名 , MAE , 正答率 , F1スコア
, InceptionResNetV2 , 0.153177455220403 , 0.7970123022847...
, DenseNet121 , 0.147729756383773 , 0.794376098418278 , 0...
, Xception , 0.142235872050986 , 0.808435852372584 , 0.77...
, Ensemble , 0.12868636939779 , 0.812829525483304 , 0.783...
-DenseNet121
, モデル名 , MAE , 正答率 , F1スコア
, 旧モデルDenseNet121 , 0.123156605893157 , 0.80667838312...
, 新モデルDenseNet121 , 0.147729756383773 , 0.79437609841...
, (新・旧)の差 , 0.024573150490616 , -0.012302284710016...
両学習モデルで乱数シードを固定し学習条件を統一した上で訓...
◯GPU演算の非決定性:CuDNN(NVIDIAのディープラーニングライ...
◯os, numpy, tensorflow のランダム初期化:スクリプトでは s...
◯並列スレッドおよびGPUメモリスケジューリング:マルチスレ...
◯EarlyStoppingとModelCheckpointの影響:各エポック終了時の...
-つまり同一環境・同一コードで学習を行ってもGPU演算の非決...
-11/4
宮城県の橋梁調査データをもらい学習の事前準備を行った
--セロテープ画像 … 1つの画像に何枚も貼ってあったためセロ...
--近接画像 … 以前作成した台形変換・色補正スクリプトで枠の...
***11月第2週(11/6〜11/12) [#j417c1106]
-11/6
色補正・台形変換に関する各スクリプトに変更を加えたので以...
-Scalehosei1.py
import cv2 ...
import numpy as np ...
import os ...
import shutil ...
INPUT_DIR = "drone_images" ...
OUTPUT_DIR = "processed_patches1" ...
os.makedirs(OUTPUT_DIR, exist_ok=True) ...
# === フォルダーの削除・作成 ===
folder_name = OUTPUT_DIR
if os.path.exists(folder_name):
shutil.rmtree(folder_name) ...
print(f"既存のフォルダ '{folder_name}' を削除しまし...
os.makedirs(folder_name, exist_ok=True) ...
print(f"新しいフォルダ '{folder_name}' を作成しました。")
# === 拡張子統一 (.JPG, .JPEG → .jpg) ===
def unify_extensions(folder): ...
for fname in os.listdir(folder): ...
old_path = os.path.join(folder, fname) ...
if not os.path.isfile(old_path): ...
continue
base, ext = os.path.splitext(fname) ...
ext_lower = ext.lower()
if ext_lower in [".jpeg", ".jpg", ".png", ".tif"...
new_path = os.path.join(folder, base + ".jpg...
if new_path != old_path: ...
os.rename(old_path, new_path)
print(f"🔄 拡張子統一: {fname} → {os.pat...
unify_extensions(INPUT_DIR) ...
def detect_and_extract_color_scale(img): ...
h, w = img.shape[:2]
roi = img[:, int(w * 0.65):] ...
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) ...
color_ranges = { ...
'red1': ((0, 120, 80), (10, 255, 255)),
'red2': ((160, 120, 80), (180, 255, 255)),
'yellow': ((20, 150, 150), (35, 255, 255)), ...
'green': ((45, 80, 80), (85, 255, 255)),
'blue': ((90, 80, 80), (130, 255, 255)),
}
mask_red = cv2.bitwise_or(cv2.inRange(hsv, *color_ra...
mask_yellow = cv2.inRange(hsv, *color_ranges['yellow...
mask_green = cv2.inRange(hsv, *color_ranges['green'])
mask_blue = cv2.inRange(hsv, *color_ranges['blue']) ...
combined_mask = cv2.bitwise_or(cv2.bitwise_or(mask_r...
# --- ② ノイズ除去・スムージング ---
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (...
combined_mask = cv2.morphologyEx(combined_mask, cv2....
combined_mask = cv2.morphologyEx(combined_mask, cv2....
contours, _ = cv2.findContours(combined_mask, cv2.RE...
candidates = [] ...
for cnt in contours: ...
rect = cv2.minAreaRect(cnt) ...
(cx, cy), (width, height), angle = rect
if width == 0 or height == 0:
continue
aspect_ratio = max(width, height) / (min(width, ...
area = width * height ...
# --- ③ 縦長・面積条件 ---
if area > 0.005 * roi.shape[0] * roi.shape[1] an...
box = cv2.boxPoints(rect)
box = np.int0(box)
candidates.append((area, box, rect))
# === 自動検出失敗時の手動選択 ===
if not candidates: ...
print("⚠️ 適合するカラースケール候補がありません...
manual_points = [] ...
#以下「右クリックで追加・左クリックで取り消し」というイ...
def mouse_callback(event, x, y, flags, param):
if event == cv2.EVENT_RBUTTONDOWN: ...
manual_points.append((x, y))
cv2.drawMarker(param, (x, y), (0, 255, 0...
cv2.imshow("manual_select", param)
print(f"✅ {len(manual_points)}点目 ({x}...
elif event == cv2.EVENT_LBUTTONDOWN: ...
if manual_points:
manual_points.pop()
print(f"↩️ 直前の点を削除。残り{len(...
temp = img.copy()
for px, py in manual_points:
cv2.drawMarker(temp, (px, py), (...
cv2.imshow("manual_select", temp)
temp = img.copy()
cv2.imshow("manual_select", temp)
cv2.setMouseCallback("manual_select", mouse_call...
print("🖱️ 右クリックで4点選択、左クリックで直前...
while True:
key = cv2.waitKey(0)
if key in [13, 10]: ...
break
cv2.destroyWindow("manual_select")
if len(manual_points) != 4:
print("⚠️ 4点が選択されませんでした。スキッ...
return img, None
src_pts = np.array(manual_points, dtype=np.float...
w_scale = int(np.linalg.norm(src_pts[0] - src_pt...
h_scale = int(np.linalg.norm(src_pts[1] - src_pt...
dst_pts = np.array([[0,0],[w_scale-1,0],[w_scale...
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
extracted = cv2.warpPerspective(img, M, (w_scale...
cv2.destroyAllWindows()
return img, extracted
# --- 自動検出成功時 ---
#最も大きい領域を選び元画像上に枠を描画して射影変換...
area, box, rect = max(candidates, key=lambda c: c[0])
box[:,0] += int(w * 0.65)
img_with_box = img.copy()
cv2.drawContours(img_with_box, [box], 0, (0,255,0), 3)
width = int(rect[1][0])
height = int(rect[1][1])
dst_pts = np.array([[0,0],[width-1,0],[width-1,heigh...
src_pts = box.astype(np.float32)
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
extracted = cv2.warpPerspective(img, M, (width, heig...
extracted = cv2.resize(extracted, (height, width)) ...
return img_with_box, extracted
def process_images(in_dir, out_dir): ...
os.makedirs(out_dir, exist_ok=True)
for fn in os.listdir(in_dir): ...
if fn.lower().endswith(('.png','.jpg','.jpeg','....
path_in = os.path.join(in_dir, fn)
path_out = os.path.join(out_dir, fn)
path_crop = os.path.join(out_dir, "crop_"+fn)
img = cv2.imread(path_in) ...
if img is None:
print(f"{fn} 読み込み失敗")
continue
img_boxed, crop_img = detect_and_extract_col...
cv2.imwrite(path_out, img_boxed) ...
print(f"{fn} に枠を描画")
if crop_img is not None:
cv2.imwrite(path_crop, crop_img) ...
print(f"{fn} の切り出し領域を保存")
if __name__ == "__main__":
process_images(INPUT_DIR, OUTPUT_DIR) ...
このスクリプトは「ドローン画像」フォルダ内の画像ファイル...
-Scalehosei2.py
import cv2
import numpy as np
import os
import shutil
INPUT_DIR = "processed_patches1"
OUTPUT_DIR = "processed_patches2"
MATRIX_DIR = "processed_patches2"
os.makedirs(OUTPUT_DIR, exist_ok=True)
os.makedirs(MATRIX_DIR, exist_ok=True)
folder_name = OUTPUT_DIR
if os.path.exists(folder_name):
shutil.rmtree(folder_name)
print(f"既存のフォルダ '{folder_name}' を削除しまし...
os.makedirs(folder_name, exist_ok=True)
print(f"新しいフォルダ '{folder_name}' を作成しました。")
# 純色BGR値(OpenCVはBGR順)
pure_colors = [ #画像上の標準「ターゲット純色」BGR値
(0, 0, 255), # 赤
(0, 255, 255), # 黄
(0, 255, 0), # 緑
(255, 0, 0) # 青
]
def create_color_conversion_matrix(img): ...
h, w = img.shape[:2] ...
conversion_matrix = []
if h > w:
# 縦長:縦方向に4分割(色ごとの帯が縦に配置され...
segment_h = h // 4
for i in range(4):
region = img[i*segment_h:(i+1)*segment_h if ...
avg_bgr = region.mean(axis=(0,1)) ...
conversion_matrix.append({
'segment': i,
'avg_bgr': avg_bgr,
'target_color': pure_colors[i]
})
else:
# 横長:横方向に4分割
segment_w = w // 4
# 横長は左から [青, 緑, 黄, 赤] のため色配列を逆...
reversed_colors = pure_colors[::-1]
for i in range(4):
region = img[:, i*segment_w:(i+1)*segment_w ...
avg_bgr = region.mean(axis=(0,1))
conversion_matrix.append({
'segment': i,
'avg_bgr': avg_bgr,
'target_color': reversed_colors[i]
})
return conversion_matrix
def save_conversion_matrix(matrix, filename): ...
np.save(filename, matrix)
def apply_pure_colors(img, matrix): ...
h, w = img.shape[:2] ...
result = np.zeros_like(img)
if h > w:
# 縦長:縦方向に4分割
segment_h = h // 4
for entry in matrix:
i = entry['segment']
color = entry['target_color']
start_y = i * segment_h
end_y = (i + 1) * segment_h if i < 3 else h
result[start_y:end_y, :] = color
else:
# 横長:横方向に4分割
segment_w = w // 4
for entry in matrix:
i = entry['segment']
color = entry['target_color']
start_x = i * segment_w
end_x = (i + 1) * segment_w if i < 3 else w
result[:, start_x:end_x] = color
return result
#指定ディレクトリ内の全画像を処理
#ファイルごとに読込→変換行列作成・保存→純色置換画像生成→...
#例外処理も加味
def process_images(input_dir, output_dir, matrix_dir):
for fn in os.listdir(input_dir):
if not fn.lower().endswith(('.png','.jpg','.jpeg...
continue
path_in = os.path.join(input_dir, fn)
img = cv2.imread(path_in)
if img is None:
print(f"{fn} 読み込み失敗")
continue
matrix = create_color_conversion_matrix(img)
# 拡張子なしのファイル名を取得
base_name = os.path.splitext(fn)[0]
# .npy保存時には拡張子なしファイル名にする
save_path = os.path.join(matrix_dir, base_name +...
save_conversion_matrix(matrix, save_path)
pure_img = apply_pure_colors(img, matrix)
path_out = os.path.join(output_dir, fn)
cv2.imwrite(path_out, pure_img)
print(f"{fn} 処理完了:変換行列保存と純色画像保...
if __name__ == "__main__":
process_images(INPUT_DIR, OUTPUT_DIR, MATRIX_DIR) #メ...
このスクリプトは抽出したカラースケール画像の各色領域の平...
-なぜ色補正に「黄色」を加えたか
→多くの画像処理・印刷系カラースケールやカラーチャートで...
・黄色は「赤+緑」成分が強調された複合色であり、RGBチャ...
・実際のカメラ画像やプリンタ特性, スキャナ入力の現場では...
・黄色チャネルの平均値を併せて色補正に参入することで、「...
つまり「赤・緑・青」に加えて「黄」も参照することで現実的...
-Scalehosei3.py
import cv2
import numpy as np
import os
import shutil
# === 設定 ===
SCALE_DIR = "processed_patches2" # カラースケール抽出...
INPUT_DIR = "drone_images" # 元画像フォルダ
OUTPUT_DIR = "processed_patches3" # 補正後出力フォルダ
# 出力フォルダを初期化
if os.path.exists(OUTPUT_DIR):
shutil.rmtree(OUTPUT_DIR)
os.makedirs(OUTPUT_DIR, exist_ok=True)
print(f"🗂️ 出力フォルダを作成: {OUTPUT_DIR}")
# === 理想の純色(RGB順) ===
PURE_RGB = np.array([ # 赤、黄、緑、青、それぞれの理想的...
[255, 0, 0], # 赤
[255, 255, 0], # 黄
[0, 255, 0], # 緑
[0, 0, 255] # 青
], dtype=np.float32)
# === カラースケールの4色平均を取得 ===
#画像が縦長なら上→下に赤→黄→緑→青、横長なら左→右で青→緑→...
#各区画ごとにピクセル平均を計算し、BGR→RGB順に並べてリス...
#横長時は配列の順番を赤→黄→緑→青のRGB順に修正
def extract_scale_colors(scale_img):
"""カラースケール画像から4色の平均色(RGB順)を抽出"""
h, w = scale_img.shape[:2]
vertical = h > w
colors = []
if vertical:
# 縦長: 上から 赤→黄→緑→青
step = h // 4
for i in range(4):
region = scale_img[i*step:(i+1)*step if i<3 ...
avg_bgr = region.mean(axis=(0,1))
avg_rgb = avg_bgr[::-1] # BGR→RGB
colors.append(avg_rgb)
else:
# 横長: 左から 青→緑→黄→赤
step = w // 4
for i in range(4):
region = scale_img[:, i*step:(i+1)*step if i...
avg_bgr = region.mean(axis=(0,1))
avg_rgb = avg_bgr[::-1]
colors.append(avg_rgb)
# 横長は順序を赤→黄→緑→青に直す
colors = [colors[3], colors[2], colors[1], color...
return np.array(colors, dtype=np.float32)
# === 変換行列の計算 ===
#緑パッチの誤差を重視するために重み配列を生成
#観測色から理想色への変換を重み付き最小二乗法(線形)で...
def weighted_least_squares_matrix(observed, pure, green_...
weights = np.ones(4)
weights[2] = green_weight
W = np.diag(weights) # 重み付き最小二乗: (O^T W O...
A = np.linalg.solve(observed.T @ W @ observed, obser...
return A
# === 行列適用 ===
# 多項式補正関数(RGB各チャンネルに2次多項式)
#RGB各チャンネルに対し2次多項式補正(明るさ非線形)を加...
def polynomial_correction(rgb_flat, coeffs):
corrected = np.zeros_like(rgb_flat)
for c in range(3):
a, b, c0 = coeffs[c]
corrected[:, c] = a * (rgb_flat[:, c]**2) + b * ...
return corrected
# 行列適用+多項式+ガンマ補正
def apply_matrix(img, M, rgb_strength=(1.0, 0.7, 1.0), g...
"""
#入力画像をRGB変換→行列線形補正→強度倍率補正(チャン...
#img: 入力画像(BGR)
#M: 3x3変換行列
#rgb_strength: チャンネル毎の乗算強度
#gamma: チャンネル毎のガンマ補正値(べき乗の逆数をか...
#poly_coeffs: 各チャンネルの多項式係数(a,b,c)のリスト
"""
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB).astyp...
h, w = img_rgb.shape[:2]
flat = img_rgb.reshape(-1, 3)
# 線形変換
corrected_flat = np.dot(flat, M)
# 強度補正
for c in range(3):
corrected_flat[:, c] *= rgb_strength[c]
# 多項式補正
corrected_flat = polynomial_correction(corrected_fla...
# ガンマ補正(正規化してべき乗)
for c in range(3):
# 0-255範囲を0-1に変換して補正、戻す
corrected_flat[:, c] = np.power(np.clip(correcte...
corrected_flat = np.clip(corrected_flat, 0, 255).ast...
corrected = corrected_flat.reshape(h, w, 3)
return cv2.cvtColor(corrected, cv2.COLOR_RGB2BGR)
# === 誤差評価関数 ===
#4色の各パッチに対し、色差誤差を計算して合計
#とくに緑のB・R、赤のG・B、黄のR、青のG・Bの項目で誤差を...
def multi_patch_error(observed, pure, M, rgb_strength):
pred = np.dot(observed, M)
for i in range(4):
pred[i, 0] *= rgb_strength[0] # 赤
pred[i, 1] *= rgb_strength[1] # 緑
pred[i, 2] *= rgb_strength[2] # 青
e_red_G = np.abs(pred[0, 1] - 0)
e_red_B = np.abs(pred[0, 2] - 0)
e_yellow_R = np.abs(pred[1,0] - 255)
e_green_R = np.abs(pred[2, 0] - 0)
e_green_B = np.abs(pred[2, 2] - 0)
e_blue_G = np.abs(pred[3, 1] - 0)
e_blue_B = np.abs(pred[3, 2] - 255)
error_total = e_green_B + e_green_R + e_red_G + e_re...
return error_total
best_params = None
best_err = float('inf')
"""
# グリッド探索範囲
#グリッド探索範囲として、緑重み・RGB倍率の候補群を用意。
#最初の元画像1枚のパッチを観測値とし、
#全ての候補パラメータで「変換→誤差評価」
#最良パラメータを採用(loop脱出)。
#採用パラメータを表示。
"""
green_weights = np.arange(5, 81, 1) # "5"から"80...
red_values = np.arange(1.2, 1.3, 0.1) # 1.2固定
green_values = np.arange(1.3, 1.4, 0.1) # 1.3固定
blue_values = np.arange(1.2, 1.3, 0.1) # 1.2固定
# カラースケール画像1枚目だけでパラメータ探索
for fn in os.listdir(INPUT_DIR):
if not fn.lower().endswith((".jpg", ".jpeg", ".png",...
continue
base = os.path.splitext(fn)[0]
scale_path = os.path.join(SCALE_DIR, f"crop_{base}.j...
if not os.path.exists(scale_path):
continue
scale_img = cv2.imread(scale_path)
observed = extract_scale_colors(scale_img)
pure = PURE_RGB.copy()
for gw in green_weights:
for rv in red_values:
for gv in green_values:
for bv in blue_values:
rgb_strength = (rv, gv, bv)
M = weighted_least_squares_matrix(ob...
err = multi_patch_error(observed, pu...
if err < best_err:
best_err = err
best_params = dict(gw=gw, rv=rv,...
break
print(f"⭐ 最良パラメータ: weight={best_params['gw']}, r...
M = best_params["M"]
rgb_strength = best_params["rgb_strength"]
# 画像全体に最適化後の補正を適用
#フォルダ内の全画像を対象にスケールパッチ画像を抽出し補...
#結果を出力フォルダに保存し進捗を通知
for fn in os.listdir(INPUT_DIR):
if not fn.lower().endswith((".jpg", ".jpeg", ".png",...
continue
base = os.path.splitext(fn)[0]
scale_path = os.path.join(SCALE_DIR, f"crop_{base}.j...
img_path = os.path.join(INPUT_DIR, fn)
out_path = os.path.join(OUTPUT_DIR, fn)
if not os.path.exists(scale_path):
print(f"⚠️ カラースケールが見つかりません: {fn} ...
continue
scale_img = cv2.imread(scale_path)
observed = extract_scale_colors(scale_img)
pure = PURE_RGB.copy()
corrected = apply_matrix(cv2.imread(img_path), M, rg...
cv2.imwrite(out_path, corrected)
print(f"✅ 補正完了: {fn} → {out_path}")
print("\n🎯 全画像の色補正が完了しました。")
スクリプト前半で画像からカラースケール領域の抽出と各色の...
重要な補正式として「緑帯の誤差を重視」する重み調整ロジッ...
画像から特定パッチを切り出し平均RGB値を計算し、その値と...
複数の補正パラメータ(行列、強度、ガンマなど)を探索し、...
-Scalehosei4.py
◯プレート領域の白枠マスク・抽出スクリプト
基本設定・フォルダ操作
入出力フォルダ名などの初期設定
既存の出力先は削除し、新たに作り直します
プレート領域のマスク処理
mask_plate_regionで画像をグレースケール化して白黒マスク...
◯ノイズ除去(remove_noise)
細かな領域や切れぎみの線の補完を行った後に小面積のゴミ除...
◯輪郭取得(detect_plate_contour)
画像から白枠(矩形状)の輪郭を検出し、面積・中心・角度・...
基準に適合しない場合はGUIでユーザー4点指定もサポート
import cv2
import numpy as np
import os
import shutil
from glob import glob
# === 設定 ===
INPUT_DIR = "processed_patches3" # 入力画像フォルダ
OUTPUT_DIR = "processed_patches4" # 出力フォルダ
REF_WIDTH = 1000 # 出力画像の横サイズ
REF_HEIGHT = 750 # 出力画像の縦サイズ
os.makedirs(OUTPUT_DIR, exist_ok=True)
# === フォルダーの削除・作成 ===
folder_name = OUTPUT_DIR
if os.path.exists(folder_name):
shutil.rmtree(folder_name)
print(f"既存のフォルダ '{folder_name}' を削除しまし...
os.makedirs(folder_name, exist_ok=True)
print(f"新しいフォルダ '{folder_name}' を作成しました。")
# === 1. プレート領域のマスク処理 ===
def mask_plate_region(img, threshold_mode="adaptive"): #★
"""
撮影画像から白いプレート枠領域を抽出してマスク化する。
"""
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (7, 7), 0) #★
if threshold_mode == "otsu":
_, binary = cv2.threshold(blur, 0, 255, cv2.THRE...
elif threshold_mode == "adaptive":
binary = cv2.adaptiveThreshold(blur, 255,
cv2.ADAPTIVE_THRE...
cv2.THRESH_BINARY...
else:
raise ValueError("threshold_mode must be 'otsu' ...
# 白黒反転(白枠が白くなるように)
if np.mean(binary) < 127:
binary = cv2.bitwise_not(binary)
return binary
# === 2. ノイズ除去処理 ===
def remove_noise(binary_mask):
"""
二値化画像から小さなノイズを除去し、白枠を明確化する。
"""
kernel = np.ones((4, 4), np.uint8) #★
# Opening:白ノイズ除去
opened = cv2.morphologyEx(binary_mask, cv2.MORPH_OPE...
# Closing:枠線の途切れ補完
closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, k...
# Connected Componentで小領域除去
num_labels, labels, stats, _ = cv2.connectedComponen...
cleaned = np.zeros_like(closed)
for i in range(1, num_labels): # 0は背景
area = stats[i, cv2.CC_STAT_AREA]
if area > 500: #★
cleaned[labels == i] = 255
return cleaned
# === 3. 輪郭の取得と近似化 ===
def detect_plate_contour(img, cleaned_mask):
"""
白枠プレートの輪郭を検出し、内側スケール矩形のみを抽...
外枠(画像外周)は無視。
条件:
- 面積が 20000〜1450×1060 の範囲
- アスペクト比が 1.0〜a の範囲
- 図形中心が画像の外周b%以内でない
- 各角の角度が c°〜d° の範囲
"""
h, w = img.shape[:2]
max_allowed_area = 1450 * 1060
min_allowed_area = 630 * 340
# --- ① 外枠除外マスク ---
inner_mask = np.zeros_like(cleaned_mask)
margin = int(min(h, w) * 0.12) # 外周を無視
inner_mask[margin:h - margin, margin:w - margin] = 255
masked = cv2.bitwise_and(cleaned_mask, inner_mask)
# --- ② 輪郭検出 ---
contours, _ = cv2.findContours(masked, cv2.RETR_EXTE...
if not contours:
print("⚠️ 輪郭が検出されませんでした。")
return img, None
valid_candidates = []
for cnt in contours:
area = cv2.contourArea(cnt)
if area < min_allowed_area or area > max_allowed...
continue
rect = cv2.minAreaRect(cnt)
(cx, cy), (rw, rh), angle = rect
# --- ③ 中心位置チェック(外周に寄りすぎていない...
if cx < margin or cx > (w - margin) or cy < marg...
continue
# --- ④ アスペクト比チェック ---
aspect_ratio = max(rw, rh) / (min(rw, rh) + 1e-6)
if not (1.5 <= aspect_ratio <= 2.0):
continue
# --- ⑤ 近似と角度判定 ---
epsilon = 0.02 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
if len(approx) != 4:
continue
pts = approx.reshape(4, 2)
angles = []
for i in range(4):
p1, p2, p3 = pts[i], pts[(i + 1) % 4], pts[(...
v1, v2 = p1 - p2, p3 - p2
cos_angle = np.dot(v1, v2) / (np.linalg.norm...
angle = np.degrees(np.arccos(np.clip(cos_ang...
angles.append(angle)
if all(69 <= a <= 111 for a in angles):
valid_candidates.append((area, pts))
# --- ⑥ 最も適した候補を選択 ---
if not valid_candidates:
print("⚠️ 有効な内枠が見つかりませんでした。手動...
manual_points = []
def mouse_callback(event, x, y, flags, param):
if event == cv2.EVENT_RBUTTONDOWN: # 右クリ...
manual_points.append((x, y))
cv2.drawMarker(param, (x, y), (0, 255, 0...
cv2.imshow("manual_select", param)
print(f"✅ 追加: {len(manual_points)}点...
elif event == cv2.EVENT_LBUTTONDOWN: # 左ク...
if manual_points:
manual_points.pop()
print(f"↩️ 直前の点を削除。残り {len...
param_copy = img.copy()
for px, py in manual_points:
cv2.drawMarker(param_copy, (px, ...
cv2.imshow("manual_select", param_co...
temp = img.copy()
cv2.imshow("manual_select", temp)
cv2.setMouseCallback("manual_select", mouse_call...
print("🖱️ 右クリックで4点選択、左クリックで直前...
while True:
key = cv2.waitKey(0)
if key in [13, 10]: # Enter
break
cv2.destroyWindow("manual_select")
if len(manual_points) != 4:
print("⚠️ 4点が選択されませんでした。スキッ...
return img, None
best_pts = np.array(manual_points, dtype=np.floa...
contour_img = img.copy()
cv2.polylines(contour_img, [best_pts.astype(int)...
print("✅ 手動で内枠を確定しました。")
return contour_img, best_pts
best_area, best_pts = max(valid_candidates, key=lamb...
contour_img = img.copy()
cv2.drawContours(contour_img, [best_pts.astype(int)]...
print(f"🟩 内枠矩形を検出(面積={best_area:.0f}, ア...
return contour_img, best_pts
# === 4. メイン処理 ===
for fname in os.listdir(INPUT_DIR):
if not fname.lower().endswith((".jpg", ".jpeg", ".pn...
continue
img_path = os.path.join(INPUT_DIR, fname)
img = cv2.imread(img_path)
if img is None:
print(f"⚠️ 読み込み失敗: {fname}")
continue
# --- ステップ1: マスク化 ---
mask = mask_plate_region(img, threshold_mode="otsu")
-Scalehosei5.py
2. 緑枠検出と台形補正
○基本設定
台形補正に使う出力画像サイズ、検出用緑範囲、フォルダ再初...
○緑枠検出(detect_green_corners)
BGR→HSV変換し、緑色HSV域をinRangeで抽出
輪郭検出し、最大4点近似で「緑枠」四隅座標を取得
○透視変換による補正(perspective_correction)
4点座標を「左上→右上→右下→左下」に順序付け
指定したサイズに合わせてOpenCVの透視変換実行
import cv2
import numpy as np
import os
import shutil
# === 設定 ===
INPUT_DIR = "processed_patches4" # 緑枠付き画像フォルダ
OUTPUT_DIR = "processed_patches5" # 台形補正後の出力フ...
os.makedirs(OUTPUT_DIR, exist_ok=True)
# === 出力画像サイズ ===
# 縦8cm × 横14cm → 比率4:7 として例: 256×448 ピクセルに...
OUT_H, OUT_W = 256, 448
# === 緑色検出用のHSV範囲(調整可能) ===
HSV_LOWER = np.array([35, 80, 40]) # 緑の下限
HSV_UPPER = np.array([85, 255, 255]) # 緑の上限
# === フォルダ初期化 ===
if os.path.exists(OUTPUT_DIR):
shutil.rmtree(OUTPUT_DIR)
print(f"🧹 既存のフォルダ '{OUTPUT_DIR}' を削除しま...
os.makedirs(OUTPUT_DIR, exist_ok=True)
print(f"📁 新しいフォルダ '{OUTPUT_DIR}' を作成しました...
def detect_green_corners(img):
"""
緑枠の四隅を検出して返す。
成功すれば4点座標(np.float32)を返し、失敗時はNone。
"""
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, HSV_LOWER, HSV_UPPER)
# ノイズ除去
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (...
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel...
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kerne...
# 輪郭検出
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERN...
if not contours:
return None
# 面積の大きい順にソート
contours = sorted(contours, key=cv2.contourArea, rev...
for cnt in contours:
peri = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, 0.04 * peri, True)
if len(approx) == 4:
pts = approx.reshape(4, 2).astype(np.float32)
return pts # 四角形と認識されたら返す
return None
def order_points(pts):
"""4点を順序付きに整列(左上・右上・右下・左下)"""
rect = np.zeros((4, 2), dtype="float32")
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)] # 左上
rect[2] = pts[np.argmax(s)] # 右下
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)] # 右上
rect[3] = pts[np.argmax(diff)] # 左下
return rect
def perspective_correction(img, pts, out_size=(OUT_W, OU...
"""透視変換を行い、指定サイズにリサイズ"""
rect = order_points(pts)
dst = np.array([
[0, 0],
[out_size[0] - 1, 0],
[out_size[0] - 1, out_size[1] - 1],
[0, out_size[1] - 1]
], dtype="float32")
# dst の順番 (w,h)に注意:OpenCVでは横×縦
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(img, M, out_size)
return warped
# === メイン処理 ===
for fname in os.listdir(INPUT_DIR):
if not fname.lower().endswith((".jpg", ".jpeg", ".pn...
continue
path_in = os.path.join(INPUT_DIR, fname)
img = cv2.imread(path_in)
if img is None:
print(f"⚠️ 画像読み込み失敗: {fname}")
continue
corners = detect_green_corners(img)
if corners is None:
print(f"⚠️ 緑枠の四隅が検出できません: {fname}")
continue
warped = perspective_correction(img, corners, out_si...
path_out = os.path.join(OUTPUT_DIR, fname)
cv2.imwrite(path_out, warped)
print(f"✅ {fname} を補正・保存しました。")
print(f"\n🎯 台形補正が完了しました。出力フォルダ: {OUTP...
-Scalehosei6.py
○基本設定
目的ごとのフォルダ/クロップ範囲等を準備
○自動クロップ
各入力画像を一定サイズ(例:256x256)でNUM_CROPS枚だけ切...
保存ファイル名は評価点+連番で命名
import cv2
import os
import numpy as np
import shutil
# === 設定 ===
INPUT_DIR = "processed_patches5" # 抽出済み錆画像フォ...
OUTPUT_DIR = "processed_patches6" # 出力先
CROP_SIZE = 256 # 切り抜きサイズ(px)
NUM_CROPS = 4 # 1枚あたり生成枚数
OVERLAP_RATIO = 0.3 # オーバーラップ率(0...
# === 出力フォルダ初期化 ===
if os.path.exists(OUTPUT_DIR):
shutil.rmtree(OUTPUT_DIR)
os.makedirs(OUTPUT_DIR, exist_ok=True)
print(f"🗂️ 出力フォルダを作成: {OUTPUT_DIR}")
# === メイン処理 ===
for fname in os.listdir(INPUT_DIR):
if not fname.lower().endswith((".jpg", ".jpeg", ".pn...
continue
# 元画像の評点(例: "1S_0001.jpg" → score = 1)
score = fname[0]
img_path = os.path.join(INPUT_DIR, fname)
img = cv2.imread(img_path)
if img is None:
print(f"⚠️ 読み込み失敗: {fname}")
continue
h, w = img.shape[:2]
# 切り抜き可能数を超える場合は調整
crop_w = CROP_SIZE
crop_h = CROP_SIZE
# オーバーラップを考慮して切り抜き開始座標を決定
x_step = int((w - crop_w) / (NUM_CROPS - 1)) if NUM_...
y_step = int((h - crop_h) / (NUM_CROPS - 1)) if NUM_...
# オーバーラップの設定(比率で調整)
x_step = max(int(crop_w * (1 - OVERLAP_RATIO)), 1)
y_step = max(int(crop_h * (1 - OVERLAP_RATIO)), 1)
# スライドウィンドウ的に切り抜き
crop_count = 0
for y in range(0, max(h - crop_h + 1, 1), y_step):
for x in range(0, max(w - crop_w + 1, 1), x_step):
crop = img[y:y+crop_h, x:x+crop_w]
if crop.shape[0] != CROP_SIZE or crop.shape[...
continue
crop_count += 1
save_name = f"{score}_{crop_count}.jpg"
save_path = os.path.join(OUTPUT_DIR, save_na...
cv2.imwrite(save_path, crop)
if crop_count >= NUM_CROPS:
break
if crop_count >= NUM_CROPS:
break
print(f"✅ {fname}: {crop_count}枚の256×256画像を生...
print("🎯 全画像の切り抜き完了。")
-11/7
カラーの錆画像を256ピクセル四方で切り抜くことができた。11...
-色補正・切り抜き手順
--1, 始めにカラーの錆画像(drone_images)を用意する
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--2, Scalehosei1.pyを実行する そうすると元画像からカラー...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--3, Scalehosei2.pyを実行する 純色に変換された切り抜かれ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--4, Scalehosei3.pyを実行する 3の変換行列をもとに画像全...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--5, Scalehosei4.pyを実行する カラースケールの内側に緑色...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--6, Scalehosei5.pyを実行する 枠から錆を切り抜く
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--7, Scalehosei6.pyを実行する 切り抜いた画像を学習用に25...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
抽出の際に使用した緑の枠が残ってしまっているがとりあえず...
-11/9
カラーの錆画像で学習を行った。(評点1:6枚, 評点2:54枚, 評...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-11/11
-DenseNet121 単体で3回学習(同一条件)を行ったところ以下...
, Run , MAE , 正答率 , F1スコア
, 1 , 0.282702960866563 , 0.553191489361702 , 0.366171839...
, 2 , 0.237414197718844 , 0.553191489361702 , 0.381286549...
, 3 , 0.237434574898253 , 0.531914893617021 , 0.383338632...
カラーの錆画像を学習させた結果( .keras ファイル)を用い...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
, 画像 , 評点 , 錆割合 , 錆数
, 1_1.jpg , 3 , 0.996 , 1
, 1_2.jpg , 3 , 0.530 , 138
, 1_3.jpg , 3 , 0.999 , 1
, 2_1.jpg , 3 , 1.000 , 1
, 3_1.jpg , 3 , 0.996 , 1
, 3_2.jpg , 3 , 1.000 , 1
, 3_3.jpg , 3 , 1.000 , 1
, 4_1.jpg , 3 , 1.000 , 1
, 4_4.png , 3 , 0.423 , 956
, 4_5.png , 3 , 0.396 , 1038
, 4_61.jpg , 3 , 0.477 , 26143
, 4_62.jpg , 3 , 0.482 , 11092
, 4_63.jpg , 4 , 0.729 , 3324
, 4_64.jpg , 3 , 0.374 , 66397
, 4_65.jpg , 2 , 0.330 , 217611
ドローン撮影画像では照明・影・反射の影響が大きく学習済みC...
-11/12
ドローンで撮影した画像から評点を求めることを行った。錆の...
-β版(最初モノクロに変換してから領域と評点を自動で抽出 → ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-改良版(白, 黒の強弱を調節し橋梁側面の錆をより自動で抽出...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-錆領域手動切り抜き版(予め橋梁側面の錆領域を定義してその...
--錆領域切り抜き
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--評点領域を抽出
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
***11月第3週(11/13〜11/19) [#j417c1113]
-11/13
昨日行った評点領域抽出に関して納得の行く結果が得られなか...
-ヒートマップでの評点抽出
ドローンで撮影された橋梁表面の錆画像から錆の劣化程度を視...
○手順
--1, 入力画像の頂点選択
---対象となる橋梁のカラー画像を入力フォルダーに入力し、マ...
---マウス操作のコマンド一覧
, コマンド , 実行内容
, 左クリック , 頂点を追加
, 右クリック , 直前の頂点を削除
, Enterキー , 頂点選択確定 次の画像へ
, Escapeキー , 選択中止
---頂点選択を行う際には以下のようなウィンドウが出力される...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--2, 領域分割と評点推定
---選択された領域を任意サイズ(例:256×256ピクセル)の小...
---各パッチ画像に対して事前に学習済みのCNNアンサンブルモ...
---評点は以下の基準で分類される
, 評点 , 劣化状態 , 表示色
, 1 , 重度の錆 , 🔴 赤
, 2 , 進行した錆 , 🟠 橙
, 3 , 中程度の錆 , 🟡 黄
, 4 , 軽微な錆 , 🟢 緑
, 5 , 錆がほとんどない , 🔵 水色
--3, ヒートマップ生成
---各パッチの推定評点に応じて対応する色を半透明で重ね合わ...
--4, 出力と付加情報
---生成されたヒートマップには以下の情報が自動的に付与される
, 情報記載位置 , 内容
, 左下 , ポリゴン内全体の平均評点値(Avg)
, 右下 , 各評点に対応する色凡例(Legend)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-ヒートマップ抽出スクリプト
import cv2
import shutil
import numpy as np
from tensorflow.keras.models import load_model
import os
import glob
# === 設定 ===
MODEL_PATHS = [
"ensemble_model_1.keras",
"ensemble_model_2.keras",
"ensemble_model_3.keras"
]
INPUT_DIR = "drone_images"
OUTPUT_DIR = "drone_detect_result"
PATCH_SIZE = 256 # 任意変更可
# === フォルダー初期化 ===
if os.path.exists(OUTPUT_DIR):
shutil.rmtree(OUTPUT_DIR)
print(f"既存のフォルダ '{OUTPUT_DIR}' を削除しました...
os.makedirs(OUTPUT_DIR, exist_ok=True)
# === モデル読み込み ===
models = []
for path in MODEL_PATHS:
if os.path.exists(path):
model = load_model(path, compile=False)
models.append(model)
print(f"✅ モデル読み込み完了: {path}")
if not models:
raise FileNotFoundError("❌ 有効な .keras モデルが見...
# === 評点ごとの色設定 ===
score_colors = {
1: (0, 0, 255), # 赤
2: (0, 165, 255), # 橙
3: (0, 255, 255), # 黄
4: (0, 255, 0), # 緑
5: (255, 255, 0), # 水色
}
# === 評点推定関数 ===
def predict_score(crop_img):
resized = cv2.resize(crop_img, (256, 256))
x = resized.astype("float32") / 255.0
x = np.expand_dims(x, axis=0)
preds = [m.predict(x, verbose=0)[0][0] for m in mode...
avg_pred = np.mean(preds)
# 評点を5段階に丸める
if avg_pred < 1.9: score = 1
elif avg_pred < 2.6: score = 2
elif avg_pred < 3.3: score = 3
elif avg_pred < 4.0: score = 4
else: score = 5
return score, avg_pred
# === 手動ポリゴン選択 ===
points = []
def draw_polygon(event, x, y, flags, param):
global points
img_copy = param["img_copy"]
if event == cv2.EVENT_LBUTTONDOWN:
points.append((x, y))
cv2.circle(img_copy, (x, y), 5, (0, 0, 255), -1)
elif event == cv2.EVENT_RBUTTONDOWN and points:
points.pop()
img_copy[:] = param["img"].copy()
for px, py in points:
cv2.circle(img_copy, (px, py), 5, (0, 0, 255...
elif event == cv2.EVENT_MOUSEMOVE:
img_copy[:] = param["img"].copy()
for px, py in points:
cv2.circle(img_copy, (px, py), 5, (0, 0, 255...
if len(points) > 1:
cv2.polylines(img_copy, [np.array(points)], ...
# === 錆検出処理(ヒートマップ生成 + 凡例 + 平均) ===
def detect_rust_heatmap(img_path, patch_size=256):
global points
basename = os.path.basename(img_path).split('.')[0]
img = cv2.imread(img_path)
if img is None:
print(f"⚠️ 読み込み失敗: {img_path}")
return
# --- 画像を1980×1080で表示 ---
display_img = cv2.resize(img, (1980, 1080))
scale_x = img.shape[1] / 1980
scale_y = img.shape[0] / 1080
img_copy = display_img.copy()
cv2.namedWindow("Select Area", cv2.WINDOW_NORMAL)
cv2.setMouseCallback("Select Area", draw_polygon, {"...
print("🟢 左クリック=頂点追加 / 右クリック=戻す / ...
while True:
cv2.imshow("Select Area", img_copy)
key = cv2.waitKey(1) & 0xFF
if key == 13: # Enter
if len(points) >= 3:
print("✅ ポリゴン確定")
break
else:
print("⚠️ 頂点が3つ未満です")
elif key == 27: # ESC
print("🚫 中断されました")
cv2.destroyAllWindows()
return
# --- スケール戻す ---
scaled_points = np.array([[int(x * scale_x), int(y *...
points = []
# --- ポリゴンマスク作成 ---
mask = np.zeros(img.shape[:2], np.uint8)
cv2.fillPoly(mask, [scaled_points], 255)
# --- 領域内スライスしてスコア予測 ---
result = img.copy()
heatmap = np.zeros_like(img)
h, w = img.shape[:2]
scores_list = []
for y in range(0, h, patch_size):
for x in range(0, w, patch_size):
cx, cy = x + patch_size//2, y + patch_size//2
if cx >= w or cy >= h:
continue
# 中心がポリゴン内か確認
if mask[cy, cx] == 0:
continue
crop = img[y:y+patch_size, x:x+patch_size]
if crop.shape[0] < patch_size or crop.shape[...
continue
score, raw_pred = predict_score(crop)
scores_list.append(raw_pred)
color = score_colors[score]
overlay = np.full_like(crop, color, dtype=np...
# 透明重ね
cv2.addWeighted(overlay, 0.4, result[y:y+pat...
# --- 平均スコア ---
avg_score = np.mean(scores_list) if scores_list else 0
print(f"📊 平均スコア: {avg_score:.2f}")
# --- ポリゴン枠 + 平均スコア表示(左下) ---
cv2.polylines(result, [scaled_points], True, (255, 2...
h, w = result.shape[:2]
cv2.putText(result, f"Avg: {avg_score:.2f}", (30, h ...
cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255,255,...
# --- 凡例描画(右下) ---
legend_x = w - 250
legend_y = h - 180
cv2.rectangle(result, (legend_x - 20, legend_y - 30)...
cv2.putText(result, "Legend", (legend_x, legend_y - ...
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255,255,...
for i, (score, color) in enumerate(score_colors.item...
y_pos = legend_y + i * 35
cv2.rectangle(result, (legend_x, y_pos), (legend...
cv2.putText(result, f"{score}", (legend_x + 45, ...
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255,...
# --- 保存 ---
out_path = os.path.join(OUTPUT_DIR, f"{basename}_hea...
cv2.imwrite(out_path, result)
print(f"🟩 ヒートマップを保存: {out_path}")
cv2.destroyAllWindows()
# === メイン ===
def batch_process_images():
image_files = sorted(glob.glob(os.path.join(INPUT_DI...
for idx, path in enumerate(image_files, 1):
print(f"📸 ({idx}/{len(image_files)}) {path}")
detect_rust_heatmap(path, patch_size=PATCH_SIZE)
if __name__ == "__main__":
batch_process_images()
-11/14
○ピクセル数を変化させると評点に変化がでるのか
--256ピクセル
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
, 画像 , 平均スコア
, 左1 , 3.08
, 左2 , 3.12
, 中央 , 2.99
, 右2 , 3.06
, 右1 , 3.02
--128ピクセル
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
, 画像 , 平均スコア
, 左1 , 3.01
, 左2 , 3.03
, 中央 , 2.99
, 右2 , 3.01
, 右1 , 3.02
--64ピクセル
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
, 画像 , 平均スコア
, 左1 , 3.34
, 左2 , 3.34
, 中央 , 3.18
, 右2 , 3.39
, 右1 , 3.28
--32ピクセル
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
, 画像 , 平均スコア
, 左1 , 3.44
, 左2 , 3.62
, 中央 , 3.49
, 右2 , 3.49
, 右1 , 3.50
出力時間を考慮したら128ピクセル、精度を考慮したら64ピクセ...
-11/17
これまで橋側面からSAM (Segment Anything Model)を用いて錆...
--SAMでの抽出(アンサンブルとCNNモデル単体比較)上段から...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
大きく評点が変わることは無かったがヒートマップにはそれな...
-11/19
U-netを用いて錆領域の自動判別を行った。錆側面以外にも護岸...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
***11月第4週(11/20〜11/26) [#j417c1120]
-11/20
import os
import sys
import cv2
import json
import time
import glob
import shutil
import random
import datetime
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers, models, Input, Model
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import ModelCheckpoint, ...
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
# -------------------------
# CONFIGURATION - 必要に応じて書き換えてください
# -------------------------
TRAIN_IMAGES_DIR = "train_images" # アノテ・学習用...
MASKS_DIR = "masks" # アノテ結果(0=bg...
MODELS_DIR = "models" # 保存先フォルダ
UNET_MODEL_PATH = os.path.join(MODELS_DIR, "unet.keras")
DRONE_IMAGES_DIR = "drone_images" # 推論対象ドロー...
RESULTS_DIR = "results_unet" # 推論出力(ヒー...
# パッチ・ヒートマップ設定
PATCH_SIZE = 256
STRIDE = 126 # スライド幅(任意で調整)
DISPLAY_MAX = (1980, 1080) # 表示最大解像度(要求により...
# CNN評点用の事前学習済みモデル群(任意)
# ここに .keras ファイルを列挙すると、アンサンブルでスコ...
ENSEMBLE_MODELS = [
"ensemble_model_1.keras",
"ensemble_model_2.keras",
"ensemble_model_3.keras"
]
RANDOM_SEED = 42
np.random.seed(RANDOM_SEED)
tf.random.set_seed(RANDOM_SEED)
random.seed(RANDOM_SEED)
# -------------------------
# ユーティリティ
# -------------------------
def ensure_dirs():
os.makedirs(MASKS_DIR, exist_ok=True)
os.makedirs(MODELS_DIR, exist_ok=True)
os.makedirs(RESULTS_DIR, exist_ok=True)
# --- 錆マスクから bridge マスクを自動生成するユーティリ...
def generate_bridge_mask_from_rust_mask(rust_mask_path, ...
dilate_iter=20, ...
"""
rust_mask_path: 二値 or ラベルマスク(255=rust など...
out_bridge_mask_path: 保存先(png)。None の場合は r...
戻り値: bridge_mask (H,W) uint8 (0/255)
パラメータ調整: - dilate_iter: 錆領域をどれだけ...
- min_area: 小領域を無視する閾値
"""
m = cv2.imread(rust_mask_path, cv2.IMREAD_UNCHANGED)
if m is None:
raise FileNotFoundError(f"rust mask not found: {...
# もしラベル(maskが0/1/2)なら rust に対応する値を抽出
if m.ndim == 3:
m_gray = cv2.cvtColor(m, cv2.COLOR_BGR2GRAY)
else:
m_gray = m.copy()
# 二値化(非ゼロを錆とみなす)
_, bw = cv2.threshold(m_gray, 1, 255, cv2.THRESH_BIN...
# 膨張で周囲をカバー(橋側面を含めたいので大きめ)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE...
dil = cv2.dilate(bw, kernel, iterations=dilate_iter)
# 小領域除去(ノイズ排除)
num_labels, labels, stats, _ = cv2.connectedComponen...
mask_clean = np.zeros_like(dil)
for i in range(1, num_labels):
area = stats[i, cv2.CC_STAT_AREA]
if area >= min_area:
mask_clean[labels == i] = 255
# 凸包で外形を滑らかに(オプション)
if convexify:
contours, _ = cv2.findContours(mask_clean, cv2.R...
hull_mask = np.zeros_like(mask_clean)
for cnt in contours:
if cv2.contourArea(cnt) < min_area:
continue
hull = cv2.convexHull(cnt)
cv2.drawContours(hull_mask, [hull], -1, 255,...
mask_final = hull_mask
else:
mask_final = mask_clean
# 少し膨らませて余裕を持たせる(bridge全体を覆う)
kernel2 = cv2.getStructuringElement(cv2.MORPH_ELLIPS...
mask_final = cv2.dilate(mask_final, kernel2, iterati...
if out_bridge_mask_path is None:
base = os.path.splitext(rust_mask_path)[0]
out_bridge_mask_path = base + "_bridge.png"
cv2.imwrite(out_bridge_mask_path, mask_final)
return mask_final
def resize_for_display(img, max_size=DISPLAY_MAX):
h, w = img.shape[:2]
scale = min(max_size[0] / w, max_size[1] / h, 1.0)
if scale < 1.0:
return cv2.resize(img, (int(w*scale), int(h*scal...
else:
return img, 1.0
# -------------------------
# 1) アノテーションツール(橋側面: 1ポリゴン、錆: 複数ポ...
# -------------------------
def annotate_mode(input_dir=TRAIN_IMAGES_DIR, masks_dir=...
ensure_dirs()
files = sorted([f for f in os.listdir(input_dir) if ...
if not files:
print("No images found in", input_dir); return
for fname in files:
img_path = os.path.join(input_dir, fname)
img = cv2.imread(img_path)
if img is None:
print("Failed to load", img_path); continue
basename = os.path.splitext(fname)[0]
print(f"\n=== Annotate bridge only: {fname} ===")
poly = polygon_draw_ui(img, title=f"Draw Bridge ...
if poly is None:
print("Skipped:", fname)
continue
mask = np.zeros(img.shape[:2], dtype=np.uint8)
cv2.fillPoly(mask, [np.array(poly)], 1) # 1 = ...
save_path = os.path.join(masks_dir, f"{basename}...
# if existing rust mask exists, merge later duri...
cv2.imwrite(save_path, mask)
print("Saved bridge mask:", save_path)
def polygon_draw_ui(img, title="Draw polygon", allow_qui...
win = title
img_work = img.copy()
img_display = img.copy()
points = []
# ▼ 初期縮小(ウィンドウに合わせる)
disp_img, scale = resize_for_display(img_display)
def mouse_cb(event, x, y, flags, param):
nonlocal img_display, points, scale
# ▼ 表示画像 → 元画像座標へ変換
orig_x = int(x / scale)
orig_y = int(y / scale)
if event == cv2.EVENT_LBUTTONDOWN:
points.append((orig_x, orig_y))
elif event == cv2.EVENT_RBUTTONDOWN:
if points:
points.pop()
# 描画は元画像サイズで行う
img_display = img_work.copy()
for px, py in points:
cv2.circle(img_display, (px, py), 4, (0,0,25...
if len(points) > 1:
cv2.polylines(img_display, [np.array(points)...
cv2.namedWindow(win, cv2.WINDOW_NORMAL)
cv2.setMouseCallback(win, mouse_cb)
print("Instructions:")
print(" - Left click: add vertex")
print(" - Right click: undo last vertex")
print(" - Enter: confirm polygon")
print(" - Esc: cancel")
while True:
# ▲ 表示ごとに縮尺計算(ウィンドウサイズが変わる...
disp_img, scale = resize_for_display(img_display)
cv2.imshow(win, disp_img)
k = cv2.waitKey(20) & 0xFF
if k == 13: # Enter
if len(points) >= 3:
cv2.destroyWindow(win)
return points.copy()
else:
print("Need at least 3 points.")
elif k == 27: # Esc
cv2.destroyWindow(win)
return None
elif allow_quit_key and k == ord(allow_quit_key):
cv2.destroyWindow(win)
return None
# -------------------------
# 2) U-Net モデル定義(軽量版)
# -------------------------
def build_unet(input_shape=(256,256,3), n_classes=2, bas...
inputs = Input(shape=input_shape)
# Encoder
c1 = conv_block(inputs, base_filters)
p1 = layers.MaxPool2D()(c1)
c2 = conv_block(p1, base_filters*2)
p2 = layers.MaxPool2D()(c2)
c3 = conv_block(p2, base_filters*4)
p3 = layers.MaxPool2D()(c3)
c4 = conv_block(p3, base_filters*8)
p4 = layers.MaxPool2D()(c4)
b = conv_block(p4, base_filters*16)
# Decoder
u1 = layers.UpSampling2D()(b)
u1 = layers.Concatenate()([u1, c4])
c5 = conv_block(u1, base_filters*8)
u2 = layers.UpSampling2D()(c5)
u2 = layers.Concatenate()([u2, c3])
c6 = conv_block(u2, base_filters*4)
u3 = layers.UpSampling2D()(c6)
u3 = layers.Concatenate()([u3, c2])
c7 = conv_block(u3, base_filters*2)
u4 = layers.UpSampling2D()(c7)
u4 = layers.Concatenate()([u4, c1])
c8 = conv_block(u4, base_filters)
outputs = layers.Conv2D(n_classes, kernel_size=1, ac...
model = Model(inputs, outputs)
model.compile(optimizer=Adam(1e-4), loss='sparse_cat...
return model
def conv_block(x, filters):
x = layers.Conv2D(filters, 3, padding='same', activa...
x = layers.BatchNormalization()(x)
x = layers.Conv2D(filters, 3, padding='same', activa...
x = layers.BatchNormalization()(x)
return x
# -------------------------
# 3) Data loader for training
# -------------------------
def load_dataset(img_dir=TRAIN_IMAGES_DIR, mask_dir=MASK...
"""
- mask が missing の場合、可能なら rust-only mask ...
- 統合後: mask values -> 0(background) / 2(rust)
"""
imgs = []
masks = []
files = sorted([f for f in os.listdir(img_dir) if f....
for f in files:
base = os.path.splitext(f)[0]
img_path = os.path.join(img_dir, f)
mask_path = os.path.join(mask_dir, base + "_mask...
img = cv2.imread(img_path)
if img is None:
print("Failed to load image:", img_path); co...
# ---- マスクが存在しない場合の自動処理 ----
if not os.path.exists(mask_path):
# try to find a rust-only mask: either base+...
rust_candidates = [
os.path.join(mask_dir, base + "_rust.png...
os.path.join(mask_dir, base + "_rustmask...
os.path.join(mask_dir, base + "_r.png"),
os.path.join(mask_dir, base + ".png"),
]
rust_found = None
for rc in rust_candidates:
if os.path.exists(rc):
rust_found = rc
break
if rust_found:
# generate bridge mask from rust mask
bridge_mask = generate_bridge_mask_from_...
out_bridge_mask_pat...
# Now combine: create a 0/1/2 mask: star...
combined = np.zeros(img.shape[:2], dtype...
combined[bridge_mask > 0] = 1
rust_img = cv2.imread(rust_found, cv2.IM...
if rust_img is not None:
if rust_img.ndim == 3:
rust_gray = cv2.cvtColor(rust_im...
else:
rust_gray = rust_img
combined[rust_gray > 0] = 2
mask = combined
# save combined mask
cv2.imwrite(mask_path, mask)
print("Auto-generated mask saved:", mask...
else:
print("Mask not found and no rust candid...
continue
else:
mask = cv2.imread(mask_path, cv2.IMREAD_UNCH...
if mask is None:
print("Invalid mask for", f, "- skipping...
# resize and append
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_resized = cv2.resize(img_rgb, img_size)
mask_resized = cv2.resize(mask, img_size, interp...
imgs.append(img_resized.astype(np.float32) / 255...
masks.append(mask_resized.astype(np.uint8))
if not imgs:
raise RuntimeError("No training pairs found. Run...
X = np.stack(imgs, axis=0)
Y = np.stack(masks, axis=0)
return X, Y
# -------------------------
# 4) Training function
# -------------------------
def train_unet(epochs=20, batch_size=8, img_size=(256,25...
ensure_dirs()
X, Y = load_dataset(img_size=img_size)
X_train, X_val, Y_train, Y_val = train_test_split(X,...
model = build_unet(input_shape=(*img_size, 3), n_cla...
print(model.summary())
ckpt = ModelCheckpoint(UNET_MODEL_PATH, save_best_on...
es = EarlyStopping(patience=6, restore_best_weights=...
history = model.fit(X_train, Y_train, validation_dat...
epochs=epochs, batch_size=batch_...
print("Training finished. Model saved to", UNET_MODE...
return model, history
# -------------------------
# 5) U-Net 推論(bridge + rust マスクを返す)
# -------------------------
def unet_predict(img_bgr, model=None, img_size=(256,256)):
"""
img_bgr: OpenCV BGR image
returns: pred_mask (H,W) with values 0 or 1 (argmax ...
"""
if model is None:
if not os.path.exists(UNET_MODEL_PATH):
raise RuntimeError("No UNET model found. Tra...
model = tf.keras.models.load_model(UNET_MODEL_PA...
orig_h, orig_w = img_bgr.shape[:2]
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
inp = cv2.resize(img_rgb, img_size).astype(np.float3...
pred = model.predict(np.expand_dims(inp, axis=0), ve...
pred_mask = np.argmax(pred, axis=-1).astype(np.uint8...
# resize back to original size
pred_mask = cv2.resize(pred_mask, (orig_w, orig_h), ...
return pred_mask
# -------------------------
# 6) Load ensemble CNN models (optional) for score predi...
# -------------------------
def load_ensemble_models(model_paths):
models_list = []
for p in model_paths:
if os.path.exists(p):
try:
m = tf.keras.models.load_model(p, compil...
models_list.append(m)
print("Loaded model:", p)
except Exception as e:
print("Failed to load", p, ":", e)
else:
print("Model not found, skip:", p)
return models_list
ensem_models = load_ensemble_models(ENSEMBLE_MODELS)
def predict_patch_score(patch_bgr):
# パッチが 1px でも欠けていると CNN に入れられないた...
if patch_bgr is None or patch_bgr.shape[0] < 16 or p...
return 0.0 # または skip
# CNN の入力は必ず 256×256 に統一
resized = cv2.resize(patch_bgr, (256, 256))
x = resized.astype(np.float32) / 255.0
x = np.expand_dims(x, axis=0)
if not ensem_models:
return float(x.mean()*5.0)
preds = []
for m in ensem_models:
pred = m.predict(x, verbose=0)[0][0]
preds.append(pred)
return float(np.mean(preds))
# -------------------------
# 7) Detection & scoring pipeline (for drone images)
# -------------------------
SCORE_COLORS = {
1: (0, 0, 255), # 赤
2: (0, 165, 255), # 橙
3: (0, 255, 255), # 黄
4: (0, 255, 0), # 緑
5: (255, 255, 0), # 水色
}
def score_to_color(score):
# score in continuous range ~ [0..5.5], convert to d...
if score < 1.9: s = 1
elif score < 2.6: s = 2
elif score < 3.3: s = 3
elif score < 4.0: s = 4
else: s = 5
return SCORE_COLORS[s], s
def detect_and_score(drone_dir=DRONE_IMAGES_DIR, out_dir...
ensure_dirs()
if not os.path.exists(UNET_MODEL_PATH):
print("UNET model not found. Please train first.")
return
unet_model = tf.keras.models.load_model(UNET_MODEL_P...
files = sorted([f for f in os.listdir(drone_dir) if ...
for fname in files:
path = os.path.join(drone_dir, fname)
img = cv2.imread(path)
if img is None:
print("Failed to read", path); continue
orig = img.copy()
print("Processing:", fname)
# Predict mask (0=bg,1=rust)
pred_mask = unet_predict(img, model=unet_model, ...
rust_mask = (pred_mask == 1).astype(np.uint8) * ...
# 使用する領域:錆が検出されたバウンディングボッ...
ys, xs = np.where(rust_mask > 0)
if len(ys) == 0:
print("No rust found in", fname);
# それでも出力用フォルダは作る
out_base = os.path.join(out_dir, os.path.spl...
os.makedirs(out_base, exist_ok=True)
cv2.imwrite(os.path.join(out_base, "orig.jpg...
cv2.imwrite(os.path.join(out_base, "pred_mas...
cv2.imwrite(os.path.join(out_base, "rust_mas...
continue
miny, maxy = ys.min(), ys.max()
minx, maxx = xs.min(), xs.max()
heatmap = np.zeros((img.shape[0], img.shape[1]),...
countmap = np.zeros_like(heatmap)
# スライディング窓を錆のバウンディング箱だけで回す
y_start = max(miny - patch_size, 0)
y_end = min(maxy + patch_size, img.shape[0])
x_start = max(minx - patch_size, 0)
x_end = min(maxx + patch_size, img.shape[1])
for y in range(y_start, y_end - patch_size + 1, ...
for x in range(x_start, x_end - patch_size +...
patch_rust = rust_mask[y:y+patch_size, x...
# 錆ピクセルが十分に含まれるパッチのみ評価
if patch_rust.sum() < 10:
continue
patch = img[y:y+patch_size, x:x+patch_si...
score = predict_patch_score(patch) # 連...
heatmap[y:y+patch_size, x:x+patch_size] ...
countmap[y:y+patch_size, x:x+patch_size]...
# 平均マップ計算(count>0 のみ)
avg_map = np.zeros_like(heatmap)
mask_count = countmap > 0
avg_map[mask_count] = heatmap[mask_count] / coun...
# for visualization, create colored overlay
overlay = img.copy()
legend_items = []
# create color-coded overlay by mapping each cel...
disp = img.copy()
for y in range(0, img.shape[0]-patch_size+1, 4):...
for x in range(0, img.shape[1]-patch_size+1,...
val = avg_map[y, x]
if val <= 0: continue
color, s = score_to_color(val)
cv2.rectangle(disp, (x, y), (x+4, y+4), ...
# blend original and disp
blended = cv2.addWeighted(img, 0.6, disp, 0.4, 0)
# compute overall average score for area
if mask_count.any():
global_avg = float(avg_map[mask_count].mean())
else:
global_avg = 0.0
# annotate global avg left-bottom, legend right-...
text = f"avg_score: {global_avg:.2f}"
cv2.putText(blended, text, (10, img.shape[0]-30)...
# legend
legend_h = 160
legend_w = 200
legend = np.zeros((legend_h, legend_w, 3), dtype...
cv2.putText(legend, "Legend (score)", (10,20), c...
y0 = 40
i = 0
for s in sorted(SCORE_COLORS.keys()):
color = SCORE_COLORS[s]
cv2.rectangle(legend, (10, y0+i*24), (30, y0...
cv2.putText(legend, f"{s}", (40, y0+12+i*24)...
i += 1
# paste legend on blended bottom-right
lh, lw = legend.shape[:2]
blended[-lh-10:-10, -lw-10:-10] = legend
# save outputs
out_base = os.path.join(out_dir, os.path.splitex...
os.makedirs(out_base, exist_ok=True)
cv2.imwrite(os.path.join(out_base, "orig.jpg"), ...
cv2.imwrite(os.path.join(out_base, "pred_mask.pn...
cv2.imwrite(os.path.join(out_base, "rust_mask.pn...
cv2.imwrite(os.path.join(out_base, "heatmap_over...
print("Saved results to", out_base)
# -------------------------
# 8) small CLI to run steps
# -------------------------
def print_menu():
print("""
rust_unet_system - menu:
1) Annotate images (create masks)
2) Train UNet (from masks)
3) Run detection & scoring on drone_images
4) Exit
""")
def main():
ensure_dirs()
while True:
print_menu()
c = input("Choose option: ").strip()
if c == '1':
print("Starting annotation mode...")
annotate_mode()
elif c == '2':
print("Starting training...")
epochs = int(input("epochs [default 20]: ") ...
batch = int(input("batch_size [default 8]: "...
img_s = int(input("image_size (square) [defa...
train_unet(epochs=epochs, batch_size=batch, ...
elif c == '3':
print("Running detection & scoring...")
detect_and_score()
elif c in ('4','q','quit','exit'):
print("Exit.")
break
else:
print("Unknown option.")
if __name__ == "__main__":
main()
-① 画像アノテーション(マスク作成)モード
--目的:U-Net が学習するための「教師データ(錆領域マスク...
--動作内容
---rain_images/ 内の画像を1枚ずつ表示して錆領域をポリゴン...
--ポイント
---ポリゴンは1つだけ(錆領域のみ)
---以前のように「bridge」「rust」の2段階の描画ではない
---一度マスクが作られたら、その後は何度も学習に再利用可能
-② U-Net の学習モード
--目的:アノテーション済みマスクを使って錆領域を自動検出...
--処理内容
---train_images/ の画像とmasks/ 内の対応マスク(0:背景/2...
---画像とマスクを (256×256) にリサイズしセットを作る
---U-Net(簡易軽量版)を構築
--学習後に得られるもの
---錆の領域をピクセル単位で推定できる U-Netファイル
-③ ドローン画像 → 錆領域抽出 → 評点ヒートマップ生成モード
-目的:ドローンで撮影した広い橋側面画像に対してU-Net で錆...
・処理詳細
--(1) U-Net により錆領域の抽出
---入力画像を読み込み U-Net で推定 (pred_mask==0 → 背景,...
---必要なのは錆領域のみなのでrust_mask = (pred_mask == 2)...
--(2) 錆領域の周囲でスライドウィンドウ実施
---錆領域の bounding box(範囲)を計算し、その範囲でパッ...
--(3) パッチごとに CNN でスコア予測
---スライスされたパッチは CNN(あなたが学習した3つのDense...
--(4) スコアをヒートマップ領域に加算し、ピクセルごとの平...
--(5) 凡例と平均スコアを追記して保存
---左下:全体平均評点(avg_score)
---右下:凡例(1〜5の色)
-「錆の領域抽出(U-Net)+ ヒートマップで評点可視化」 を...
-11/21
ヒートマップを作成するのに必要な撮影画像のピクセルサイズ...
上記のスクリプトを実行して錆とそうでない部分(ここでは背...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-11/25
錆だけを予め学習させた状態でドローン撮影画像にヒートマッ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
***11月第5週(11/27〜12/3) [#j417c1127]
-11/26
◯ 1. U-Net を用いた錆領域抽出の開始
従来の手法では錆の抽出に SAM(Segment Anything Model)や...
-ピクセル単位の分類能力が高い
-少量の教師データでも学習しやすい
-橋面に対して斜めから撮影された画像でも比較的安定した結果...
◯ 2. 必要なクラス構造(背景・橋側面・錆)
U-Net 導入前までは「錆(対象)」と「それ以外(背景)」と...
, クラス(class)番号 , 分類内容 , 役割
, class0 , 背景 , 河川、空、堤防、民家、植物など「錆領域...
, class1 , 橋側面 , U-Net が抽出すべき橋のみを特定
, class2 , 錆 , 評点推定の入力として利用
◯ 3. 教師データの整備
教師データは以下の 3 種類で構成される
, 教師データ , クラス番号 , データ利用方法
, 近接錆画像(256×256ピクセル四方), class2(= 錆) , 錆の...
, 近影画像(橋側面) , class1(= 橋側面) , 背景が写り込む...
, 背景画像(ただの風景画), class0(= 背景) , 橋が写ってい...
◯ 4. マスク画像(≒モノクロ画像)の作成
-① ポリゴン選択 UI により橋側面を手動で指定
-② class1 として塗りつぶし
-③ 錆部分がある場合は追加で class2 を塗り分ける
-④ 近接錆画像と背景画像は、自動で class2 / class0 として...
◯ 5. U-Net の学習
U-Net の入出力は以下の通り
-入力:256×256 RGB値
-出力:256×256 の 3 クラス分類マップ(0,1,2)
学習プロセスでは EarlyStopping を導入しベストなエポックで...
◯ 6. 橋側面の自動抽出
U-Net によって作成された「class1=橋側面」領域は自動的に抽...
◯7. 錆ヒートマップ生成(class2 → 評点)
橋側面マスクが生成されたら次に錆領域を小領域パッチに分割...
-錆ヒートマップ(5 色)
-橋側面マスク(白黒)
-予測された錆マスク
-元画像
スクリプトの改良を行ったところヒートマップが表示されなく...
**12月 [#j417c1200]
***12月第1週(12/4〜12/10) [#j417c1204]
パラメータを調節しながら結果を見比べている
-検討要素
--画像の水増し…現在は撮影した画像そのものを学習させたいた...
--学習データ比較…1回各要素の学習枚数を1枚とかに変えて学...
-12/8
スクリプトに何度も修正を加えたことで原型を留めていない事...
なので今月の中間発表に向けて使用するスクリプトとアプロー...
-12/10
錆を検出して mask 画像を生成するシステムの改善に取り組み...
[[↑上がるゥ?>#contents]]
**画像のスキャンについて [#j991c400]
錆画像スキャン方法
・スキャナーは研究室にあるものを使用する
・使用するソフトはXSane(デスクトップの左上 "メニュー → ...
-1, XSane を立ち上げてOKを押す
-2, ウィンドウを消したい or 表示させたい場合はウィンドウ...
-3, 様々な色が表示されているアイコンでカラー, モノクロに...
-4, 3の2つ下にある部分で解像度の変更が可能
[[XSaneの詳しい使い方はこちら >https://kledgeb.blogspot.c...
*創造工房実習 [#v035a40a]
**4/18 [#j417c418]
創造工房実習でI型断面等の三角形分布や塑性状態がどういう...
-I型断面に10Nの面載荷をかけたとき
--梁の断面に赤と青で濃く色分けされており、上下縁の両方は...
--固定端近くでは、直応力σxxがほぼ対称に上縁で圧縮、下縁で...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/1/t...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/1/t...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/1/t...
-15Nの面載荷をかけたとき
--10Nの荷重をかけたときよりも梁の上下縁にかなりの力がかか...
--固定端近くでは10Nのときと同様、真ん中から先端付近にかけ...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/1/t...
-5N, 6Nの面載荷をかけたとき
--固定面側は最大281MPaの直応力が加わっている上縁がありこ...
--下端側よりも上端側のほうが直応力の値が高く出る傾向にあ...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/1/t...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/1/t...
上端
・ーーーーーーーーーー
| ーーーーー
・ーーーーーーーーー ーーーー|
下端 ーーーーー |↓集中荷重位置...
ーーーーー| ...
**4/17 [#j417c906]
-SALOME2021でメッシュが計算されない事例が発生
--1, メッシュの編集画面を開く
--2, アルゴリズムで"NETGEN 1D-2D-3D" を選択
--3, その下にある詳細設定右側の歯車をクリックして "NETGEN...
--4, 最大サイズと最小サイズに数値を入れてOKをクリック (最...
--5, メッシュの編集画面に戻るので"適用して閉じる"をクリック
--6, 左側のオブジェクトブラウザーにある"Mesh_1"を右クリッ...
**4/14 [#ja17c906]
-載荷点での荷重、たわみのプロット
1ステップごとに面に載荷した荷重の10分の1を加えたもの...
-軸方向直応力が最大となる点での直応力-直ひずみのプロット...
知りたい方向のEPSI_NOEUーSIGM_NOEUとのグラフを作成 (0...
-その点での相当応力(ミーゼス応力)―相当ひずみのプロット...
知りたい方向のSIEQ_NOEUーEPSI_NOEUのx,y,zの直ひずみ,...
-曲げモーメントが最大となる断面の軸方向直応力$\sigma_{zz}...
曲げモーメント最大は固定端(片持ち梁の場合)なので0に限...
**4/10 [#ja87c906]
-片持ち梁にかける荷重を6.25N以上にするとParavisでtime ste...
-6.25Nで解析を行うと実際の結果が降伏応力に収束しなくなる...
-明日やること
--応力(ミーゼス)-ひずみ,応力-たわみのグラフを作成
--スライドの作成
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/kek...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/kek...
,経過時間,↖たわみ(mm),↖応力(MPa),↗たわみ(mm),↗応力(MPa)
,0,0,0,0,0
,1,0.000140332,34.4925,0.000184238,44.0832
,2,0.000280661,68.9829,0.000368391,88.1643
,3,0.000420986,103.471,0.000552457,132.243
,4,0.000561308,137.957,0.000736434,176.318
,5,0.000701624,172.44,0.000923909,220.147
,6,0.000842636,206.506,0.00123269,241.658
,7,0.00101987,237.23,0.002051,244.44
,8,0.00130101,257.808,0.00765896,243.452
,9,0.00191133,268.432,0.0109555,241.582
,10,0.00377436,280.533,0.0148525,239.658
,11,ー,ー,0.0191575,237.724
,12,ー,ー,0.0237442,235.762
,13,ー,ー,0.0285207,233.804
,14,ー,ー,0.0333983,231.819
,15,ー,ー,0.0383132,229.768
,16,ー,ー,0.043212,227.625
**4/7 [#ja87c006]
-Paravisでのグラフ作成方法
--https://www.rccm.co.jp/icem/pukiwiki/index.php?%E9%81%B...
--https://www.rccm.co.jp/icem/pukiwiki/index.php?%E3%83%9...
--Salomeで荷重をかける段階を設定する際に1.0と入力する箇所...
⇒一度01と入力しOKを押した後再度1.0と入力する画面に戻ると...
--また弾塑性解析を行う際にSalome-Meca演習_弾塑性解析(2021...
⇒原因は関数がある範囲までしか定義されていないの事。Funct...
**2/7 [#ja87c596]
春休みの課題
-弾塑性班
--鋼材の長方形断面の梁(想像しやすい大きさ)の片持ち梁と...
--軸方向:$z$, たわみ方向:$y$
--SS400ぐらいのヤング率と降伏応力で弾塑性解析の設定
--載荷点での荷重、たわみのプロット
--軸方向直応力が最大となる点での直応力-直ひずみのプロット...
--その点での相当応力(ミーゼス応力)―相当ひずみのプロット...
--曲げモーメントが最大となる断面の軸方向直応力$\sigma_{zz...
--上記の三角形分布において、上下縁から徐々に降伏が入って...
--上記を確認できたら、I型断面とかの三角形分布や全塑性が、...
**12/20 [#w0335ea2]
今日はviを用いて論文に画像を貼る方法について学んだ。
**12/13 [#w0335ea3]
今日はviを用いた論文の書き方を学んだ。
・文頭に%をつけるとその行に書かれた文章は反映されない
・強制改行したい場合は"\\"を入力する
・更新→:!pdfplatexsibup2
○式を書く(書き方は編集画面から)
$v=\frac{P\ell^{3}}{48EI} + \frac{P\ell}{4kGA}$
\begin{eqnarray}
v=\frac{P\ell^{3}}{48EI} + \frac{P\ell}{4kGA}
\\
I=\int_{A}y^{2}dA
\end{eqnarray}
**11/29 [#ceb57a7c]
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/san...
今回は鋼材で木材を挟んだサンドイッチ梁の解析を行い、縦軸...
メッシュ長さの大小関係なく理論値と20%の誤差が生じ、これ以...
全員で作成した解析結果のグラフを下に示しておく
,メッシュ長さ,要素数,先端変位(4隅の平均値)[mm],相対誤差($...
,0.7,155419,0.0772,26.943,湊
,0.8,138734,0.0775,26.452,湊
,0.9,82935,0.0774,26.614,湊
,1.1,38671,0.0766,27.937,森井
,1.2,32044,0.0770,27.273,森井
,1.3,28599,0.0768,27.604,森井
,1.4,23950,0.07640,22.04,米谷
,1.5,19998,0.07641,22.03,米谷
,1.6,19448,0.07715,21.28,米谷
,1.7,13801,0.07567,22.79,米谷
,1.8,12677,0.07736,21.06,沼野
,1.9,11464,0.07546,23.00,沼野
,2,10699,0.07404,24.45,沼野
,3,3579,0.08414,15.004,國井
,4,1628,0.08279,16.37,國井
,5,1016,0.08303,16.26,國井
,6,839,0.08288,16.26,西澤
,7,554,0.08087,18.28,西澤
,8,285,0.07898,19.20,西澤
,9,261,0.01421,85.49,真庭
,10,232,0.03380,65.51,真庭
,11,208,0.00913,90.68,真庭
**11/22 [#ceb57a7d]
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/kad...
今回は単純異方性と等方性の解析を行い、縦軸に変位(mm), 横...
全員で作成した解析結果のグラフを下に示しておく
,メッシュ長さ,要素数,変位(異方性)[mm],相対誤差-異方性($\f...
,0.7,171996,0.5068,2.993,0.4301,3.141,湊
,0.8,161561,0.5069,2.999,0.4300,3.116,湊
,0.9,94185,0.5021,2.071,0.4301,3.139,湊
,1.1,47998,0.4957,0.814,0.4122,1.056,森井
,1.2,47343,0.4952,0.712,0.4300,3.217,森井
,1.3,42112,0.4941,0.488,0.4298,3.169,森井
,1.4,38960,0.4937,0.407,0.4299,3.193,森井
,1.5,15041,0.4845,1.460,0.4298,3.179,米谷
,1.6,16071,0.4849,1.380,0.4298,3.157,米谷
,1.7,12933,0.4845,1.460,0.4299,3.182,米谷
,1.8,12993,0.4832,1.73,0.4298,3.19,沼野
,1.9,11235,0.4783,2.73,0.4295,3.10,沼野
,2,11456,0.4982,1.32,0.4296,3.12,沼野
,3,2514,0.4369,4.87,0.4293,3.05,國井
,4,1461,0.4341,4.20,0.4293,3.05,國井
,5,433,0.2803,32.7,0.4284,2.83,國井
,6,356,0.4283,2.80,0.3437,17.5,西澤
,7,102,0.4260,2.26,0.2225,46.6,西澤
,8,93,0.4260,2.26,0.1123,73.0,西澤
,9,81,0.2212,54.9,0.4255,2.13,真庭
,10,84,0.2051,58.3,0.4247,1.95,真庭
,11,74,0.2260,54.0,0.4246,1.91,真庭
**11/15 [#ceb57a7e]
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/kad...
単純梁の解析結果から縦軸に変位(mm), 横軸にボリューム数を...
前回と同じくメッシュの長さを長くすると接点変位は小さくな...
全員で作成した解析結果のグラフを下に示しておく
,メッシュ長さ,要素数,先端変位(4隅の平均値)[mm],相対誤差($...
,0.7,171996,0.4260,2.207,湊
,0.8,161561,0.4256,2.115,湊
,0.9,94185,0.4169,0.0719,湊
,1.1,47998,0.4122,1.067,森井
,1.2,47343,0.4118,1.166,森井
,1.3,42112,0.4113,1.289,森井
,1.4,38960,0.4112,1.313,森井
,1.5,15041,0.3978,4.516,米谷
,1.6,16071,0.3999,4.002,米谷
,1.7,12993,0.3971,4.687,米谷
,1.8,12203,0.3964,4.85,沼野
,1.9,11235,0.3942,5.38,沼野
,2,11456,0.3991,4.20,沼野
,3,2514,0.2141,21.4,國井
,4,1461,0.34028,18.4,國井
,5,433,0.1354,67.8,國井
,6,356,0.2135,48.8,西澤
,7,102,0.11,73.6,西澤
,8,93,0.112,73.0,西澤
,9,81,0.1125,73.0,真庭
,10,84,0.0794,80.9,真庭
,11,74,0.1297,68.9,真庭
**11/8 [#ceb57a7f]
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/kad...
全員で行った解析の結果を縦軸に変位, 横軸にボリューム数を...
メッシュの長さを長くすると接点変位は小さくなり、相対誤差...
一方でメッシュの長さを小さくするほど接点変位は断面二次モ...
メッシュ長さを0.5や0.3として解析を行えばもっと理論値に値...
全員で作成した解析結果のグラフを下に示しておく
,メッシュ長さ,要素数,先端変位(4隅の平均値)[mm],相対誤差($...
,0.7,107380,6.47,2.96,湊
,0.8,57821,6.44,3.62,湊
,0.9,57698,6.43,3.73,湊
,1.1,57980,6.44,3.57,湊
,1.2,52123,6.41,3.90,森井
,1.3,45549,6.34,4.98,森井
,1.4,26951,6.32,5.31,森井
,1.5,16904,6.25,6.32,米谷
,1.6,14296,6.20,7.05,米谷
,1.7,13596,6.21,6.81,米谷
,1.8,6299,5.74,13.9,沼野
,1.9,6001,5.73,14.1,沼野
,2,5617,5.65,15.3,沼野
,3,2309,5.48,17.8,國井
,4,617,3.62,45.6,國井
,5,494,3.85,42.3,國井
,6,581,2.51,62.4,西澤
,7,133,1.41,78.8,西澤
,8,78,1.29,80.7,西澤
,9,72,1.288,80.69,真庭
,10,60,1.226,81.62,真庭
,11,65,1.231,81.54,真庭
**11/1 [#w68ec49g]
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/abc...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/123...
今日(11/1)はwikiにグラフの貼り付けとviとlinuxを使ってグラ...
上のグラフは自身で作成したもの、下のグラフは過去の先輩方...
先輩方が作成したグラフの数値を下に示しておく
,メッシュの長さ ,要素数 ,変位[mm],相対誤差,計算者
,0.7,155192,0.08378905246,15.365,安藤
,0.8,138808,0.08380386491,15.350,安藤
,0.9,82587,0.083707073981,15.45,兼田
,1.1,38671,0.084201207602,14.95,兼田
,1.2,31929,0.083688,15.466,柴田
,1.3,28621,0.083669,15.4857,柴田
,1.4,28854,0.08368,15.47,佐藤
,1.5,20015,0.084052,15.10,佐藤
,1.6,19448,0.0835402938,15.62,皆川
,1.7,13801,0.0834355098,15.72,皆川
,1.8,12528,0.083733,15.42,永山
,1.9,11769,0.083924,15.23,永山
,2,10699,0.084076876559,15.074,辻
,3,3579,0.08414561753,15.004,辻
,4,1628,0.082794,16.37,服部
,5,1016,0.083033,18.89,服部
,6,839,0.082882,16.26,梶原
,7,554,0.080871,18.28,梶原
,8,285,0.079995,19.20,工藤
,9,261,0.078980,20.22,工藤
,10,232,0.081911,17.26,佐々木
,11,208,0.075676,23.56,佐々木
**10/4 [#w0335eb3]
今日は顔合わせをした
頑張りたい
"いきものがかり"についた
終了行:
&aname(Top);
#contents
*卒論 [#v045a40a]
**4月 [#j417c400]
***4月第4週(4/23〜4/30) 手書き数字の判別 [#j417c423]
卒論テーマが決まった。
耐候性鋼橋の耐候性鋼材の錆の現地調査(今年度から)(日本鉄...
-jpg, ppmファイルから1点ごとのRGB値を確認する方法
--1,写真フィルを選択して"gimpで開く"をクリックする。
--2,gimpが開けたら上にあるバーから "ウィンドウ" → "ドッキ...
-MATE端末のコマンド
--1, locate (ファイル名等) → (ファイル名)がどこにあるのか...
--2, gfortran -o (ファイル名) (ファイル名.拡張子) → ある...
--3, history → MATE端末内で過去にどんなコマンドを用いたか...
--4, hg (ファイル名等) → MATE端末内でどの部分に(ファイル...
-明日までにやっておくべきこと
--佐藤さんの卒論日誌をはじめ耐候性鋼材にまつわるウィキを...
-これからしばらくはpythonになれることが必要 Python上で佐...
-【質問内容】
--最初に取り組むべきステップは何か?
機械学習を使うにはどんなデータ整理や前処理が必要かがわか...
--過去の先輩方の研究でどの部分を参考にするのがよいか?
佐藤卒論日誌やRGBメモの中で、現在の作業と関連が深い部分が...
--MATE端末からpythonを開くには → python と入力すれば入る...
--MATE端末から1つ上のディレクトリに上がりたい(戻りたい)場...
-python上で ホーム / hikitugi 内に保存された.pyファイルを...
--MATE端末にもどり( python の状態から脱出する) "python フ...
--hikitugi内には RGBrironti.py , edge.py , henkan.py , ma...
-RGBrironti.py について (コマンド)
--1, MATE端末を開き py ファイルのある hikitugi に移動する...
--2, py ファイルを呼び出す (python3 RGByomitori.py) [3は ...
--3, フォルダーのパス入力が求められるので錆の写真があるフ...
--4, 保存するファイル名を入力する → Enterキー (一例: RGBs...
--5, その後ウィンドウのどこかしらに錆の画像が表示されるの...
画像: ファイル名(.ppm) | クリック位置: (x座標, y座標), R...
と表示される。またフォルダーパスの入力をしたファイル内に4...
--6, 文書ファイルを開くと写真のようにMATE端末に表示されて...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--7, 写真の左下に写っている錆の写真は矢印キー(←, →)でフォ...
--補足:保存するファイル名を前に作成したファイル名と同じに...
-edge.py について(コマンド)
--1, MATE端末を開き py ファイルのある hikitugi に移動する...
--2, py ファイルを呼び出す (python3 edge.py) [3は ver の...
--3, 入力ディレクトリパスの入力が求められる。なお edge.py...
--4, 出力ディレクトリパスの入力が求められる。ここでは3で...
---/edge-syuturyoku0424はエッジ処理したものを入れておくフ...
--5, エッジ処理の閾値1, 2を入力してくださいと表示される
---エッジ処理の閾値1は "Cannyエッジ検出の「低い方のしきい...
---エッジ処理の閾値2は "Cannyエッジ検出の「高い方のしきい...
--6, 閾値1, 2 を入力して Enterキー を押してしばらくすると...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/202...
-明日までにやっておくべきこと
--残りのPythonファイルについて使い方を学ぶ
-svm.py について (コマンド)
--0, はじめに
--- svm = "s"upport-"v"ector-"m"achine のこと
--1, py ファイルを呼び出す前に svm.py ファイルを開く
--2, ファイルを開いたら13行目に書いてある data_dir = 〜 ...
---デフォルトでは data_dir = "edge/250_300_edge" となって...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
この写真の場合 "mk1_pgm"等が入っている1番左にある "250_3...
-henkan.py について (コマンド)
--1, MATE端末を開き py ファイルのある hikitugi に移動する...
--2, py ファイルを呼び出す (python3 henkan.py) [3は ver ...
--3, 変換行列ファイルのパスを入力してください と表示され...
--- 変換行列ファイルは3 ☓ 3行列の .txtファイルで表されて...
---henkanm内にある 1_henkanm ~ 4_henkanmの .txt の名前と ...
--4, 補正したいPPM画像のパスを入力してください と表示され...
---基本的に/home/kouzou/sato24/gr/data内にある ○_ppm や h...
--5, 補正後の画像を保存するパスを入力してください と表示...
---変換行列ファイルはかなり細かい値まで書かれているため...
-matrix.py について (コマンド)
--0, はじめに
このpythonファイルは henkan.py で必要になる変換行列ファ...
--1, MATE端末を開き py ファイルのある hikitugi に移動する...
--2, py ファイルを呼び出す (python3 matrix.py) → Enterキー
--3, 結果を書き込むファイルを入力してください と表示され...
--4, シアン・イエロー・マゼンタの各RGB値を入力してくださ...
---各RGB値を入力するにあたりなにか根拠となるものがあるは...
-来週中にやること
--サポートベクターマシーン(優先)と畳込みニューラルネット...
--このページをまとめる(5つのpythonファイルをどういう順序...
--佐藤さんへの質問をまとめておく
-5つあるpythonファイルの使い方についてとそれぞれの役割
-錆の写真を撮影してそれをサポートベクターマシンを用いるま...
-SVMについて
-佐藤さんが研究していたファイル達(sato2024)は各フォルダー...
-佐藤さんから錆の写真からサポートベクターマシンを用いて用...
--耐候性鋼材のページに上記の事について詳しくまとめてある。
--機械学習のページは何か発見があったら随時更新していく必...
-来週までにやること
--畳み込みニューラルネットワークについて調べる
--佐藤さんのファイル内にある手書き数字を使って機械学習
--それに加え動物の顔(一例)が判別できるかについても機械学...
-畳込みニューラルネットワークを用いて手書き数字の機械学習
--手書き文字の機械学習は mnist という手書き数字を認識する...
--1, MATE端末で "pip install tensorflow" と入力しEnterキ...
--2, エラーが出なかったらmnistの画像データをCNNに読み込ま...
Pythonコード
--3, しばらくすると正答率が表示される。だいたい99%前後の...
-ある手書き文字を読み込ませて判別させる
--1, mnist等から28×28ピクセルで書いた数字のpngやjpgファイ...
Pythonコード
--2, Enterキーを押せば判別してもらえる。
-複数枚の手書き文字を一度に判別させたい場合
--1, 複数枚ある手書き数字をどこか1つのフォルダーにまとめ...
--2, Enterキーを押せば一度に複数枚判別してもらえる。
Pythonコード
---11行目で予め画像を保存していたフォルダー名を入力する
---12行目で画像ファイル形式を入力する
---7と書いた画像(右の写真)を読み込ませたところ0と出力され...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-畳込みニューラルネットワーク(CNN)について
--特に画像認識や画像分類で優れた性能を発揮するディープニ...
-層の用語について
--畳み込み層 … 画像におけるエッジや色の変化といった局所的...
--プーリング層 … 画像の特徴と位置を紐付けてしまうとCNNの...
--全結合層 … 畳み込み層・プーリング層で抽出された特徴から...
,手順 ,実行内容
,1,画像を入力する(ピクセルで)
,2,畳み込み層で画像の特徴を検出する
,3,プーリング層で重要な情報を抽出する
,4,2と3を繰り返す
,5,全結合層で画像の特徴をもとに分類を行う
,6,出力する
-CNNとSVMの違いについて
, 比較項目 , SVM , CNN
, モデル , すでに用意された特徴(数値)をもとにクラス分け...
, 入力 , 数値データ (特徴量ベクトル) → 画像を数値に変換す...
, 特徴の抽出方法 , 手動(人があらかじめ決めた特徴) ,自動...
, モデル構造 , 数学的な式(境界面) ,多層のニューラルネッ...
, 適用例 , 小規模なデータで分類(例:文字分類、小さな画像...
, 学習の難易度 , 比較的簡単(実装も軽い) ,やや難しい(GP...
, 処理速度 , 小規模なら早い ,訓練に時間がかかる(推論は速...
--SVM → 与えられた特徴を元に最適な境界線で分類する方法。...
--CNN → 画像の中から重要な特徴を自分で見つけて分類してく...
**5月 [#j417c500]
***5月第1週(5/1〜5/7) [#j417c502]
-手書き数字の機械学習(CNN, 判別)について
--mnistのテストデータに入っている数字を判別させたところ正...
--mnist以外の数字画像はまだ正確に判別できているとはいえず...
--また mnist.py または mnist-hosei.py を何度もMATE端末上...
-mnistの画像を少し補正
--今までは画像の中心に数字を書いて機械学習を行ってきたが...
,学習方法 ,テストデータ正答率(%) ,判別精度(%)
,そのままの状態で画像を学習 ,99.14 ,44 (4/9)
,画像を回転・移動・拡大・変形させて学習 ,98.49 ,67 (6/9)
-エポック数を増やして学習させてみた
---そのままの状態で画像を学習させた時
,エポック数 ,テストデータ正答率(%) ,判別精度(%)
,10 ,99.20 ,67 (6/9)
,12 ,99.13 ,56 (5/9)
,13 ,99.26 ,89 (8/9)
,14 ,99.19 ,89 (8/9)
,15 ,99.16 ,89 (8/9)
,16 ,99.08 ,89 (8/9)
--エポック数が13を超えると判別精度が向上したがmnistにはな...
--4の画像を識別させたときにどれくらいの確率でどの数字であ...
---画像を回転・移動・拡大・変形させて学習させた時
,エポック数 ,テストデータ正答率(%) ,判別精度(%)
,10 ,95.47 ,56 (5/9)
,12 ,87.01 ,56 (5/9)
,13 ,90.55 ,56 (5/9)
,14 ,96.15 ,56 (5/9)
,15 ,86.66 ,56 (5/9)
,16 ,74.12 ,56 (5/9)
--エポック数の大小に関わらず判別精度は変化せず、特に5, 7,...
-来週にやること
--GW中に手書き数字の機械学習は終わらせたい
---自身で手書き数字を書いてみてそれがどのくらいの精度であ...
--動物の顔(仮)が機械学習を通じて判別できるようになるスク...
---とりあえず多くの動物の顔の写真が載っているサイトを探す。
-MNISTについて
--MNISTを用いた手書き文字の画像判別はどのサイト・ホームペ...
--限りなく100%の正解率に持っていく手法はあるだろうが正解...
-手書き数字の機械学習 (続き)
--とりあえず自身で作成した手書き数字を機械学習に判別させ...
,数字 ,モデル数 ,正答率
,0 ,3 ,3/3
,1 ,5 ,5/5
,2 ,6 ,6/6
,3 ,6 ,6/6
,4 ,2 ,2/2
,5 ,10 ,9/10
,6 ,2 ,1/2
,7 ,3 ,2/3
,8 ,2 ,1/2
,9 ,13 ,9/13
本来は各数字のモデル数が揃っていることが望ましいが、こち...
-来週までにやること
--手書き数字の画像に点や線、枠を入れても正確に判別できる...
--自動的に画像内のどこか適当なところに点や図形を自動的に...
--動物の顔を使った機械学習(できたら)
--自分の研究についてスライドを3枚程度作成する
-明日にやること
--数字に点や線, 図形を加えた結果の表を作成
--使用した画像を載せる
--色を白ではなく他の色にするとどうなるか (エラーを吐き出...
***5月第2週(5/8〜5/14) [#j417c508]
-画像に数字に点や線, 図形を加えてみた。これを判別してみる
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
Pythonコード (Gazou-Kakou.py)
-- 0 ~ 9 の画像数をそれぞれ9枚に統一させた。表の真ん中の...
--表の右側の列は数字に点や線, 図形のすべてを画像に加え数...
,数字 ,正答率 ,正答率
,0 ,9/9 ,9/9
,1 ,8/9 ,1/9
,2 ,9/9 ,6/9
,3 ,9/9 ,6/9
,4 ,9/9 ,0/9
,5 ,9/9 ,5/9
,6 ,5/9 ,2/9
,7 ,8/9 ,4/9
,8 ,6/9 ,6/9
,9 ,6/9 ,0/9
---数字は太くはっきり書いてあること且つ線や図形と被ってい...
---ただ要素を加えすぎたせいか無加工と比べてかなり判別精度...
-改良版
--画像を10枚に統一させてフォントを太くした。以下の表はそ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
,数字 ,正答率
,0 ,10/10
,1 ,10/10
,2 ,10/10
,3 ,10/10
,4 ,10/10
,5 ,10/10
,6 ,10/10
,7 ,10/10
,8 ,10/10
,9 ,10/10
--数字を中心に持ってきたからなのかまたはほとんど形が変わ...
--次に点, 線, 図形のうち1つの要素だけを画像に加え数字ごと...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
,数字 ,点 正答率 ,線 正答率 ,図形 正答率(リスタート前) ,...
,0 ,10/10 ,9/10 ,8/10 ,10/10
,1 ,10/10 ,4/10 ,5/10 ,6/10
,2 ,10/10 ,9/10 ,8/10 ,10/10
,3 ,10/10 ,7/10 ,9/10 ,10/10
,4 ,10/10 ,9/10 ,8/10 ,8/10
,5 ,10/10 ,9/10 ,10/10 ,10/10
,6 ,8/10 ,3/10 ,1/10 ,3/10
,7 ,10/10 ,7/10 ,7/10 ,8/10
,8 ,10/10 ,10/10 ,10/10 ,10/10
,9 ,10/10 ,7/10 ,7/10 ,6/10
--点は1画像あたり50個加えた(Gazou-Kakou-Ten_only.py を...
Pythonコード (Gazou-Kakou-Ten_only.py)
--線は1画像あたり5本加えた(Gazou-Kakou-Sen_only.py を使...
Pythonコード (Gazou-Kakou-Sen_only.py)
--図形は1画像あたり三角形から十角形までの多角形を1〜3...
Pythonコード (Gazou-Kakou-Zukei_only.py)
-判別させたい手書き文字を作成するには
--① GIMPで直接書きたい場合 (推奨)
---1, GIMPを開く
---2, 上のツールバーから ファイル → 新しい画像 の順にクリ...
---3, 左側に下のようなアイコンが現れたと思うがこれは背景...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
---4, アイコンの下にはサイズ, 縦横比, 角度, .... と大きさ...
---5, 文字を書き直したいときはアイコンの上にある消しゴム...
---6, 書き終えたら保存し、その際にどこに保存するかを指定...
---7, 保存はxcfファイルでされるため一度pngに変換する必要...
---※スクリプトでxcfファイルを直接判別してもらえるような文...
--② AIに書かせる場合 (ChatGPT)
--1, ChatGPTに"黒背景に白の線で 0 ~ 9 の数字を各5枚、合計...
--2, 画像が作成されるまで1, 2時間かかる場合があるためその...
--3, 生成された画像は複数の文字(数字)が1枚の画像となって...
--4, 分割できたら各画像を保存 → png に変換 → 28×28ピクセ...
-画像に点, 線, 図形をそれぞれ加えたときの正答率について
--点を加えたときに関してはかなり精度の高い判別ができたの...
--線を加えると判別精度は点の時と比較してやや劣るが、その...
--最後に図形を加えた場合数字によって精度のばらつきがある...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-次回やること
--MNISTで学習させてからある数字の画像を何と判別したかをフ...
→大まかな内容は完了しこれから肉付け(5/11)
--動物の顔を使った機械学習(できたら)
-とりあえずmnistで学習させた後 "判別したい画像を1つのフォ...
-全自動スクリプトにmnistで学習させる作業を入れなかった理...
Pythonスクリプト (Hanbetsu-Full_auto.py)
-上記のスクリプトを実行すると "既存の画像を加工したフォル...
--テキストファイルの中身
,0_7.png 予測: 0 正解: 0 一致: 〇
,1_5.png 予測: 8 正解: 1 一致: ×
,3_5.png 予測: 3 正解: 3 一致: 〇
,8_2.png 予測: 8 正解: 8 一致: 〇
,…
,--- 数字ごとの正答率 ---
,0: 正解 ○ / □ 枚 → 正答率 △ %
,1: 正解 △ / ○ 枚 → 正答率 □ %
, …
--以下の表は全自動スクリプトを3回用いて判別させた数字ごと...
,数字 ,正答率 ,正解率 ,-正答率- ,-正解率- ,--正答率-...
,0 ,10/10 ,100.00% ,10/10 ,100.00% ,10/1...
,1 ,0/10 ,0.00% ,8/10 ,80.00% ...
,2 ,10/10 ,100.00% ,5/10 ,50.00% ,...
,3 ,10/10 ,100.00% ,10/10 ,100.00% ,10...
,4 ,6/10 ,60.00% ,4/10 ,40.00% ...
,5 ,10/10 ,100.00% ,10/10 ,100.00% ,10...
,6 ,0/10 ,0.00% ,0/10 ,0.00% ...
,7 ,10/10 ,100.00% ,1/10 ,10.00% ...
,8 ,10/10 ,100.00% ,10/10 ,100.00% ,10...
,9 ,10/10 ,100.00% ,10/10 ,100.00% ,10...
Pythonスクリプト (Mnist-Hanbetsu-Full_auto.py)
-上記のスクリプトで出来ること
--mnistの数字画像を用いて学習し、その結果をh5ファイルに保...
--判別させたい数字画像に図形を加える等の加工をする。
--加工した画像を見られるように別のフォルダーを作成して保存
--加工した図形を判別させて合っていたか、また数字別の正解...
--→判別させたい画像をフォルダーに入れておき Mnist-Hanbets...
--以下の表は"シン"・全自動スクリプトを3回用いて判別させた...
,数字 ,正答率 ,正解率 ,-正答率- ,-正解率- ,--正答率-...
,0 ,10/10 ,100.00% ,10/10 ,100.00% ,10/10 ,...
,1 ,0/10 ,0.00% ,0/10 ,0.00% ...
,2 ,8/10 ,80.00% ,9/10 ,90.00% ,1...
,3 ,10/10 ,100.00% ,7/10 ,70.00% ,8/10...
,4 ,8/10 ,80.00% ,9/10 ,90.00% ,1...
,5 ,10/10 ,100.00% ,10/10 ,100.00% ,10/10 ...
,6 ,1/10 ,10.00% ,4/10 ,40.00% ,0...
,7 ,2/10 ,20.00% ,7/10 ,70.00% ,...
,8 ,10/10 ,100.00% ,10/10 ,100.00% ,1/10 ...
,9 ,10/10 ,100.00% ,9/10 ,90.00% ,10/...
--3回とも正答率が100%である数字もあれば判別を1回行うごと...
-2種類の全自動スクリプトで判別に使用した画像(画像加工前)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
Pythonスクリプト (Mnist-Hanbetsu-Full_auto2.py)
-上記のスクリプトの違い
--mnistの中にある画像を加工して学習させた (加工方法と内...
--以下の表は Mnist-Hanbetsu-Full_auto2.py を用いて判別さ...
,数字 ,正答率 ,正解率 ,-正答率- ,-正解率- ,--正答率-...
,0 ,15/15 ,100.00% ,15/15 ,100.00% ,15/15 ,100.00%
,1 ,15/16 ,93.75% ,15/16 ,93.75% ,16/16 ,100.00%
,2 ,15/16 ,93.75% ,16/16 ,100.00% ,15/16 ,93.75%
,3 ,16/16 ,100.00% ,16/16 ,100.00% ,16/16 ,100.00%
,4 ,15/16 ,93.75% ,15/16 ,93.75% ,15/16 ,93.75%
,5 ,15/16 ,93.75% ,15/16 ,93.75% ,15/16 ,93.75%
,6 ,12/16 ,75.00% ,6/16 ,37.50% ,8/16 ,50.00%
,7 ,15/16 ,93.75% ,14/16 ,87.50% ,16/16 ,100.00%
,8 ,16/16 ,100.00% ,15/16 ,93.75% ,15/16 ,93.75%
,9 ,15/16 ,93.75% ,16/16 ,100.00% ,15/16 ,93.75%
--今までと比べて作業効率も識別の精度も上がっていると思う...
--どのスクリプトを実行しても"6"も識別が上手くいかない。明...
-明日やること
--日誌に画像を貼り付ける
--mnistから抽出したデータで判別させる
--明後日に向けて自身の研究内容を英語で書いておく
-mnistから抽出したデータで判別
--判別する画像の対象を以下の画像に変更した。(今までよりも...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--以下の表は全自動スクリプトを用いて判別させた数字ごとの...
,数字 ,正答率 ,正解率 ,正答率 ,正解率 ,正答率 ,正解...
,0 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,1 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,2 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,3 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,4 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,5 ,19/20 ,95.00% ,20/20 ,100.00% ,18/20 ,90.00% ...
,6 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,7 ,17/20 ,85.00% ,20/20 ,100.00% ,19/20 ,95.00% ...
,8 ,20/20 ,100.00% ,20/20 ,100.00% ,20/20 ,100.00% ,...
,9 ,19/20 ,95.00% ,18/20 ,90.00% ,19/20 ,95.00% ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--全体的に高い判別精度が得られ0〜4と 6, 8はすべての回で10...
--精度をさらに上げるためには
---ノイズの位置をランダムではなく重要領域を避けるように設...
---Dropoutの実装 (過学習の防止とアンサンブル効果を得るため)
---混同行列を作成しどの数字が判別に弱いのかを確認する
-アンサンブル効果とは
個々の学習モデルにはそれぞれ異なる「誤り」や「予測の癖」...
Pythonスクリプト (Hanbetsu-Full_auto3.py)
--画像中央付近への点の描画を減らした。
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--混同行列ををpng形式で表示・保存できるように設定した。
--以下の表はそのスクリプトを用いて判別させた数字ごとの正...
,数字 ,正答率 ,正解率 ,正答率 ,正解率 ,正答率 ,正解...
,0 ,20/20 ,100.00% ,20/20 ,100.00% ,20...
,1 ,20/20 ,100.00% ,19/20 ,95.00% ,1...
,2 ,20/20 ,100.00% ,20/20 ,100.00% ,19...
,3 ,20/20 ,100.00% ,20/20 ,100.00% ,20...
,4 ,20/20 ,100.00% ,20/20 ,100.00% ,20...
,5 ,20/20 ,100.00% ,19/20 ,95.00% ,1...
,6 ,20/20 ,100.00% ,20/20 ,100.00% ,20...
,7 ,19/20 ,95.00% ,19/20 ,95.00% ...
,8 ,20/20 ,100.00% ,20/20 ,100.00% ,20...
,9 ,20/20 ,100.00% ,19/20 ,95.00% ,...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-上記の行列の見方
--縦軸は True Label (画像に書いてある実際の数字 = 答え), ...
--濃い青で塗りつぶされていない部分は機械が実際の数字とは...
--1番右の Recall は数字別の判別率を表している。
自身の研究内容について
研究テーマ : 耐候性鋼橋の耐候性鋼材の錆の現地調査に参加し...
About his own research
Research theme : While participating in a field survey of...
耐候性鋼材の錆の調査は来週の水曜日に行く予定であり、この...
The investigation of rust on weather resistant steel is s...
1, 画像処理を加えてCNNで学習させたもの
1, The images of mnist, a data set containing nearly 60,0...
使用したデータ Data used(example) &ref(http://www.str.ce...
,数字 ,正答率 ,正解率
,number ,correct response rate ,correct response rate
,0 ,9/9 ,9/9
,1 ,8/9 ,1/9
,2 ,9/9 ,6/9
,3 ,9/9 ,6/9
,4 ,9/9 ,0/9
,5 ,9/9 ,5/9
,6 ,5/9 ,2/9
,7 ,8/9 ,4/9
,8 ,6/9 ,6/9
,9 ,6/9 ,0/9
2, 判別したい画像を以下のように変更し 判別する画像の加工,...
2, Change the image to be discriminated as follows, proce...
,数字 ,正答率 ,正解率
,number ,correct response rate ,correct response rate
,0 ,10/10 ,100.00%
,1 ,0/10 ,0.00%
,2 ,10/10 ,100.00%
,3 ,10/10 ,100.00%
,4 ,6/10 ,60.00%
,5 ,10/10 ,100.00%
,6 ,0/10 ,0.00%
,7 ,10/10 ,100.00%
,8 ,10/10 ,100.00%
,9 ,10/10 ,100.00%
3, 2で用いたスクリプトはmnistをCNNで用いて学習させる機能...
3, The script used in 2, did not have the ability to trai...
,数字 ,正答率 ,正解率
,number ,correct answer rate ,correct answer rate
,1 ,0/10 ,0.00%
,2 ,8/10 ,80.00%
,3 ,10/10 ,100.00%
,4 ,8/10 ,80.00%
,5 ,10/10 ,100.00%
,6 ,1/10 ,10.00%
,7 ,10/10 ,100.00%
,8 ,10/10 ,100.00%
,9 ,10/10 ,100.00%
4, 判別したい画像に加工処理を施すだけでは正答率を100%に近...
4, Since there is a limit to achieving a correct response...
,数字 ,正答率 ,正解率
,number ,correct answer rate ,correct answer rate
,0 ,15/15 ,100.00%
,1 ,15/16 ,93.75%
,2 ,15/16 ,93.75%
,3 ,16/16 ,100.00%
,4 ,15/16 ,93.75%
,5 ,15/16 ,93.75%
,6 ,12/16 ,75.00%
,7 ,15/16 ,93.75%
,8 ,16/16 ,100.00%
,9 ,15/16 ,93.75%
Data used in 2-4 (example)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
5, 判別したい画像をmnistから抽出したデータに変更した。2~4...
5, The image to be discriminated was changed to the data ...
,数字 ,正答率 ,正解率
,number ,correct answer rate ,correct answer rate
,0 ,20/20 ,100.00%
,1 ,20/20 ,100.00%
,2 ,20/20 ,100.00%
,3 ,20/20 ,100.00%
,4 ,20/20 ,100.00%
,5 ,20/20 ,100.00%
,6 ,20/20 ,100.00%
,7 ,20/20 ,100.00%
,8 ,20/20 ,100.00%
,9 ,18/20 ,90.00%
Data used(example)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
6, 数字の書いてある部分(画像の中心部分)になるべく白い点を...
6, I tried to avoid white dots in the numbered area (cent...
,数字 ,正答率 ,正解率
,number ,correct answer rate ,correct answer rate
,0 ,20/20 ,100.00%
,1 ,20/20 ,100.00%
,2 ,20/20 ,100.00%
,3 ,20/20 ,100.00%
,4 ,20/20 ,100.00%
,5 ,20/20 ,100.00%
,6 ,20/20 ,100.00%
,7 ,19/20 ,95.00%
,8 ,20/20 ,100.00%
,9 ,20/20 ,100.00%
Data used(example)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
***5月第3週(5/15〜21) 物体分類判別 [#j417c515]
-判別をするにあたって以下の要素を加えた
--mnistの画像を加工した状態で機械学習を行う
--学習の偏りを避けるため複数の学習モデルを構築 ( h5 → ker...
--過学習抑制のためDropout関数の実装
--エポック数を増やす
--機械学習によてどの数字が判別に弱いのかを知るために混同...
--判別させる画像にも加工を加える
--最良のモデルを保存するためにEarlyStoppingとModelCheckpo...
これら全てを取り込んだのが以下の Ver5.0 = Mnist-Hanbetsu-...
Pythonコード (Mnist-Hanbetsu-Full_auto5.py)
,数字 ,正答率 ,正解率 ,正答率 ,正解率 ,正答率 ,正解...
,0 ,19/20 ,95.00% ,19/20 ,95.00% ,18/20 ,90.00%
,1 ,8/20 ,40.00% ,19/20 ,95.00% ,6/20 ,30.00%
,2 ,19/20 ,95.00% ,19/20 ,95.00% ,19/20 ,95.00%
,3 ,20/20 ,100.00% ,18/20 ,90.00% ,19/20 ,95.00%
,4 ,18/20 ,90.00% ,18/20 ,90.00% ,20/20 ,100.00%
,5 ,19/20 ,95.00% ,18/20 ,90.00% ,20/20 ,100.00%
,6 ,20/20 ,100.00% ,20/20 ,95.00% ,20/20 ,100.00%
,7 ,18/20 ,90.00% ,15/20 ,75.00% ,19/20 ,95.00%
,8 ,17/20 ,85.00% ,20/20 ,100.00% ,17/20 ,85.00%
,9 ,18/20 ,90.00% ,19/20 ,95.00% ,18/20 ,90.00%
Mnist-Hanbetsu-Full_auto4.py を実行したときと比較して明ら...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
下記の内容が判別精度低下の主な原因ではないかと考えている。
--学習方法を同一のCNNモデルから3種類のCNNモデルへ変更(ア...
--間違えて判別した画像を再学習させて精度の向上を図る
---1, 複数の学習方法の統合によるモデルの一貫性の欠如
→アンサンブル学習を行なうため3つの学習モデルで判別を行っ...
---2, 誤判別画像の再学習による過学習や偏り
→誤判別された画像のみを再学習させることで過剰に最適化され...
---3, スクリプトの構成が複雑化したことでデータ処理や前処...
→スクリプトの行が多くなるとどうしても学習データと推論デー...
-使用したCNNモデルについて
,要素 ,モデル① ,モデル② ,モデル③
,畳み込み層数 ,1 ,2 ,2
,カーネルサイズ ,3×3 ,3×3(2回) ,5×5 → 3×3
,プーリング ,あり(1回) ,あり(1回) ,あり(1回)
,Dropout ,なし ,あり(0.3) ,あり(0.25・0.5)
,BatchNorm ,なし ,あり(1回) ,あり(1回)
,全結合層 ,64ユニット ,128ユニット ,64ユニット
以下は全5バージョンのMnist-Hanbetsu-Full_autoを実行したと...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
以上より手書き数字の画像判別はあまり要素を盛り込みすぎる...
物体分類の判別
-以下のサイト (CIFAR-10) にあるデータを用いて画像識別を行...
https://www.cs.toronto.edu/~kriz/cifar.html
-CIFAR-10には飛行機, 自動車, 鳥, 猫, 鹿, 犬, カエル, 馬, ...
-Pythonスクリプトの実装
--とりあえずCIFAR-10上にある画像で学習を行い混同行列を作...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
左側がエポック数20, 右側がエポック数50としたがあまり精度...
-来週やること
--Animal.pyを改良して精度が向上するか確かめる
--今日はただCIFAR-10上にある画像を学習させただけなのでテ...
-物体分類の判別について最終的な目標として
--1, 学習する画像には特に加工を加えることなく
--2, テストデータから何枚か画像を抽出して判別をさせて
--3, 10種類ごとに正答率の割合を算出することを目的とする。
とりあえず
-① 学習画像は無加工とし混同行列を表示させるスクリプトと
-② テストデータから画像を抽出するスクリプト,
-③ 学習データを用いてテストデータを判別させるスクリプト
を組んでみた。3つとも独立(手動)で実行させてみて特に気にな...
--③について①で作成された学習データをもとに判別した結果を...
,タグ ,正答率
,airplane ,90.00 %
,automobile ,100.00 %
,bird ,85.00 %
,cat ,80.00 %
,deer ,95.00 %
,dog ,55.00 %
,frog ,95.00 %
,horse ,100.00 %
,ship ,100.00 %
,truck ,95.00 %
Pythonコード① (Animal.py)
Pythonコード② (Yobidashi.py)
Pythonコード③ (Buttai-Bunrui.py)
-1回の学習 (.py を実行させてから全て出力し終えるまで) で1...
--エポック数を1にして学習させれば20分ぐらいで結果が出力さ...
-その間に明日から本格的に始動する研究に向けて耐候性鋼材の...
目標に向けて大まかなステップ
--1, 100 枚/クラス を目標にデータ収集&CSV ラベリング
--2, .img .jpg形式で整理
--3, 整理した画像を 転移学習+EarlyStopping で試走
--4, 混同行列 と Grad‑CAM で失敗要因を可視化
--5, クラスあたりの枚数を増やし誤判定画像を追加収集 → 再...
目標を達成するために
-1. データ収集 … 構造物の部位や照明条件, 距離を揃えて撮影...
-- → 作業現場でスマホ等で撮影 → PCに画像データを移行する...
-2. 前処理 … 画像を 224×224〜299×299 にリサイズまたは正規...
-- torchvision.transforms (PyTorch をインストールした場合...
-- tf.keras.preprocessing (TensorFlow をインストールした...
-3. データ分割 … Train(訓練用) : Val(検証用) : Test(評価...
-- → sklearn.model_selection.train_test_split を用いて分...
-4. データ拡張 … ランダム左右反転・回転 ±15°
-- → ColorJitter(彩度・明度)や Cutout or RandomErasing ...
-5. モデル … 転移学習が最速
-- 転移学習とは … 1から CNN を学習させるには何千〜何万枚...
-- ResNet‑50 / EfficientNet‑B0 (事前学習済みのCNN)などを ...
--- → PyTorch Lightning / Keras を使用
-6. 学習 … EarlyStopping & ModelCheckpoint を用いる
-7. 評価 … 従来どおり混同行列を表示し学習は上手くいった...
-- 以下のツールで判断根拠を可視化
---① scikit‑learn … Pythonの定番機械学習ライブラリであり...
---② torchmetrics … PyTorch 専用の 評価指標ライブラリであ...
---③ grad‑cam … CNNが画像のどこを見てその点数であると判断...
- 8. 推論 … 重みを .keras (推奨) で保存
-- CLIツールをPython上で作成して判別させたい画像のラベル(...
より高精度・運用向けの改善アイデア
--クラス間で枚数が偏ると “稀な酷い錆(5点)” を取りこぼすの...
-- 回帰併用 … 5 段階を「1〜5 の連続値」として MSELoss も...
-- アンサンブル学習 … EfficientNet + ConvNeXt など 2–3 種...
***5月第4週(5/22〜5/28) 錆画像判別 [#j417c521]
-Animal-2.pyを実行して結果が出力されたので下の表に示す。
,評価項目 ,判別率(学習回数1回) ,判別率(学習回数60回)
,airplane ,74.20% ,86.80%
,automobile ,77.90% ,83.40%
,bird ,43.00% ,80.00%
,cat ,55.10% ,62.80%
,deer ,67.60% ,85.30%
,dog ,45.90% ,76.90%
,frog ,66.40% ,89.40%
,horse ,74.90% ,88.80%
,ship ,76.00% ,92.50%
,truck ,78.70% ,92.00%
,平均 ,65.97% ,83.79%
自作したスクリプトが正常に動作するかどうかを調べたかった...
学習させたデータをもとに cifar10 上にあるテスト用画像を各...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
,評価項目 ,判別率(学習回数1回) ,判別率(学習回数10回) ,判...
,airplane ,73.00 % ,100.00 % ,100.00 % ,88.20 %
,automobile ,84.00 % ,100.00 % ,100.00 % ,74.50 %
,bird ,53.00 % ,100.00 % ,100.00 % ,76.50 %
,cat ,68.10 % ,100.00 % ,100.00 % ,58.90 %
,deer ,71.60 % ,100.00 % ,100.00 % ,86.70 %
,dog ,44.00 % ,100.00 % ,99.00 % ,75.80 %
,frog ,69.00 % ,100.00 % ,100.00 % ,91.10 %
,horse ,79.00 % ,100.00 % ,100.00 % ,90.10 %
,ship ,83.00 % ,100.00 % ,100.00 % ,93.40 %
,truck ,89.70 % ,100.00 % ,100.00 % ,91.10 %
,平均 ,71.30% ,100.00 % ,99.90% ,82.63 %
5/23現在エポック数を60にして判別した結果は記載していない...
-午前中に高専の中島さん達と今後の動きについて話し合った。...
-東北各地から錆のデータが集まるまでまだ時間がかかりそうな...
--はじめに目視で評点を画像に入力するなどした後に機械がど...
錆の一例
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
過去の先輩が残してくださったセロハン試験の画像データがあ...
以下の画像はUMAPという高次元データを低次元空間に圧縮し、...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
この図から分かること
-1. 似た錆画像は近くに配置されている
--UMAPは元の画像の見た目の特徴の類似度を保持したまま、2次...
-2. KMeansクラスタリングにより錆画像が5つのグループに分か...
--色が異なる5つの「クラスタ (= 点)」は特徴的に似た錆画像...
--- 微細な粉状の錆
--- 鱗片状に剥がれた錆
--- 密集した粗い錆
--- 色調の異なる錆(赤錆、黒錆など)
-3. UMAPと2つの軸(UMAP-1とUMAP-2)の意味
--UMAP(Uniform Manifold Approximation and Projection)と...
--両軸は「人間が理解できる物理的意味」は持っておらずただ...
UMAPは錆の画像に評点をつけてから使うこととする。評点をつ...
5/29 やること
-評点を判別させるスクリプトの構築
**6月 [#j417c600]
***6月第1週(5/29〜6/4) [#j417c529]
昨日UMAPによって分類された錆の画像からグループ毎に10枚 合...
,評点 ,枚数
,1 ,6枚
,2 ,12枚
,3 ,15枚
,4 ,9枚
,5 ,8枚
-学習1回目
--錆の画像の一部にラベルをつけて機械学習させた後その結果...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
---紫色 … 評点1 紺色 … 評点2 翠玉色 … 評点3 ...
--評点2と3はある程度グループとして固まっているもののラベ...
-学習2回目
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
---紫色 … 評点1 紺色 … 評点2 翠玉色 … 評点3 ...
--1回目と比較してバラつきは少なくなったものの左上に固まっ...
-明日やること
--学習の繰り返し
-3回目と4回目
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
評点付きの錆画像データを見つけたのでこれをUMAPを用いて分...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-1. 点(クラスタ)の存在
--縦軸と横軸にそれぞれ注目するとUMAP-1(縦軸)で見ると広く...
--画像の中央から左側は縦方向に伸びていることが確認できる...
--左上には4~5の評点が密に集まる領域があり比較的状態が良...
-2. UMAPとスコア(≠評点)について
スコアは完全分離しているわけではないが、スコアごとにある...
--スコア1 : 縦軸に注目すると殆どの点がUMAP2の1〜5に集中し...
--スコア2 : UMAP2の -1〜3に集中している。
--スコア3, 4 : どちらも画像の左側に密集しているもののスコ...
--スコア5 : スコア4と同様な分布となっているが左上にかなり...
2方向のUMAPはそれぞれ何を基に分類を行っているか確認してみ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-錆画像から抽出した各特徴量の意味
, 特徴量名 , 説明 , 数字の大小で何が分かるか
, aspect_ratio , 縦方向エッジ量 ÷ 横方向エッジ量(= Sobel...
, sobel_x , 横方向(左右)の輪郭強度(エッジ) , 値が大き...
, sobel_y , 縦方向(上下)の輪郭強度(エッジ) , 値が大き...
, orientation_std , 画像内の輪郭や模様の方向性のばらつき...
, num_blobs , 二値化画像から検出された「斑点」や「模様の...
, roughness , 周辺の濃淡変化の激しさや表面のざらつき度(...
-UMAP1は sobel_x, sobel_y, roughness, に強い負の相関が見...
-よってUMAP1の場合横・縦方向のエッジ(sobel_x・sobel_y)が...
-UMAP2の場合は模様の方向(orientation_std)がバラバラだとUM...
--UMAPの座標が意味するもの
, 軸 , 表すものの傾向
, UMAP1 , 主に「模様の強さ(エッジ強度)」+「表面の粗さ...
, UMAP2 , 主に「模様の方向性のバラバラ度合い(orientation...
このことから、UMAPプロット上で:
--左下あたりにいる画像:鋭いエッジがあり、ざらざらした錆...
--右上あたりにいる画像:模様がバラバラ・薄く滑らかな錆(...
といった分布傾向が読み取れる。
1回の学習ごとに手動で評点を入力してはの繰り返しだと終わり...
評点付きの錆画像は何枚か同じ画像が存在したため重複してい...
, 処理 , 内容
, 画像の反転 , ただ画像を鏡写しにするだけ
, 画像の回転 , ランダムで ±10°~±30°、±90°、±180°のいずれ...
, 類似画像の削除 , 画像処理分野で画像の類似度を数値化する...
MMLI (=More Machine Learning Images).py … 既存の画像を反...
Hyoutenhuriwake+UMAP2.py … ラベル付きの画像を学習させた...
上記のPythonスクリプトを実行させると24枚しかなかった画像...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
以前よりもクラスタリング精度が向上したのではないか。
-1. 評点ごとの分離性
--この画像ではグループ5(黄色)が密集しており、他の評点と...
--以前のUMAP図では評点ごとの混在が見られたのに対し今回は...
-2. 色の連続性
--カラーバーは評点1〜5の連続スケールとなっているが評点を...
-3. まとめ
--このUMAP可視化では以前よりも評点ごとのクラスタリングが...
-次回やること : CNN×スクリプトの作成
--augmented_imagesをCNNで機械学習させる (3層×3回 や 3通り...
--評点付き教師画像はラベルの先頭が評点となっている。 例) ...
--上記の学習結果を基に判別させたい画像(Fuji2として保存) ...
--また判別した結果を評点別にUMAP + KMeans で可視化 & 伝播...
--画像における2方向(x, y軸)のUMAPはそれぞれ何を基に分類を...
***6月第2週(6/5〜6/11) [#j417c606]
-CNNを用いて錆画像を判別させるスクリプトを作成中
Main.py ーー軽量化ー> Main2.py
-とりあえず評点付き錆画像を3タイプのCNNで判別させた学習結...
3パターンのCNNは以下の通り
# === CNN構造定義(バリエーションを3通り用意) ===
def create_model(version):
model = models.Sequential() ...
model.add(layers.Input(shape=(*IMAGE_SIZE, 3))) ...
if version == 0: (パターン1)
model.add(layers.Conv2D(64, (3,3), activation='r...
model.add(layers.BatchNormalization()) ...
model.add(layers.Conv2D(64, (3,3), activation='r...
model.add(layers.MaxPooling2D((2,2))) ...
model.add(layers.Dropout(0.3)) ...
model.add(layers.Conv2D(128, (3,3), activation='...
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3,3), activation='...
elif version == 1: (パターン2)
model.add(layers.Conv2D(64, (5,5), activation='r...
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Dropout(0.3)) ...
model.add(layers.Conv2D(128, (3,3), activation='...
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3,3), activation='...
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Dropout(0.3)) ...
elif version == 2: (パターン3)
model.add(layers.Conv2D(64, (3,3), activation='r...
model.add(layers.Conv2D(64, (3,3), activation='r...
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Dropout(0.3)) ...
model.add(layers.Conv2D(128, (3,3), activation='...
model.add(layers.Conv2D(128, (3,3), activation='...
model.add(layers.BatchNormalization()) ...
model.add(layers.GlobalAveragePooling2D()) ...
model.add(layers.Dense(128, activation='relu')) ...
model.add(layers.Dropout(0.5)) ...
model.add(layers.Dense(NUM_CLASSES, activation='soft...
model.compile(optimizer='adam', ...
loss='sparse_categorical_crossentropy'...
metrics=['accuracy']) ...
return model
-上記のCNNで学習させると特に評点1と評点2, 評点5の判別が上...
-評点2には人間の目で評点1と捉えられてもおかしくない画像が...
--評点2に保存されていた評点1だと思われる画像(評点2の錆画...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
従来はラベルの付いた画像をそのまま学習させていたが、判別...
RSRC.py (rust_score_regression_cluster.py)
→ 錆画像にラベルをつける際に判断材料となるであろう錆の粒...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-1, Mennsekisa:錆の大きさのバラつき(面積差)
クラスタ2が評点2と完全一致しており非常に良い識別精度とい...
-2, Color_kosa:色の濃さ(グレースケール)
クラスタ2が評点2と完全一致している。クラスタ0と4も比較的...
-3, Mensekihi:錆面積比
錆を黒, それ以外の部分を白と区別させて面積比を求めた。ク...
-4, Sabi_kazu:錆の数
どのクラスタも比較的均等に分散しているため、錆の数と錆の...
-5, Ryukei:錆の大きさ・粒径
クラスタ2が評点2に1.00、クラスタ3が評点2に0.96 評点2, 3...
以上よりクラスタ2のように明確に分類できているものもあった...
-特徴量付き CNN(ハイブリッドモデル)
--画像はCNNで処理しつつ、錆の形状・数などの特徴量を別入力...
--画像から抽出される“感性的”なパターンと、手動で抽出した“...
--CNN単体だと分類が不安定(特に評点1と2)
--面積・数・濃淡などの情報が評価に明らかに効いている場合
rating_cnntrainer.py
上記のスクリプトで処理していることについて
, 項目 , 説明
, 画像入力 , CNNで64×64の画像から特徴を抽出
, 数値特徴量入力 , .npyファイルから読み込まれた錆特徴量(...
, ハイブリッド構造 , CNN特徴と手作り特徴を Concatenateで...
, モデルバージョン , 3種類のCNN構造を用意したアンサンブル...
, 予測 , 3モデルの出力平均で最終スコアを判定 1〜5の評点に...
, 錆特徴量名 , 内容説明 , 特徴量例
, rust_ratio , 錆の占有面積(全体に対する割合) , 平均輝...
, num_blobs , 錆のかたまり数(connected components) , 形...
, np.mean(gray) , グレースケールの平均(明るさ) , GLCM(...
, np.std(gray) , グレースケールの標準偏差(濃淡の変動) ,...
, np.mean(sobel_edges) , エッジ強度(輪郭の激しさ) , Sob...
, area_diff , 最大面積 - 最小面積の錆領域の差 , 形状(面...
ラベル付き画像 (947枚) を学習させてラベルを付けていない画...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
CNN+binning.py
-CNN回帰モデル + binning
--評点(1〜5)を離散ラベルではなく連続的な“点数”と捉えて...
--各評点の境界が曖昧で連続的なスコアで表現した方が自然
--評価者の意識が「点数感覚」に近い(例:2.3点 ≒ 評点2)
-構成内容
--1. 目的
画像を入力しCNNで連続的なスコア(評点1.0〜5.0の0.1点刻み...
そのスコアをbinning(1点〜5点)に変換して評点分類として扱う
--2. モデル構成(従来のCNN分類との違い)
, 項目 , CNN分類 , CNN回帰 + binning
, 出力層 , Dense(NUM_CLASSES. activation='softmax') , Den...
, 損失関数 , sparse_categorical_crossentropy , mean_squar...
, 出力形式 , クラス確率(0〜1) , 連続値スカラー(例:3.7...
, 評点の扱い , クラス分類(0〜4) , binning により 1〜5 ...
---出力層・形式 , 評点の扱い
今までのCNNは NUM_CLASSES(出力次元数 = 何クラスに分類する...
model.add(Dense(5, activation='softmax')) → この場合NU...
とスクリプトに入力すれば [0.1, 0.1, 0.7, 0.05, 0.05] のよ...
model.add(Dense(1))
と入力すれば錆画像の評点は3.27点というように連続スカラー...
---損失関数
分類の場合
loss='sparse_categorical_crossentropy'
で正解クラスだけ1、それ以外0のone-hotラベル (異なるグルー...
回帰の場合
loss='mean_squared_error'
で予測値 3.27 とラベル 3.0 との差 0.27 を最小化し「正解と...
--3. 学習データ(ラベル)の準備
.png などの画像を Image.open → resize → 正規化 の順に変...
--4. binning(スコア→評点)
学習後の推論時に以下のような関数で連続値を離散化する (一例)
, def binning(score):
, if score < 1.5: return 1
, elif score < 2.5: return 2
, elif score < 3.5: return 3
, elif score < 4.5: return 4
, else: return 5
--5. 評価方法
学習時は回帰(MSE = 平均二乗誤差)で最適化されるが、推論...
from sklearn.metrics import accuracy_score, confusion_ma...
y_pred = model.predict(x_val).flatten()
y_binned = [binning(p) for p in y_pred]
acc = accuracy_score(y_val, y_binned)
--6. CNN回帰モデル + binning に追加で工夫できること
工夫 内容
出力に ReLU + offset 出力が1〜5に制限されるようにする(例...
Huber loss に変更 MSEよりも外れ値に強く安定
binning間隔を柔軟化 ヒストグラムを見て等間隔でない分割に...
CNN Backbone
┌───────────┐
│ Conv2D → Pool → ...│
└─────┬─────┘
▼
┌───────────┐
│ Flatten → Dense() │
└─────┬─────┘
▼
分類モデル 回帰モデル
┌─────────┐ ┌─────┐
│ Dense(5, softmax)│ │ Dense(1) │
└─────────┘ └─────┘
↓ ↓
argmax float値 → binning → int
***6月第3週(6/12〜6/18) [#j417c612]
これまでの結果から錆画像を識別するのはCNN回帰 + binning ...
現時点(6/12現在)で使用している錆画像フォルダーとPythonス...
-錆画像フォルダー
--1, Fuji2 … ラベル付けを行っておらず学習結果を基にどのく...
--2, train_images … ラベル付けされた画像のあるフォルダー
--3, augmented_images … train_imagesに入っているラベル付...
--4, Kekka_Reg … 下記の CNN + binning.py を実行した後に出...
-Pythonスクリプト
--1, MMLI.py … train_images 内に保存されている画像に指定...
&ref(http://www....
--2, CNN+binning.py … この学習で核となるスクリプトであり...
回帰スコアから評点の分類を行っていた。スクリプトのデフォ...
, 評点 , 回帰スコア
, 1 , 2.30点以下
, 2 , 2.31点以上3.00点以下
, 3 , 3.01点以上3.40点以下
, 4 , 3.41点以上3.64点以下
, 5 , 3.65点以上
以下回帰スコア別に保存された錆画像を貼っておく
-評点1
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-評点2
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-評点3
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-評点4
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-評点5
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-来週の外国語文献までに原稿と発表スライドを書く。
-CNN+binning.py内のアンサンブル学習に少し手を加えた
CNN+binning3.py
CNN+binning.py内のアンサンブル学習についての変更点(CNN+bi...
-CNNモデルの変更, 今までは名前のない簡単な構造の自作CNNモ...
--1, EfficientNetV2S... 高精度なのに学習が早い・モデルが...
--2, DenseNet121... ResNetよりモデルは軽く錆やひび割れと...
--3, MobileNetV3Large... 特徴の抽出力はそこまで高くないも...
def create_model(version): ...
input_img = Input(shape=(*IMAGE_SIZE, 3)) ...
if version == 0: # EfficientNetV2S ...
base = EfficientNetV2S(include_top=False, weight...
...
...
elif version == 1: ...
base = DenseNet121(include_top=False, weights="im...
elif version == 2: ...
base = MobileNetV3Large(include_top=False, weight...
base.trainable = False ...
x = base.output ...
x = layers.GlobalAveragePooling2D()(x) ...
x = layers.Dense(512, activation='relu')(x) ...
x = layers.BatchNormalization()(x) ...
x = layers.Dropout(0.5)(x) ...
x = layers.Dense(256, activation='relu')(x) ...
x = layers.BatchNormalization()(x)
x = layers.Dropout(0.3)(x)
x = layers.Dense(128, activation='relu')(x) ...
x = layers.Dropout(0.2)(x)
output = layers.Dense(1)(x) ...
model = Model(inputs=input_img, outputs=output) ...
model.compile(optimizer='adam', loss=custom_penalizi...
...
...
return model ...
明日やること
-CNN+binning3.pyの回帰スコア調節
***6月第4週(6/19〜6/25) [#j417c619]
CNN+binning2.py と CNN+binning3.py の比較
, 諸条件 , CNN+binning2.py , CNN+binning3.py
, CNNモデル , 自作CNNモデル3パターン , EfficientNetV2S、D...
--2つのPythonスクリプトで異なる部分はCNNモデルぐらいであ...
-評点と回帰スコアの対応表
, 評点 , 回帰スコア
, 1 , 0.00点以上2.30点未満
, 2 , 2.30点以上3.10点未満
, 3 , 3.10点以上3.40点未満
, 4 , 3.40点以上3.70点未満
, 5 , 3.70点以上5.00点未満
上記の条件でそれぞれ学習させると評点ごとに保存された枚数...
-評点ごとに保存された枚数
, 評点 , CNN+binning2.py , CNN+binning3.py
, 1 , 24 , 13
, 2 , 110 , 119
, 3 , 54 , 123
, 4 , 59 , 146
, 5 , 418 , 264
評点が1, 2 の場合は判別に大きな差は見られなかったが、逆に...
CNN+binning3.pyに
-面積・粒径・Sobelエッジ量などの数値特徴を別途抽出
-CNN出力 + 特徴量ベクトル を結合してDense層に入力 →CN...
を加えたところ回帰スコアが負の値や2桁になってしまったため...
CNN+binning4.py にUMAPを画像で出力することに加えて評点も...
-シグモイド関数 パラメータ
--SIGMOID_SCALE ...
通常の sigmoid(x) は0〜1の値を出力するがそれを 5.0 * sigm...
--SIGMOID_K ...
シグモイド関数の傾きを調整する係数でありあいまいな場合小...
--SIGMOID_X0 ...
シグモイド関数の 中央(変曲点)をどこに置くかを決めるシフ...
CNN+binning5.py
CNN+binning5.pyの内容
-1. CNNを用いた錆画像の評点回帰モデルの構築
EfficientNetV2S / DenseNet121 / MobileNetV3Large の3つの...
-2. 画像+数値特徴のハイブリッドモデル構築
画像からのCNN出力に加え、以下の 数値特徴量(4種類)を自動...
-錆面積割合(rust_ratio)
-粒径(平均・最大)
-Sobelエッジ量
-錆の個数
-3. Optunaによるしきい値の自動最適化
特徴量(粒径, Sobel量など)に基づき評点を補正するためのし...
今まではCNNで評点付き錆画像を学習させた後、判別させたい錆...
例) 錆画像の回帰スコアが3.45点であり、評点と回帰スコアの...
例を読んで理解できただろうか。今回はこの判別方法を行った...
ただUMAPでの分類はそれなりであるものの評点ごとに保存され...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
ちなみにUMAPの理想は同じ色の点が密集しているのが理想であ...
***6月第5週(6/26〜7/2) [#j417c626]
評点を1.0~5.0点の間に変換する方法として シグモイド関数, ...
1, シグモイド関数(+スケーリング)
シグモイド関数 ''sigmoid(x) = 1 / (1 + e^(-x))'' は、出力...
・実装方法
output = Dense(1, activation='sigmoid')(x) #...
output = Lambda(lambda z: 1.0 + 4.0 * z)(raw_output) #...
※PCやバージョンによっては "Lambda" 関数がインストールさ...
SIGMOID_SCALE = 5.0 # 出力を0〜5にスケーリング
SIGMOID_K = 1 # 傾き係数(大きくするとS字...
SIGMOID_X0 = 0 # 切片(出力の中心)
--------------------------------------------------------...
output = layers.Dense(1)(x)
output = ScaledSigmoid(SIGMOID_SCALE, SIGMOID_K, SIGMOID...
メリットとして上記のスクリプトを組み込むだけで1.0~5.0点の...
2, 線形変換(+出力範囲制限)
出力層は線形のまま Dense(1) を用いて範囲外の値(~1.0, 5.0...
・実装方法
output = layers.Dense(1)(x)
output = layers.Lambda(lambda z: tf.clip_by_value(z, 1.0...
※Lambdaを実装しない場合(一例)
# === ClipLayer の定義 ===
@register_keras_serializable()
class ClipLayer(Layer):
def __init__(self, min_val=1.0, max_val=5.0, **kwarg...
super().__init__(**kwargs)
self.min_val = min_val
self.max_val = max_val
def call(self, inputs):
return tf.clip_by_value(inputs, self.min_val, se...
def get_config(self):
config = super().get_config()
config.update({
'min_val': self.min_val,
'max_val': self.max_val
})
return config
--------------------------------------------------------...
output = layers.Dense(1)(x)
output = ClipLayer()(output)
メリットとしてシグモイド関数と同様に実装が簡単であり、ネ...
3, 活性化関数 [tanh(x)] ( + スケーリング)
シグモイド関数と異なる箇所は用いる関数のみで、活性化関数 ...
・実装方法
output = Dense(1, activation='tanh')(x) #...
output = Lambda(lambda z: 3.0 + 2.0 * z)(raw_output) #...
※PCやバージョンによっては "Lambda" 関数がインストールさ...
メリットとして出力が中央(3.0)に対して対称でありシグモイ...
これらをまとめると
, 方法 , 評点の保証 , 出力の自由度 , 実装難易度 , 学習安...
, 1. Sigmoid + スケーリング , ◎ 常に1~5 , △ 中央に寄る ,...
, 2. Clipによる制限 , ◯ 制限あり , ◎ 高い , ◎ 簡単 , △ 勾...
, 3. Tanh + スケーリング , ◎ 常に1~5 , ◯ 対称性あり , ◯ ...
, 制限なし(今まで) , × 保証なし , ◎ 最大自由度 , ◎ 簡単 ,...
どれも課題があるもののその中で学習安定性を重視したいと考...
これから5つのアンサンブル学習(計15のCNNモデル)で精度・時...
画像から評点を出すまでの流れ
1. 前処理・画像とスコアの準備
-指定したフォルダから画像ファイルを読み込み、画像ファイル...
2. モデル構築・学習
-複数のCNNモデル(アンサンブル)を構築させ各モデルはImage...
--この損失関数(カスタム損失関数)では予測値と正解値の絶...
3. 評点の予測
-テスト用画像(未知画像)を同じく前処理し各モデルで回帰ス...
4. モデル評価・可視化
-検証データに対しても同様に予測し回帰スコアと評点(整数)...
-まとめ
--1, 画像を前処理して読み込み、元スコアを取得(前処理→特...
--2, 複数のCNNモデルで回帰学習(1〜5点)(学習)
--3, テスト画像を各モデルで予測し、平均スコアをbinningで1...
--4, 評点ごとに画像を分類・保存し、検証データで精度評価や...
結果
-モデルA ["EfficientNetV2M", "InceptionResNetV2", "DenseN...
--学習時間:4時間15分, 分類:評点1から順番に[2, 32, 365, ...
-UMAP
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-モデルB["EfficientNetB3", "ResNet50", "MobileNetV3Small"...
--学習時間:1時間36分 分類:評点1から順番に[0, 0, 5, 153,...
-UMAP
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-モデルC["InceptionResNetV2", "ResNet101", "EfficientNetV...
--学習時間:7時間32分 分類:評点1から順番に[0, 11, 73, 33...
-UMAP
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-モデルD["DenseNet201", "EfficientNetV2M", "EfficientNetV...
--学習時間:6時間48分 分類:評点1から順番に[0, 15, 84, 46...
-UMAP
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-モデルE["EfficientNetV2S", "EfficientNetV2M", "Efficient...
--学習時間:4時間33分 分類:評点1から順番に[0, 4, 90, 409...
-UMAP
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
**7月 [#j417c700]
***7月第1週(7/3〜7/9) [#j417c701]
5タイプのCNNモデルの結果
-パターンA
クラスタの分離が良好であり特にグループ1・2が図の右側に、...
-パターンB
グループ5は分離はしているものの何箇所にも散らばっている上...
-パターンC
グループ1, 2, 5の分類は明瞭であるもののグループ3, 4がクラ...
-パターンD
全体的に「偏り」が見られ、クラスタ構造自体は悪くないがグ...
-パターンE
グループ1, 2は明瞭な分類ができているものの3, 4は分類が上...
→ パターンAに劣らない分類性能は持っているものの複数のクラ...
-まとめ
, パターン , 分類精度 , 分離度 , 安定性 , 処理速度 , コメ...
, A , ◎ , ◎ , ◎ , ○ , 実用・研究の両面でバランス型 安定...
, B , △ , △ , △ , ◎ , 軽量だが精度不足 軽量で高速な実験...
, C , ◎ , ○ , ◎ , △ , 深層学習として高性能、やや重い 安...
, D , ○ , ○ , △ , ○ , 特徴は良いが評点にはやや曖昧さ
, E , ◎ , ◎ , ◎ , △ , 最強構成、実装コスト高め 最大性能を...
学習時間, 精度の面から元々行っていた CNN+binning3.py を改...
来週までにやること
-評点ごとの平均絶対誤差を算出する
-混同行列を表示する(分類的な観点)
-誤差の大きい画像を抽出する
Pythonスクリプトに 平均絶対誤差 と 混同行列の可視化 を追...
平均絶対誤差 MAE は1枚毎に予測値と正解値のズレの大きさを...
-augmented_imagesに保存されているラベル付き錆画像
評点1:144枚 評点2:1,131枚 評点3:1,004枚 評点4...
CNN+binning3-UMAP.pyで学習させてみた
, -項目- , -条件-
, 評点を付ける画像サイズ(Fuji2) , 334×334ピクセル
, 使用するCNNモデル , EfficientNetV2S / DenseNet121 / Mob...
, 全結合層 , 6層
, Dropout関数の実装 , あり
, 活性化関数 , なし(出力された予測・回帰スコアをbinning...
, 学習中に精度が良くならない状態が続いたら学習をやめるエ...
, エポック数(学習する最大の回数) , 50回
※binningスコアと予測・回帰スコアの違いについて
-予測・回帰スコアとはCNNモデルが出力する ”連続的な数値(...
-binningスコアとは連続的な回帰スコアを "評点1〜5のカテゴ...
# === binning: 予測・回帰スコア → 評点1〜5 ===
def binning(score):
if score < 2.00: return 1 #予測・回帰スコアが2.0...
elif score < 2.50: return 2 #予測・回帰スコアが2.0...
elif score < 3.00: return 3 #予測・回帰スコアが2.5...
elif score < 3.50: return 4 #予測・回帰スコアが3.0...
else: return 5 #予測・回帰スコアが3.5...
上記の条件に当てはめると予測・回帰スコアは以下のように変...
, 予測・回帰スコア , → binningスコア
, 1.96 , → 1
, 2.23 , → 2
, 2.86 , → 3
, 3.48 , → 4
, 4.01 , → 5
-7/4
特徴量を実装してみた
--錆の占める割合…白黒画像の場合 mask(cv2.threshold)とい...
--錆の数… cv2.findContours というツールを用いて錆部分の輪...
--最大錆サイズ…cv2.contourAreaというツールを用いて各輪郭...
--※錆の部分とそうでない部分の色が逆転してしまっているが画...
-7/7
引き続き特徴量の補正関数について試行錯誤を重ねていく 重回...
-7/8 CNNに限らず機械学習(アンサンブル学習)を行なうと学...
--今まで:スタート ⇒既存の .keras ファイルを削除 ⇒ アンサ...
--これから:スタート ⇒既存の .keras ファイルを呼び出す ⇒ ...
-7/9
CNN+binning5.py
上記のスクリプトで行われていること
--ラベルの付いていない錆画像から画像の面積(ピクセル)に...
--3つのCNNモデル(EfficientNetV2S / DenseNet121 / Mobile...
--学習を繰り返している中で最も結果・精度が良くなったとこ...
--学習結果を保存するファイルとして .keras ファイルという...
例)📂 1_001.jpg → 回帰: 2.62 → 補正後: 2.52 → 評点: 2 |...
📂 1_002.jpg → 回帰: 3.14 → 補正後: 3.14 → 評点: 3 |...
📂 1_003.jpg → 回帰: 2.71 → 補正後: 2.71 → 評点: 3 |...
📂 1_004.jpg → 回帰: 3.37 → 補正後: 3.67 → 評点: 4 |...
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜 省略 ...
📂 4_094.jpg → 回帰: 2.58 → 補正後: 2.48 → 評点: 2 |...
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜 省略 ...
平均絶対誤差:0.11544688538164781(0.3を下回っていれ...
--評点の出力について まず3つのCNNモデルで学習した結果を...
--※1 補正したスコアが0.00〜5.00の範囲から外れてしまった...
--※2 補正係数については以下のスクリプトで設定している。
# 特徴量によるスコア補正関数(抜粋)
def adjust_score_by_features(score, rust_ratio, rust_cou...
correction = 0.0
#錆面積割合
if rust_ratio >= 0.70:
correction -= 0.4 #錆面積割合が0.7(7...
#elif 0.50<= rust_ratio < 0.70: #評点の結果に応じて...
# correction -= 0.3
elif rust_ratio < 0.10:
correction += 0.2
#錆の数
if rust_count > 800:
correction -= 0.1
〜〜〜〜〜〜 省略 〜〜〜〜〜〜
#最大錆サイズ
if max_blob_area >= 10000:
correction -= 0.3
〜〜〜〜〜〜 省略 〜〜〜〜〜〜
return score + correction
--※3 特徴量の抽出方法について
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
***7月第2週(7/10〜16) [#j417c710]
-7/10
特徴量補正の有無による評点結果の比較検証
CNNによる回帰スコアに対して画像特徴量(錆割合、錆数、最大...
-手順
--1, CNN+binning4.py を実行しCNNモデルの学習を行った。こ...
--2, 続いてCNN+binning5.py を実行。先ほど CNN+binning4.py...
--3, このとき学習モデルは同一であるため、元の「回帰スコア...
--4, 次にCNN+binning5.py 側で新たに .keras ファイルを出力...
--5, 最後にその CNN+binning5.py で出力した .keras モデル...
以下に結果を示す
-① 学習に "特徴量を使い(青)、使わず(朱)" 評点を算出す...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-② 学習に "特徴量を使わず(緑)、使った(黃)" 評点を算出...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-③ ①と②の分布図をひとまとめにしたグラフを以下に示す
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
, グラフの色 , 学習に用いたpython , 評点算出の際に用いたp...
, 青 , CNN+binning4.py , CNN+binning5.py , 0.1783
, 朱 , CNN+binning4.py , CNN+binning4.py , 0.1756
, 緑 , CNN+binning5.py , CNN+binning5.py , 0.1496
, 黃 , CNN+binning5.py , CNN+binning4.py , 0.1510
-7/11
--UMAPの画像を見比べてみると変化があったのは一部の点の色...
--上記のグラフからわずか4通りの組み合わせで学習を行ったに...
-7/14〜16
O.Cの準備及び外国語文献の発表スライド作成に注力したため進...
***7月第3週(7/17〜23) [#j417c717]
-7/17
今週はMAEに注目して精度向上を狙っていきたい。現時点では学...
***7月第4週(7/24〜30) [#j417c724]
-7/28〜30
CNN+binning6.py
binning5.pyには実装しなかった重回帰分析がメインとなる。今...
-ここ最近はMAEをなるべく0に近づけるようにしている(CNN+b...
, 回 , 錆面積範囲 , 錆数 , 最大錆サイズ , MAE , 評点別画...
, 1 , 定義しない , 定義しない , 定義しない , 0.129 , 13-1...
, 2 , 0.5以上:-0.4 0.35以上0.5未満:-0.15 0.1以下:+0.15...
, 3 , 0.5以上:-0.4 0.35以上0.5未満:-0.15 0.05以下:+0.1...
, 4 , 2と変化なし , 削除(定義しない) , 2と変化なし , 0....
, 5 , 0.5以上:-0.4 0.33以上0.5未満:-0.15 0.05以上0.1未...
, 6 , 0.6以上:-0.5 0.45以上0.6未満:-0.3 0.25以上0.45未...
, 7(重回帰) , ーーー , ーーー , ーーー , 0.126 , 13-125...
特徴量に応じて回帰スコアから加減点を行うことは、定義しな...
データ数を増やすのはもう少し時間がかかりそう。重回帰で学...
-① 特徴付き画像フォルダを読み込む(学習画像 + ラベル = 回...
-② 特徴量(錆割合・数・最大サイズなど)を抽出
-③ CSVに保存(訓練データ・予測用データ兼用)
-④ 特徴量と評点(ラベル)で重回帰学習 → .pkl保存
-⑤ 同じ特徴量データに対して、重回帰を適用 → 評点を補正
-⑥ CNN3種(EffNetV2S/DenseNet/MobileNet)で学習 → アンサ...
-⑦ 評点の最終出力(重回帰補正後の回帰スコア + CNN予測スコ...
-⑧ 結果保存(CSV・UMAP・混同行列など)
を1つのスクリプトにまとめたものをこれから1周間で作成でき...
**8月 [#j417c800]
***8月第1週(7/31〜8/6) [#j417c731]
-8/3
1日から県内に架けられている耐候性鋼橋約90箇所の中から調査...
[["大学"を Google Map で開く>https://www.google.com/maps/...
-8/4
耐候性鋼橋約90箇所が調査可能かどうかを Google map で見な...
機械学習についてはやはり学習元となるセロハン試験で採取さ...
-MAE向上へ
--1, データ量を無意味に水増しするのではなく意味的な水増し...
--2, Soft label にラベル品質スコア導入…手動で「このラベル...
--3, 高次元な非線形補正…重回帰の代わりに LightGBM, XGBoos...
--4, スコア帯(4, 5)の混同行列精度や Confusion Matrix上...
-8/5, 6
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
上記の画像(混同行列)は10,000枚近くあるラベル付きの画像...
--評点4画像の枚数・バリエーション不足 → 水増しによる拡張...
--上記の原因によりモデルが評点4の特徴を学習できていない →...
が考えられる。精度向上のため
-- 評点4の「代表的な画像」を新たに5〜10枚ほど追加 → 判別...
--追学習 → 評点4に関して予測誤差が大きい画像だけを再学習...
を行うことで正答率34%からどれほど上げられるか。とりあえず...
結果
-評点4のラベル付画像を新たに6枚追加した場合のみの混同行列...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
生成された画像が少ないが評点4の正答率が大幅に向上した。(...
***8月第2週(8/7〜8/13) [#j417c807]
-8/7
--全評点のラベル付画像を10枚(評点5だけ9枚)にした場合の...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
画像を水増ししすぎてしまったのか綺麗な対角線を描けなかっ...
-8/8
--1枚あたりの水増しを約60枚にして学習させてみた。
***8月第3, 4週(8/14〜8/27) [#j417c814]
-8/14
一度に複数の条件で学習を行えるようにしたため、学習条件を...
, モデルNo. , 水増し画像枚数 , 水平・鉛直方向への移動 , ...
, 1 , 50 , あり(水平・垂直ともに最大10%移動 他のモデル...
, 2 , 50 , なし , 30回 , 32枚
, 3 , 50 , あり , 50回 , 32枚
, 4 , 50 , なし , 50回 , 32枚
, 5 , 50 , あり , 30回 , 64枚
, 6 , 50 , なし , 30回 , 64枚
, 7 , 50 , あり , 50回 , 64枚
, 8 , 50 , なし , 50回 , 64枚
, 9 , 100 , あり , 30回 , 32枚
, 10 , 100 , なし , 30回 , 32枚
, 11 , 100 , あり , 50回 , 32枚
, 12 , 100 , なし , 50回 , 32枚
, 13 , 100 , あり , 30回 , 64枚
, 14 , 100 , なし , 30回 , 64枚
, 15 , 100 , あり , 50回 , 64枚
, 16 , 100 , なし , 50回 , 64枚
, 17 , 150 , あり , 30回 , 32枚
, 18 , 150 , なし , 30回 , 32枚
, 19 , 150 , あり , 50回 , 32枚
, 20 , 150 , なし , 50回 , 32枚
, 21 , 150 , あり , 30回 , 64枚
, 22 , 150 , なし , 30回 , 64枚
, 23 , 150 , あり , 50回 , 64枚
, 24 , 150 , なし , 50回 , 64枚
-統一した諸条件
, 条件内容 , 条件
, ランダム回転(画像を 最大 ±(記入された数字)° の範囲で...
, 画像を反転させるか , ON
, 画像の明るさ変更(明るさをA倍(暗く)〜B倍(明るく) の...
, 使用するCNNモデル , EfficientNetV2S、DenseNet121、Mobil...
-8/14
--全24パターンのMAEが出力されたのでその結果を貼り付ける。
, モデルNo. , MAE
, 1 , 0.196729463582136
, 2 , 0.243831214856128
, 3 , 0.195313368524824
, 4 , 0.144703118168578
, 5 , 0.205965055494892
, 6 , 0.289931784357343
, 7 , 0.17892047945334
, 8 , 0.230305907920915
, 9 , 0.126362581399022
, 10 , 0.0984963168903273
, 11 , 0.132567102081922
, 12 , 0.0933165281402821
, 13 , 0.162534093126959
, 14 , 0.183336140306628
, 15 , 0.173845792789849
, 16 , 0.0904155796279713
, 17 , 0.108065831782867
, 18 , 0.095051087976313
, 19 , 0.104002416782639
, 20 , 0.0796793378534771
, 21 , 0.204868344141513
, 22 , 0.206347949407539
, 23 , 0.092119121267682
, 24 , 0.256283822351573
--MAEが0.11を切ったモデルのみ抽出(昇順に並び替え)
, モデルNo. , 水増し画像枚数 , 水平・鉛直方向への移動 , ...
, 20 , 150 , なし , 50回 , 32枚 , 0.0796793378534771
, 16 , 100 , なし , 50回 , 64枚 , 0.0904155796279713
, 23 , 150 , あり , 50回 , 64枚 , 0.092119121267682
, 12 , 100 , なし , 50回 , 32枚 , 0.0933165281402821
, 18 , 150 , なし , 30回 , 32枚 , 0.095051087976313
, 10 , 100 , なし , 30回 , 32枚 , 0.0984963168903273
, 19 , 150 , あり , 50回 , 32枚 , 0.104002416782639
, 17 , 150 , あり , 30回 , 32枚 , 0.108065831782867
上記の結果より学習回数(の上限)は50回, 画像の水増し枚数...
-8/18
CNN+binning5-1.py
CNN+binning7.py
今までは画像の水増しと学習を別々に行っていた(始めにMMLI....
-- CNN+binning5-1.py → .keras ファイルが作成されるよう...
-- CNN+binning7.py → 明日までに27通りの学習モデルを学習...
-8/19
--CNN+binning5-1.py … .keras ファイルの出力完了
--CNN+binning7.py … MAE等結果の確認は24日以降 今日中に回...
---上記のスクリプトは画像を生成する際に元画像(長方形)か...
CNN+binning5-1.py で出力された .keras ファイル(MAEは0.07...
-8/25
20日の朝に学習を開始させたところ学習開始約60時間で強制終...
-8/26
午前中の時点で12パターンの途中まで学習が行われていなかっ...
--学習の流れについて
今現在の学習方法を以下に示す
1, オリジナル画像の読み込み
2, 画像をアスペクト比を維持しながらリサイズ・パディング
3, 回転・明るさ調整・シフトなどを加えた画像を複数枚生成...
4, 水増し済み画像の読み込んでラベル(スコア)を抽出
5, 学習用と検証用にそれぞれ総画像枚数 8:2 に分割
6, EfficientNetV2S / DenseNet121 / MobileNetV3Large の 3...
7, モデル( .keras ファイル)が保存済みなら再利用なけれ...
8, 検証用データの結果をもとにMAE(平均絶対誤差), 混同行...
9, ラベルがついていない画像を読み込んで推論値(回帰スコ...
10, 各画像ごとの推論結果, MAE, データ数, タイムスタンプ...
--補正関数について(再掲)
現在実装している補正関数は以下の通り ペナルティ関数と補...
---1, ペナルティ関数(学習時の損失関数)
@register_keras_serializable()
def custom_penalizing_loss(y_true, y_pred): ...
diff = tf.abs(y_true - y_pred)
return tf.reduce_mean(tf.square(diff) * (1.0 + tf.po...
誤差(diff) = |y_true - y_pred| を二乗しつつ(1 + diff^1....
---2, 補正関数(特徴量で補正)
def adjust_score_by_features(score, rust_ratio, rust_cou...
correction = 0.0 #リセット
# 錆面積割合
if rust_ratio >= 0.60:
correction -= 0.5
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜...
# 錆の数
if rust_count >= 1000:
correction -= 0.2
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜...
# 最大錆サイズ
if max_blob_area >= 15000:
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜...
return score + correction #補正前評点+補正関数
錆割合・錆の数・最大錆サイズに基づいてスコアを ±0.1〜0.5 ...
現在使用しているモデル(CNN+binning7.py)と以前使用していた...
--1, 補正前スコア(回帰スコア)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--2, 補正後スコア
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--3, 評点
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
CNN+binning.pyのVerが上がるごとに学習方法も変化してくるの...
-8/27
昨日の夜に学習が強制終了されていた。どうやら学習時にメモ...
***8月第5週(8/28〜9/3) [#j417c828]
-8/28
また強制終了していた。学習の組み合わせを2パターンにして...
-8/31
CNN+binning7-1.py
スクリプトの簡略化と一度に複数回学習を行えるようにした機...
--学習結果
エポック数を1にして回してみたところ終わったので構造に問題...
-9/2
現在全64パターンを学習中 画面が固まるようなことなく無事...
--混同行列…何度も出てきているので説明は割愛 対角線上に数...
--Macro F1…再現率と適合率の調和平均 0.6以上あればモデル...
--単純精度…全体で「正しいクラス(評点1〜5)」に分類できた...
**9月 [#j417c900]
***9月第1週(9/4〜9/10) [#j417c904]
-9/4
39パターン目の学習が終了した時点で強制終了となっていた。...
-9/5
中間発表に向けて準備を進めているが現地調査の結果次第で書...
・各CNNモデルごと(単体)で学習させた結果とアンサンブル学...
・概要の見直し
・概要を書き上げた部分までに該当するスライド作成
-9/7
1枚あたりの生成枚数を200枚固定, 画像の加工処理範囲を変化...
, 明るさ(通常は1.0以下だと暗、1.0以上だと明) , 縦の移動...
, (0.8、1.2) , (-0.05、0.05) , (-0.05、0.05) , 0.123 , 0....
, (0.8、1.2) , (-0.05、0.05) , (-0.1、0.1) , 0.124 , 0.82...
, (0.8、1.2) , (-0.1、0.1) , (-0.05、0.05) , 0.131 , 0.81...
, (0.8、1.2) , (-0.1、0.1) , (-0.1、0.1) , 0.128 , 0.815 ...
, (0.7、1.3) , (-0.05、0.05) , (-0.05、0.05) , 0.134 , 0....
, (0.7、1.3) , (-0.05、0.05) , (-0.1、0.1) , 0.133 , 0.81...
, (0.7、1.3) , (-0.1、0.1) , (-0.05、0.05) , 0.126 , 0.81...
, (0.7、1.3) , (-0.1、0.1) , (-0.1、0.1) , 0.134 , 0.817 ...
結果として水増しする枚数を多くしたりパラメータをいじった...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
各CNNモデル単体とアンサンブル学習の結果比較は9日の午後時...
-9/8
CNNの単一モデルとアンサンブル学習の結果を比較したところ以...
, Model , Accuracy , MAE
, EfficientNetV2S , 0.807 , 0.211
, DenseNet121 , 0.864 , 0.112
, MobileNetV3Large , 0.858 , 0.138
, Ensemble , 0.849 , 0.124
どのモデルも精度には申し分ないのだが、アンサンブルの結果...
--どうやらモデル間の相関が高すぎるとアンサンブルの結果が...
-9/9
--昨日の現地調査で撮影した写真を橋梁ごとにフォルダを作成...
"DenseNet121", "InceptionResNetV2", "Xception" の3モデル...
, Model , Accuracy , MAE
, DenseNet121 , 0.863 , 0.109
, InceptionResNetV2 , 0.862 , 0.109
, Xception , 0.857 , 0.110
, Ensemble , 0.861 , 0.103
***9月第2, 3週(9/11〜9/24) [#j417c911]
-9/11
水増し枚数を50, 100, 150, 200枚にして学習させた結果(混同...
--混同行列(左:50枚 右:100枚)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--混同行列(左:150枚 右:200枚)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
新CNNモデルの組み合わせでそれぞれ学習, アンサンブルを行っ...
--混同行列(左:DenseNet121 右:InceptionResNetV2)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--混同行列(左:Xception 右:アンサンブル)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-9/17
画像の水増し枚数は50枚, 水増しの際に画像にする加工処理は...
-9/18
CNN+binning7-2.py
CNN+binning7-1.py に 誤差解析ツール(MAE, Accuracy, Macro...
-9/22
新旧の両モデルを学習させて結果の比較を行っている 新モデ...
-9/24
今月上旬に行った現地調査で採取した錆付きテープをスキャナ...
***9月第4週(9/25〜10/1) [#j417c925]
調査で採取した錆画像を取り入れたことでラベル付き画像が既...
-元画像→水増し1回→学習
, 精度指標 , 水増し500枚 , 水増し1000枚 , 水増し1500枚 , ...
, MAE(Ensemble) , 2.576 , 2.364 , 1.931 , 3.012 , 3.15
, 正答率 , 0.304 , 0.261 , 0.217 , 0.261 , 0.217
, F1スコア , 0.093 , 0.083 , 0.071 , 0.083 , 0.071
, 各評点ごとのMAE , 水増し500枚 , 水増し1000枚 , 水増し15...
, 評点1 , 1.573 , 1.183 , 0.647 , 1.364 , 1.594
, 評点2 , 2.352 , 2.111 , 1.683 , 2.496 , 2.589
, 評点3 , 2.516 , 2.456 , 1.985 , 3.244 , 3.399
, 評点4 , 3.302 , 3.211 , 2.691 , 4.090 , 4.057
, 評点5 , 4.003 , 3.531 , 3.263 , 4.658 , 4.537
元画像114枚から水増しを1回だけ行った結果、以前に言った通...
**10月 [#j417c1000]
***10月第1, 2週(10/2〜10/15) [#j417c1002]
-10/2
学習が強制終了していたためもう一度回しているが、無事学習...
-10/9
中間発表が終わってやること
--現地調査報告書作成
--県管理の橋梁で調査できそうな耐候性鋼橋の選定
--中間発表で用いた機械学習の中身
--耐候性鋼橋のページに写真を貼り付ける
-10/13
--機械学習の中身についての説明を明日
-10/14
--中間発表で使用したモデル
import os ...
import cv2 ...
import shutil ...
import numpy as np ...
import seaborn as sns ...
import tensorflow as tf ...
from PIL import Image ...
from tensorflow.keras import layers, models, Input, Mode...
from tensorflow.keras.models import load_model ...
from tensorflow.keras.layers import Layer ...
from tensorflow.keras.callbacks import EarlyStopping, Mo...
from sklearn.model_selection import train_test_split ...
from sklearn.metrics import mean_squared_error, confusio...
from tensorflow.keras.applications import DenseNet121, I...
import matplotlib.pyplot as plt ...
import pandas as pd ...
import psutil ...
import datetime ...
import gc ...
# === 基本設定 ===
NUM_MODELS = 3 ...
TRAIN_FOLDER = "train_images" ...
TEST_FOLDER = "Fuji2" ...
BASE_RESULTS_DIR = "results_patterns1-2" ...
IMAGE_SIZE = (256, 256) ...
USE_COLOR = False ...
MODEL_PATHS = [f"ensemble_model_{i+1}.keras" for i in ra...
# === フォルダの削除・作成 ===
if os.path.exists(BASE_RESULTS_DIR): ...
shutil.rmtree(BASE_RESULTS_DIR) ...
print(f"既存のフォルダ '{BASE_RESULTS_DIR}' を削除し...
os.makedirs(BASE_RESULTS_DIR, exist_ok=True) ...
print(f"新しいフォルダ '{BASE_RESULTS_DIR}' を作成しまし...
# === 学習パターンの定義 ===
patterns = [
{'name': 'pattern_1', 'epochs': 50, 'batch_size': 32...
{'name': 'pattern_2', ...
]
#ここでパターンごとに各パラメータを設定する(name:学習...
# === 学習画像とスコアの読み込み関数 ===
def load_images_and_scores(folder):
images, scores = [], [] ...
for fname in sorted(os.listdir(folder)): ...
if fname.endswith(".png") or fname.endswith(".jpg...
try:
score = float(fname[0]) ...
score += np.random.uniform(-0.2, 0.2) ...
if score < 0.00 or score > 5.50: ...
print(f"⚠️ スコア範囲外: {fname} → {s...
continue
path = os.path.join(folder, fname) ...
img = Image.open(path).convert("RGB").res...
images.append(np.array(img) / 255.0) ...
scores.append(score) ...
except Exception as e: ...
print(f"⚠️ 読み込み失敗: {fname}, 理由: {...
return np.array(images), np.array(scores) ...
# === カスタム損失関数 ===
@register_keras_serializable()
def custom_penalizing_loss(y_true, y_pred): ...
diff = tf.abs(y_true - y_pred) ...
return tf.reduce_mean(tf.square(diff) * (1.0 + tf.po...
...
# === CNNモデル構築関数 ===
def create_model(version):
input_img = Input(shape=(*IMAGE_SIZE, 3)) ...
# CNNモデルごとに異なるベースアーキテクチャを使用
if version == 0:
base = DenseNet121(include_top=False, weights="i...
elif version == 1:
base = InceptionResNetV2(include_top=False, weig...
elif version == 2:
base = Xception(include_top=False, weights="imag...
...
base.trainable = False ...
# グローバルプーリングで特徴マップを1次元に圧縮
x = base.output
x = layers.GlobalAveragePooling2D()(x)
# 全結合層(Dense)を追加して非線形特徴を学習
for units, dr in [(2048, 0.5), (1024, 0.4), (512, 0....
x = layers.Dense(units, activation="relu")(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(dr)(x)
output = layers.Dense(1)(x)
#-----------------------展開すると-----------------------↓
x = layers.Dense(2048, activation='relu')(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(0.5)(x) # 過学習防止
x = layers.Dense(1024, activation='relu')(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(0.4)(x)
x = layers.Dense(512, activation='relu')(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(0.3)(x)
x = layers.Dense(256, activation='relu')(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(0.2)(x)
x = layers.Dense(128, activation='relu')(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(0.15)(x)
x = layers.Dense(64, activation='relu')(x)
x = layers.Dropout(0.1)(x)
output = layers.Dense(1)(x) # 最終出力は1次元の連続...
#-------------------------------------------------------...
model = Model(inputs=input_img, outputs=output) ...
model.compile(optimizer='adam', loss=custom_penalizin...
return model ...
# === 錆特徴量抽出関数 ===
def extract_features(image, use_color=False): ...
gray = np.array(image.convert("L")) ...
if use_color: #錆画像をカラーで学習させた場合
hsv = cv2.cvtColor(np.array(image), cv2.COLOR_RG...
lower_rust = np.array([5, 50, 50]) ...
upper_rust = np.array([25, 255, 255]) ...
mask = cv2.inRange(hsv, lower_rust, upper_rust) ...
else: #錆画像を白黒で学習させた場合
_, mask = cv2.threshold(gray, 128, 255, cv2.THRE...
rust_ratio = np.sum(mask > 0) / mask.size ...
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERN...
rust_count = len(contours) # 錆の数
max_blob_area = max([cv2.contourArea(c) for c in con...
return rust_ratio, rust_count, max_blob_area ...
# === CNNの回帰スコアに基づく補正関数 ===(今回は未使用)
def adjust_score_by_features(score, rust_ratio, rust_cou...
correction = 0.0 ...
if score >= 4.5:
return score ...
# 錆面積割合に基づく補正
if rust_ratio >= 0.60: ...
correction -= 0.5 ...
elif 0.45 <= rust_ratio < 0.60:
correction -= 0.3
elif 0.25 <= rust_ratio < 0.45:
correction -= 0.1
elif rust_ratio < 0.05:
correction += 0.3 ...
# 錆の個数による補正
if rust_count >= 1000: ...
correction -= 0.2 ...
elif 500 <= rust_count < 1000:
correction -= 0.1
elif rust_count < 100:
correction += 0.1
# 最大錆サイズによる補正
if max_blob_area >= 15000: ...
correction -= 0.4 ...
elif 8000 <= max_blob_area < 15000:
correction -= 0.2
elif max_blob_area < 100:
correction += 0.2
return score + correction ...
# === スコアを1〜5の離散値に変換 ===(binning関数の設定)
def binning(score):
if score < 1.9: return 1
elif score < 2.6: return 2
elif score < 3.3: return 3
elif score < 4.0: return 4
else: return 5 ...
...
# === 実験を実行するメイン関数 ===
def run_experiment(pattern, run_id): ...
print(f"\n===== 実験 {run_id}: {pattern['name']} 開...
# 出力ディレクトリを作成
output_dir = os.path.join(BASE_RESULTS_DIR, pattern[...
if os.path.exists(output_dir):
shutil.rmtree(output_dir)
os.makedirs(output_dir, exist_ok=True)
for i in range(1, 6):
os.makedirs(os.path.join(output_dir, f"score{i}"...
# 学習データの読み込み
x, y = load_images_and_scores(TRAIN_FOLDER)
total_labeled = len(y) ...
x_train, x_val, y_train, y_val = train_test_split(x,...
# === モデルの学習 or 読み込み ===
models_ensemble = [] ...
for i in range(NUM_MODELS):
model_path = os.path.join(output_dir, f"ensemble...
if os.path.exists(model_path): ...
model = tf.keras.models.load_model(model_pat...
else:# 学習済みモデルが無ければ新規作成
model = create_model(i) ...
callbacks = [
EarlyStopping(patience=5, restore_best_w...
ModelCheckpoint(model_path, save_best_on...
]
model.fit( ...
x_train, y_train,
epochs=pattern["epochs"],
batch_size=pattern["batch_size"],
validation_data=(x_val, y_val),
callbacks=callbacks,
verbose=2
)
model.save(model_path)
models_ensemble.append(model) ...
# === 評価対象(テスト画像)を処理 ===
test_fnames = sorted([f for f in os.listdir(TEST_FOL...
results = [] ...
for fname in test_fnames:
path = os.path.join(TEST_FOLDER, fname)
img = Image.open(path).convert("RGB").resize(IMA...
x_input = np.array(img) / 255.0
x_input = x_input.reshape(1, *IMAGE_SIZE, 3)
# 各モデルの予測を平均化(=アンサンブル)
preds = [m.predict(x_input, verbose=0)[0][0] for...
avg_score = np.mean(preds)
# 錆特徴量で補正
rust_ratio, rust_count, max_blob_area = extract_...
adjusted_score = adjust_score_by_features(avg_sc...
# 範囲外スコアはスキップ
if adjusted_score < 0.00 or adjusted_score > 5.50:
print(f"⚠️ スコア範囲外: {fname} → {adjusted...
continue
final_score = binning(adjusted_score) ...
save_path = os.path.join(output_dir, f"score{fin...
img.save(save_path) ...
# 結果を記録(CSV出力用)
results.append({
"ファイル名": fname,
"回帰スコア": round(avg_score, 3),
"補正後スコア": round(adjusted_score, 3),
"評点": final_score,
"錆割合": round(rust_ratio, 3),
"錆の数": rust_count,
"錆の最大サイズ": round(max_blob_area, 1),
})
# === 予測結果CSVを保存 ===
pd.DataFrame(results).to_csv(os.path.join(output_dir...
...
# === 評価指標の算出 ===
val_preds = [m.predict(x_val, verbose=0).flatten() fo...
y_val_pred = np.mean(val_preds, axis=0) ...
# 予測スコアを1〜5のクラスラベルに変換(分類評価用)
y_val_bin = np.array([binning(s) for s in y_val_pred]...
y_true_bin = np.array([binning(s) for s in y_val])
# === MAE(平均絶対誤差) ===
mae_score = mean_absolute_error(y_val, y_val_pred)
print(f"MAE:{mae_score}") ...
# === Accuracy(単純精度) ===
from sklearn.metrics import accuracy_score, f1_score ...
acc_score = accuracy_score(y_true_bin, y_val_bin) ...
# === Macro平均F1スコア ===
macro_f1 = f1_score(y_true_bin, y_val_bin, average="m...
# === 混同行列の保存(どのクラスを誤分類したか可視化...
pattern_dir = os.path.join("results_patterns", patter...
os.makedirs(pattern_dir, exist_ok=True) ...
cm = confusion_matrix(y_true_bin, y_val_bin, labels=[...
plt.figure(figsize=(10.8, 7.2)) ...
disp = ConfusionMatrixDisplay(confusion_matrix=cm, di...
disp.plot(cmap="Blues", values_format="d") ...
plt.title("Confusion Matrix (Validation Set)") ...
plt.savefig(os.path.join(output_dir, "confusion_matri...
plt.close()
# === UMAP可視化 ===
feature_model = Model(inputs=models_ensemble[0].input...
features_val = feature_model.predict(x_val, verbose=0...
...
umap = UMAP(n_neighbors=15, min_dist=0.1, random_stat...
embedding = umap.fit_transform(features_val) ...
plt.figure(figsize=(10.8, 7.2))
scatter = plt.scatter(embedding[:, 0], embedding[:, 1...
plt.colorbar(scatter, ticks=[1, 2, 3, 4, 5]) ...
plt.title("UMAP Visualization of Validation Features"...
plt.savefig(os.path.join(output_dir, "umap_val.png"))...
plt.close()
# === メモリ使用量の可視化(強制終了の原因調査用) ===
vm = psutil.virtual_memory() ...
total_gb = vm.total / (1024 ** 3) ...
used_gb = (vm.total - vm.available) / (1024 ** 3) ...
percent = vm.percent ...
print(f"💾 System Memory usage after {pattern['name']...
...
# === クラス別MAEと±1精度 ===
def per_class_mae(y_true, y_pred, binning_fn):
y_true_bin = np.array([binning_fn(v) for v in y_t...
y_pred_bin = np.array([binning_fn(v) for v in y_p...
res = {}
for c in np.unique(y_true_bin):
mask = (y_true_bin == c)
if mask.sum() > 0:
res[int(c)] = mean_absolute_error(y_true[...
return res
def within_one_rate(y_true, y_pred, binning_fn):
y_t = np.array([binning_fn(v) for v in y_true])
y_p = np.array([binning_fn(v) for v in y_pred])
return (np.abs(y_t - y_p) <= 1).mean() ...
...
class_mae = per_class_mae(y_val, y_val_pred, binning)
one_acc = within_one_rate(y_val, y_val_pred, binning)
print("クラス別MAE:", class_mae)
print("±1精度:", one_acc)
...
# === 残差プロットと誤差分布 ===
res = y_val_pred - y_val ...
plt.scatter(y_val, y_val_pred, s=6, alpha=0.6)
plt.plot([0, 5], [0, 5], 'r--') ...
plt.xlabel("True"); plt.ylabel("Predicted")
plt.savefig(os.path.join(output_dir, "true_vs_pred.pn...
plt.close()
plt.hist(res, bins=40, color="gray")
plt.title("Residuals")
plt.savefig(os.path.join(output_dir, "residuals.png"))
plt.close()
...
# === モデル間誤差の相関(アンサンブル効果を定量化) ...
if len(val_preds) >= 2:
from scipy.stats import pearsonr ...
errors = [np.abs(y_val - p) for p in val_preds]
corr = np.zeros((len(errors), len(errors))) ...
for i in range(len(errors)):
for j in range(len(errors)):
corr[i, j], _ = pearsonr(errors[i], error...
pd.DataFrame(corr).to_csv(os.path.join(output_dir...
print("誤差相関行列を保存しました")
...
# === モデル・変数を明示的に解放 ===
del models_ensemble ...
del x_train, x_val, y_train, y_val ...
tf.keras.backend.clear_session() ...
gc.collect() ...
print(f"===== 実験 {run_id}: {pattern['name']} 完了 =...
# === モデル・データを完全解放 ===
del models_ensemble
del x_train, x_val, y_train, y_val
tf.keras.backend.clear_session()
gc.collect()
print(f"===== 実験 {run_id}: {pattern['name']} 完了 =...
...
# === 実験条件と評価結果の記録 ===
summary = {
"name": pattern["name"], ...
"epochs": pattern["epochs"], ...
"batch_size": pattern["batch_size"], ...
"num_aug": pattern["num_aug"], ...
"rotation": pattern["rotation"], ...
"brightness": str(pattern["brightness"]), ...
"width_shift": str(pattern["width_shift"]), ...
"height_shift": str(pattern["height_shift"]), ...
"total_train_samples": total_labeled * pattern["n...
"mae": mae_score, ...
"accuracy": acc_score, ...
"macro_f1": macro_f1, ...
"class_mae": str(class_mae), ...
"within_one_acc": one_acc, ...
"datetime": str(datetime.datetime.now()) ...
}
pd.Series(summary).to_csv(os.path.join(output_dir, "s...
return summary ...
# === メイン処理 ===
if __name__ == "__main__":
all_results = [] ...
for idx, pat in enumerate(patterns): ...
summary = run_experiment(pat, idx)
all_results.append(summary)
# === 全実験の集計結果をCSVで保存 ===
df_all = pd.DataFrame(all_results)
df_all.to_csv(os.path.join(BASE_RESULTS_DIR, "all_re...
print(f"✅ 全パターンの集計結果を {os.path.join(BASE_R...
--上記のスクリプトを実行すると以下のファイルが保存される
・results_patterns1-2 # 各学習パターンの結果をまとめ...
|
|__ pattern_1 # 1つの学習パターン(=特定の学...
| |
| |__ ensemble_model_1.keras, ensemble...
| | ...
| | ...
| | ...
| |
| |_ confusion_matrix.png ...
| | ...
| | ...
| |
| |_ error_corr.csv ...
| | ...
| |
| |_ residuals.png ...
| | ...
| | ...
| |_ rust_score_with_features.csv ...
| |
| |_ summary.csv ...
| |
| |_ true_vs_pred.png ...
| | ...
| |
| |_ umap_val.png ...
| ...
| ...
|
|_ pattern_2___ ensemble_model_1.keras, ensemble...
| |_ confusion_matrix.png
| |_ ...
|_ ...
-10/15
ラベル付き画像全114枚から1枚あたり50枚水増ししたのち再度...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--MAEに関して250枚までは改善傾向が見られたが、500枚前後で...
続いて正答率は50枚で約0.893と高く、150〜1000枚あたりでや...
F1スコアについて同様に50枚〜2000枚付近で0.83〜0.87の範囲...
以上より最も良いバランスはMAE=0.074、正答率=0.895、F1=...
--評点別MAEの変化
--⇒評点1・2といった重度錆は全体的に誤差が小さく、0.06〜0....
--⇒評点3の中程度の錆は最もデータが多い領域で誤差が最少 ...
--⇒評点4・5の軽度錆はやや誤差が大きい → 他クラスよりも...
---水増しは1枚あたり500〜2000枚が理想
---3000枚を超えると、類似画像の増加や過学習の影響でわずか...
---2回水増しで水増し1回よりも少ない枚数で良好な結果が得ら...
***10月第3週(10/16〜10/22) [#j417c1016]
-10/16
--.keras ファイルを用いて錆画像の判別を行ってみた。学習元...
・補正値 ...
def adjust_score_by_features(score, rust_ratio, rust_cou...
correction = 0.0 ...
...
if score >= 4.5: ...
# 高スコアはそのまま返す(補正・clipなし) ...
return score ...
...
if rust_ratio >= 0.60: ...
correction -= 0.5 ...
elif 0.45 <= rust_ratio < 0.60: ...
correction -= 0.3 ...
elif 0.25 <= rust_ratio < 0.45: ...
correction -= 0.1 ...
elif rust_ratio < 0.05: ...
correction += 0.3 ...
...
if rust_count >= 1000: ...
correction -= 0.2 ...
elif 500 <= rust_count < 1000: ...
correction -= 0.1 ...
elif rust_count < 100: ...
correction += 0.1 ...
...
if max_blob_area >= 15000: ...
correction -= 0.4 ...
elif 8000 <= max_blob_area < 15000: ...
correction -= 0.2 ...
elif max_blob_area < 100: ...
correction += 0.2 ...
...
return score + correction ...
--画像の名前について
--→頭の数字が評点になっている(例:1-111 ⇒ 評点1, 2-201 ⇒...
--使用画像(カラー 左から評点1, 2, ... 5の順で並んでいる)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--使用画像(モノクロ 左から評点1, 2, ... 5の順で並んでい...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--判別結果
, 画像 , 補正前評点 , 補正後評点 , 回帰スコア , 補正後ス...
, 1-01.png , 3 , 2 , 3.102 , 2.302 , 0.719 , 33 , 87309.0
, 2-01.png , 3 , 2 , 3.151 , 2.451 , 0.379 , 1480 , 20794...
, 3-01.png , 3 , 2 , 3.196 , 2.396 , 0.817 , 12 , 88528.0
, 4-01.png , 4 , 3 , 3.455 , 2.655 , 0.914 , 1 , 89187.5
, 5-01.png , 3 , 2 , 3.105 , 2.305 , 0.953 , 2 , 89268.5
, 1-11.jpg , 1 , 1 , 0.967 , 0.467 , 0.359 , 359 , 19832.0
, 2-11.jpg , 3 , 3 , 2.762 , 2.662 , 0.274 , 451 , 3808.5
, 2-12.jpg , 3 , 3 , 2.882 , 2.682 , 0.266 , 722 , 4102.0
, 3-11.jpg , 4 , 4 , 3.575 , 3.375 , 0.163 , 1348 , 527.5
, 4-11.jpg , 5 , 5 , 4.933 , 4.933 , 0.042 , 707 , 236.5
, 5-11.jpg , 5 , 5 , 4.936 , 4.936 , 0.005 , 178 , 153.5
補正関数を組み込んでも評点が変化することはあまりないこと...
-10/17
現地調査で撮影したカラーの錆画像を台形補正するスクリプト...
[[台形補正で参考にしたページ:https://qiita.com/otofu_dayo...
-10/20
台形変換のスクリプトがとりあえず完成したので貼り付けてお...
◯指定フォルダ内の画像に対して「白いプレート枠を検出して...
1,画像読み込み
2,グレースケール変換と二値化
3,ノイズ除去(モルフォロジー演算+ラベリング)
4,最大矩形領域の抽出
5,結果画像の保存
--------------------------------------------------------...
import cv2
import numpy as np
import os
import shutil
from glob import glob
# === 設定 ===
INPUT_DIR = "drone_images" # ★入力画像フォルダ
OUTPUT_DIR = "processed_patches3" # ★出力フォルダ
REF_WIDTH = 1000 # ★出力画像の横サイズ
REF_HEIGHT = 750 # ★出力画像の縦サイズ
os.makedirs(OUTPUT_DIR, exist_ok=True)
# === フォルダーの削除・作成 ===
folder_name = OUTPUT_DIR
if os.path.exists(folder_name):
shutil.rmtree(folder_name)
print(f"既存のフォルダ '{folder_name}' を削除しまし...
os.makedirs(folder_name, exist_ok=True)
print(f"新しいフォルダ '{folder_name}' を作成しました。")
# === 1. プレート領域のマスク処理 ===
def mask_plate_region(img, threshold_mode="otsu"): ...
"""
撮影画像から白いプレート枠領域を抽出してマスク化する。
"""
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ...
...
blur = cv2.GaussianBlur(gray, (5, 5), 0) ...
...
if threshold_mode == "otsu":
_, binary = cv2.threshold(blur, 0, 255, cv2.THRE...
elif threshold_mode == "adaptive":
binary = cv2.adaptiveThreshold(blur, 255,
cv2.ADAPTIVE_THRE...
cv2.THRESH_BINARY...
else:
raise ValueError("threshold_mode must be 'otsu' ...
# 白黒反転(白枠が白くなるように)
if np.mean(binary) < 127:
binary = cv2.bitwise_not(binary)
return binary ...
# === 2. ノイズ除去処理 ===
def remove_noise(binary_mask):
"""
二値化画像から小さなノイズを除去し、白枠を明確化する。
"""
kernel = np.ones((5, 5), np.uint8) ...
opened = cv2.morphologyEx(binary_mask, cv2.MORPH_OPE...
closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, k...
# ラベリング処理で各領域を集計し、面積500以上の領域...
num_labels, labels, stats, _ = cv2.connectedComponen...
cleaned = np.zeros_like(closed)
for i in range(1, num_labels): ...
area = stats[i, cv2.CC_STAT_AREA]
if area > 500:
cleaned[labels == i] = 255 ...
return cleaned ...
# === 3. 輪郭の取得と近似化 ===
def detect_plate_contour(img, cleaned_mask):
"""
白枠プレートの輪郭を検出し、最も大きな矩形を返す。
"""
contours, _ = cv2.findContours(cleaned_mask, cv2.RET...
if not contours:
print("⚠️ 輪郭が検出されませんでした。")
return img, None
contours = sorted(contours, key=cv2.contourArea, rev...
# 最も大きい輪郭を選択
largest_contour = contours[0]
epsilon = 0.02 * cv2.arcLength(largest_contour, True...
approx = cv2.approxPolyDP(largest_contour, epsilon, ...
# 4頂点に近い輪郭のみ採用し、緑枠で描画し角座標を出...
contour_img = img.copy()
if len(approx) == 4:
cv2.drawContours(contour_img, [approx], -1, (0, ...
pts = approx.reshape(4, 2)
print(f"🟩 四角形の頂点座標: {pts}")
return contour_img, pts
else:
print(f"⚠️ 4頂点ではありません({len(approx)}点...
cv2.drawContours(contour_img, [largest_contour],...
return contour_img, None ...
# === 4. メイン処理 ===
for fname in os.listdir(INPUT_DIR):
if not fname.lower().endswith((".jpg", ".jpeg", ".pn...
continue ...
img_path = os.path.join(INPUT_DIR, fname)
img = cv2.imread(img_path) ...
if img is None:
print(f"⚠️ 読み込み失敗: {fname}")
continue
mask = mask_plate_region(img, threshold_mode="otsu")...
cleaned = remove_noise(mask) ...
contour_img, pts = detect_plate_contour(img, cleaned...
# --- 保存 ---
save_path = os.path.join(OUTPUT_DIR, f"contour_{fnam...
cv2.imwrite(save_path, contour_img)
print(f"✅ 輪郭検出結果を保存しました: {save_path}")
-10/21
--昨日のスクリプトで輪郭の取得と近似化に修正を加えた
# === 3. 輪郭の取得と近似化 ===
def detect_plate_contour(img, cleaned_mask):
"""
白枠プレートの輪郭を検出し、内側スケール矩形のみを抽...
外枠(画像外周)は無視。
条件:
- 面積が 20000〜1450×1060 の範囲
- アスペクト比が 1.0〜a の範囲
- 図形中心が画像の外周b%以内でない
- 各角の角度が c°〜d° の範囲
"""
h, w = img.shape[:2]
max_allowed_area = 1450 * 1060
min_allowed_area = 20000
# --- ① 外枠除外マスク ---
inner_mask = np.zeros_like(cleaned_mask)
margin = int(min(h, w) * 0.12) # 外周12%を無視
inner_mask[margin:h - margin, margin:w - margin] = 255
masked = cv2.bitwise_and(cleaned_mask, inner_mask)
# --- ② 輪郭検出 ---
contours, _ = cv2.findContours(masked, cv2.RETR_EXTE...
if not contours:
print("⚠️ 輪郭が検出されませんでした。")
return img, None
valid_candidates = []
for cnt in contours:
area = cv2.contourArea(cnt)
if area < min_allowed_area or area > max_allowed...
continue
rect = cv2.minAreaRect(cnt)
(cx, cy), (rw, rh), angle = rect
# --- ③ 中心位置チェック(外周に寄りすぎていない...
if cx < margin or cx > (w - margin) or cy < marg...
continue
# --- ④ アスペクト比チェック ---
aspect_ratio = max(rw, rh) / (min(rw, rh) + 1e-6)
if not (1.0 <= aspect_ratio <= 2.5):
continue
# --- ⑤ 近似と角度判定 ---
epsilon = 0.02 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
if len(approx) != 4:
continue
pts = approx.reshape(4, 2)
angles = []
for i in range(4):
p1, p2, p3 = pts[i], pts[(i + 1) % 4], pts[(...
v1, v2 = p1 - p2, p3 - p2
cos_angle = np.dot(v1, v2) / (np.linalg.norm...
angle = np.degrees(np.arccos(np.clip(cos_ang...
angles.append(angle)
if all(69 <= a <= 111 for a in angles):
valid_candidates.append((area, pts))
# --- ⑥ 最も適した候補を選択 ---
if not valid_candidates:
print("⚠️ 有効な内枠が見つかりませんでした。")
return img, None
best_area, best_pts = max(valid_candidates, key=lamb...
contour_img = img.copy()
cv2.drawContours(contour_img, [best_pts.astype(int)]...
print(f"🟩 内枠矩形を検出(面積={best_area:.0f}, ア...
return contour_img, best_pts
上記に示したスクリプトを Scalehosei3.py と定義し撮影した...
-スクリプトの処理内容について
--1, はじめに現地で撮影した錆画像を用意する。多少画面がぶ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--2, Scalehosei3.py を実行すると最初に画像の明るい部分は...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--3, 次に 2 で出力された画像から内枠の認識に影響を及ぼす...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--4, 3 からカラースケールの内枠を抽出して元の画像に出力&...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-10/22
色補正を行う前段階としてカラースケールのカラーを切り抜く...
--今後の色補正スクリプトの作成目標
---1, 切り抜いた画像を純色(255)に変換する 変換過程を混...
---2, 混同行列をもとに画像全体のRGB値を変換する
---3, 色補正と台形補正をどちらから行うかについては後々考...
import cv2
import numpy as np
import os
import shutil ...
INPUT_DIR = "processed_patches3"
OUTPUT_DIR = "processed_patches4"
os.makedirs(OUTPUT_DIR, exist_ok=True) ...
# === フォルダーの削除・作成 ===
folder_name = OUTPUT_DIR
if os.path.exists(folder_name):
shutil.rmtree(folder_name)
print(f"既存のフォルダ '{folder_name}' を削除しまし...
os.makedirs(folder_name, exist_ok=True)
print(f"新しいフォルダ '{folder_name}' を作成しました。")
def detect_and_extract_color_scale(img): ...
h, w = img.shape[:2]
roi = img[:, int(w * 0.65):] ...
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) ...
color_ranges = {
'red1': ((0, 100, 100), (10, 255, 255)),
'red2': ((160, 100, 100), (180, 255, 255)),
'yellow': ((20, 100, 100), (40, 255, 255)),
'green': ((40, 50, 50), (90, 255, 255)), ...
'blue': ((90, 50, 50), (150, 255, 255)), ...
}
mask_red = cv2.bitwise_or(cv2.inRange(hsv, *color_ra...
mask_yellow = cv2.inRange(hsv, *color_ranges['yellow...
mask_green = cv2.inRange(hsv, *color_ranges['green']...
mask_blue = cv2.inRange(hsv, *color_ranges['blue']) ...
combined_mask = cv2.bitwise_or(cv2.bitwise_or(mask_r...
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (...
combined_mask = cv2.morphologyEx(combined_mask, cv2....
contours, _ = cv2.findContours(combined_mask, cv2.RE...
if not contours:
print("カラースケール領域検出なし")
return img, None
candidates = []
for cnt in contours:
rect = cv2.minAreaRect(cnt)
(cx, cy), (width, height), angle = rect ...
if width == 0 or height == 0:
continue ...
aspect_ratio = max(width, height) / min(width, h...
area = width * height ...
if area > 0.001 * roi.shape[0] * roi.shape[1] an...
box = cv2.boxPoints(rect)
box = np.int0(box) ...
candidates.append((area, box, rect)) ...
if not candidates:
print("適合するカラースケール候補がありません")
return img, None
largest_candidate = max(candidates, key=lambda c: c[...
area, box, rect = largest_candidate ...
box[:,0] += int(w * 0.65) ...
img_with_box = img.copy()
cv2.drawContours(img_with_box, [box], 0, (0,255,0), ...
width = int(rect[1][0])
height = int(rect[1][1])
if width == 0 or height == 0:
print("切り出し用サイズ不正")
return img_with_box, None ...
dst_pts = np.array([[0,0],[width-1,0],[width-1,heigh...
src_pts = box.astype(np.float32) ...
M = cv2.getPerspectiveTransform(src_pts, dst_pts) ...
extracted = cv2.warpPerspective(img, M, (width, heig...
extracted = cv2.resize(extracted, (height, width)) ...
return img_with_box, extracted ...
def process_images(in_dir, out_dir): ...
os.makedirs(out_dir, exist_ok=True)
for fn in os.listdir(in_dir):
if fn.lower().endswith(('.png','.jpg','.jpeg','....
path_in = os.path.join(in_dir, fn)
path_out = os.path.join(out_dir, fn)
path_crop = os.path.join(out_dir, "crop_"+fn...
img = cv2.imread(path_in)
if img is None:
print(f"{fn} 読み込み失敗")
continue
img_boxed, crop_img = detect_and_extract_col...
cv2.imwrite(path_out, img_boxed)
print(f"{fn} に枠を描画") ...
if crop_img is not None:
cv2.imwrite(path_crop, crop_img)
print(f"{fn} の切り出し領域を保存") ...
if __name__ == "__main__": #メイン実行部。
process_images(INPUT_DIR, OUTPUT_DIR) ...
まとめると
1, 右側35%領域を「カラースケール候補」として抽出。
2, HSVで赤・黄・緑・青の色領域を検出。
3, 条件(面積・縦横比)を満たす最も大きな矩形をカラース...
4, 緑枠で描画して切り抜き画像を保存。
このスクリプトを通じて画像を変換させると元画像(入力画像...
-1, 元画像
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-2, 枠付き画像
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-3, スケール切り抜き画像
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
***10月第4週(10/23〜10/29) [#j417c1023]
-10/23
スクリプトの案内
-Scalehosei1.py ... モノクロ変換
-Scalehosei2.py ... モノクロ変換+ノイズ除去
-Scalehosei3.py ... モノクロ変換+ノイズ除去+台形補正前...
-Scalehosei4.py ... 色補正前段階・カラースケール抽出
-Scalehosei5.py ... カラースケール補正→変換行列作成・出力
-Scalehosei6.py ... 変換行列で元画像を色補正
とりあえず色補正を行う所までスクリプトを組むことができた...
-10/26
カラースケールを純色にRGBの3×3の行列で変換した後その行列...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-10/27
色補正スクリプトの方でRGB毎に影響の強弱を付けられるように...
-10/29
色補正後の赤みをある程度抑えられ、先に色補正を行ってから...
--1段目:色補正
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--2段目:錆枠抽出
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--3段目:台形変換
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
**11月 [#j417c1100]
***11月第1週(10/30〜11/05) [#j417c1030]
-10/30
--旧モデル
, モデル名 , MAE , 正答率 , F1スコア
, EfficientNetV2S , 0.3029 , 0.7865, 0.7623
, DenseNet121 , 0.0888 , 0.9912 , 0.9891
, MobileNetV3Large , 0.1656 , 0.9438 , 0.9409
, ensemble , 0.1456 , 0.9859 , 0.9827
--新モデル
, モデル名 , MAE , 正答率 , F1スコア
, DenseNet121 , 0.0841 , 0.9859 , 0.9819
, InceptionResNetV2 , 0.1649 , 0.9675 , 0.9651
, Xception , 0.1215 , 0.9772 , 0.9743
, ensemble , 0.0988 , 0.9938 , 0.9923
--新・旧のDenseNet121
, モデル名 , MAE , 正答率 , F1スコア
, 旧モデルDenseNet121 , 0.0888 , 0.9912 , 0.9891
, 新モデルDenseNet121 , 0.0841 , 0.9859 , 0.9819
同じ DenseNet121 を用いているのに結果が微妙に異なるのは学...
◯主原因1:初期化の乱数
TensorFlow / Keras の重み初期値は乱数で生成されることで同...
-対策: スクリプトの先頭で乱数シードを固定(再現性の確保)
◯主原因2:データ分割のランダム性
train_test_split を使うとrandom_state=42 のように固定しな...
-対策:
x_train, x_val, y_train, y_val = train_test_split(x, y, ...
◯主原因3:アンサンブル全体の重み干渉
アンサンブル処理内でモデル学習順序が異なる, または他のモ...
--来週行う内容
---白黒のラベル付き錆画像(114枚)を学習させた結果をもと...
---カラーとドローンで撮影したものは白黒に変換
---カラーの方は256ピクセル四方に切り抜いた後評点判別にか...
--やること
---錆画像の切り抜き → 白黒で学習にかける
---ドローン画像 → 枠で囲まれた部分が評点◯というようなスク...
-10/31
-- 乱数シード固定(tf.random.set_seedなど)
-- 同じデータセット・分割を使う
-- 同じ前処理(正規化やaugmentation)を維持
-- GPU/CPUなど実行環境も統一
上記の条件下で新・旧モデルのDenseNet121を比較した。
-旧モデル
, モデル名 , MAE , 正答率 , F1スコア
, EfficientNetV2S , 0.332241619945153 , 0.687170474516696...
, DenseNet121 , 0.123156605893157 , 0.806678383128295 , 0...
, MobileNetV3Large , 0.19573938891256 , 0.783831282952548...
, Ensemble , 0.177296200058912 , 0.807557117750439 , 0.77...
-新モデル
, モデル名 , MAE , 正答率 , F1スコア
, InceptionResNetV2 , 0.153177455220403 , 0.7970123022847...
, DenseNet121 , 0.147729756383773 , 0.794376098418278 , 0...
, Xception , 0.142235872050986 , 0.808435852372584 , 0.77...
, Ensemble , 0.12868636939779 , 0.812829525483304 , 0.783...
-DenseNet121
, モデル名 , MAE , 正答率 , F1スコア
, 旧モデルDenseNet121 , 0.123156605893157 , 0.80667838312...
, 新モデルDenseNet121 , 0.147729756383773 , 0.79437609841...
, (新・旧)の差 , 0.024573150490616 , -0.012302284710016...
両学習モデルで乱数シードを固定し学習条件を統一した上で訓...
◯GPU演算の非決定性:CuDNN(NVIDIAのディープラーニングライ...
◯os, numpy, tensorflow のランダム初期化:スクリプトでは s...
◯並列スレッドおよびGPUメモリスケジューリング:マルチスレ...
◯EarlyStoppingとModelCheckpointの影響:各エポック終了時の...
-つまり同一環境・同一コードで学習を行ってもGPU演算の非決...
-11/4
宮城県の橋梁調査データをもらい学習の事前準備を行った
--セロテープ画像 … 1つの画像に何枚も貼ってあったためセロ...
--近接画像 … 以前作成した台形変換・色補正スクリプトで枠の...
***11月第2週(11/6〜11/12) [#j417c1106]
-11/6
色補正・台形変換に関する各スクリプトに変更を加えたので以...
-Scalehosei1.py
import cv2 ...
import numpy as np ...
import os ...
import shutil ...
INPUT_DIR = "drone_images" ...
OUTPUT_DIR = "processed_patches1" ...
os.makedirs(OUTPUT_DIR, exist_ok=True) ...
# === フォルダーの削除・作成 ===
folder_name = OUTPUT_DIR
if os.path.exists(folder_name):
shutil.rmtree(folder_name) ...
print(f"既存のフォルダ '{folder_name}' を削除しまし...
os.makedirs(folder_name, exist_ok=True) ...
print(f"新しいフォルダ '{folder_name}' を作成しました。")
# === 拡張子統一 (.JPG, .JPEG → .jpg) ===
def unify_extensions(folder): ...
for fname in os.listdir(folder): ...
old_path = os.path.join(folder, fname) ...
if not os.path.isfile(old_path): ...
continue
base, ext = os.path.splitext(fname) ...
ext_lower = ext.lower()
if ext_lower in [".jpeg", ".jpg", ".png", ".tif"...
new_path = os.path.join(folder, base + ".jpg...
if new_path != old_path: ...
os.rename(old_path, new_path)
print(f"🔄 拡張子統一: {fname} → {os.pat...
unify_extensions(INPUT_DIR) ...
def detect_and_extract_color_scale(img): ...
h, w = img.shape[:2]
roi = img[:, int(w * 0.65):] ...
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) ...
color_ranges = { ...
'red1': ((0, 120, 80), (10, 255, 255)),
'red2': ((160, 120, 80), (180, 255, 255)),
'yellow': ((20, 150, 150), (35, 255, 255)), ...
'green': ((45, 80, 80), (85, 255, 255)),
'blue': ((90, 80, 80), (130, 255, 255)),
}
mask_red = cv2.bitwise_or(cv2.inRange(hsv, *color_ra...
mask_yellow = cv2.inRange(hsv, *color_ranges['yellow...
mask_green = cv2.inRange(hsv, *color_ranges['green'])
mask_blue = cv2.inRange(hsv, *color_ranges['blue']) ...
combined_mask = cv2.bitwise_or(cv2.bitwise_or(mask_r...
# --- ② ノイズ除去・スムージング ---
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (...
combined_mask = cv2.morphologyEx(combined_mask, cv2....
combined_mask = cv2.morphologyEx(combined_mask, cv2....
contours, _ = cv2.findContours(combined_mask, cv2.RE...
candidates = [] ...
for cnt in contours: ...
rect = cv2.minAreaRect(cnt) ...
(cx, cy), (width, height), angle = rect
if width == 0 or height == 0:
continue
aspect_ratio = max(width, height) / (min(width, ...
area = width * height ...
# --- ③ 縦長・面積条件 ---
if area > 0.005 * roi.shape[0] * roi.shape[1] an...
box = cv2.boxPoints(rect)
box = np.int0(box)
candidates.append((area, box, rect))
# === 自動検出失敗時の手動選択 ===
if not candidates: ...
print("⚠️ 適合するカラースケール候補がありません...
manual_points = [] ...
#以下「右クリックで追加・左クリックで取り消し」というイ...
def mouse_callback(event, x, y, flags, param):
if event == cv2.EVENT_RBUTTONDOWN: ...
manual_points.append((x, y))
cv2.drawMarker(param, (x, y), (0, 255, 0...
cv2.imshow("manual_select", param)
print(f"✅ {len(manual_points)}点目 ({x}...
elif event == cv2.EVENT_LBUTTONDOWN: ...
if manual_points:
manual_points.pop()
print(f"↩️ 直前の点を削除。残り{len(...
temp = img.copy()
for px, py in manual_points:
cv2.drawMarker(temp, (px, py), (...
cv2.imshow("manual_select", temp)
temp = img.copy()
cv2.imshow("manual_select", temp)
cv2.setMouseCallback("manual_select", mouse_call...
print("🖱️ 右クリックで4点選択、左クリックで直前...
while True:
key = cv2.waitKey(0)
if key in [13, 10]: ...
break
cv2.destroyWindow("manual_select")
if len(manual_points) != 4:
print("⚠️ 4点が選択されませんでした。スキッ...
return img, None
src_pts = np.array(manual_points, dtype=np.float...
w_scale = int(np.linalg.norm(src_pts[0] - src_pt...
h_scale = int(np.linalg.norm(src_pts[1] - src_pt...
dst_pts = np.array([[0,0],[w_scale-1,0],[w_scale...
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
extracted = cv2.warpPerspective(img, M, (w_scale...
cv2.destroyAllWindows()
return img, extracted
# --- 自動検出成功時 ---
#最も大きい領域を選び元画像上に枠を描画して射影変換...
area, box, rect = max(candidates, key=lambda c: c[0])
box[:,0] += int(w * 0.65)
img_with_box = img.copy()
cv2.drawContours(img_with_box, [box], 0, (0,255,0), 3)
width = int(rect[1][0])
height = int(rect[1][1])
dst_pts = np.array([[0,0],[width-1,0],[width-1,heigh...
src_pts = box.astype(np.float32)
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
extracted = cv2.warpPerspective(img, M, (width, heig...
extracted = cv2.resize(extracted, (height, width)) ...
return img_with_box, extracted
def process_images(in_dir, out_dir): ...
os.makedirs(out_dir, exist_ok=True)
for fn in os.listdir(in_dir): ...
if fn.lower().endswith(('.png','.jpg','.jpeg','....
path_in = os.path.join(in_dir, fn)
path_out = os.path.join(out_dir, fn)
path_crop = os.path.join(out_dir, "crop_"+fn)
img = cv2.imread(path_in) ...
if img is None:
print(f"{fn} 読み込み失敗")
continue
img_boxed, crop_img = detect_and_extract_col...
cv2.imwrite(path_out, img_boxed) ...
print(f"{fn} に枠を描画")
if crop_img is not None:
cv2.imwrite(path_crop, crop_img) ...
print(f"{fn} の切り出し領域を保存")
if __name__ == "__main__":
process_images(INPUT_DIR, OUTPUT_DIR) ...
このスクリプトは「ドローン画像」フォルダ内の画像ファイル...
-Scalehosei2.py
import cv2
import numpy as np
import os
import shutil
INPUT_DIR = "processed_patches1"
OUTPUT_DIR = "processed_patches2"
MATRIX_DIR = "processed_patches2"
os.makedirs(OUTPUT_DIR, exist_ok=True)
os.makedirs(MATRIX_DIR, exist_ok=True)
folder_name = OUTPUT_DIR
if os.path.exists(folder_name):
shutil.rmtree(folder_name)
print(f"既存のフォルダ '{folder_name}' を削除しまし...
os.makedirs(folder_name, exist_ok=True)
print(f"新しいフォルダ '{folder_name}' を作成しました。")
# 純色BGR値(OpenCVはBGR順)
pure_colors = [ #画像上の標準「ターゲット純色」BGR値
(0, 0, 255), # 赤
(0, 255, 255), # 黄
(0, 255, 0), # 緑
(255, 0, 0) # 青
]
def create_color_conversion_matrix(img): ...
h, w = img.shape[:2] ...
conversion_matrix = []
if h > w:
# 縦長:縦方向に4分割(色ごとの帯が縦に配置され...
segment_h = h // 4
for i in range(4):
region = img[i*segment_h:(i+1)*segment_h if ...
avg_bgr = region.mean(axis=(0,1)) ...
conversion_matrix.append({
'segment': i,
'avg_bgr': avg_bgr,
'target_color': pure_colors[i]
})
else:
# 横長:横方向に4分割
segment_w = w // 4
# 横長は左から [青, 緑, 黄, 赤] のため色配列を逆...
reversed_colors = pure_colors[::-1]
for i in range(4):
region = img[:, i*segment_w:(i+1)*segment_w ...
avg_bgr = region.mean(axis=(0,1))
conversion_matrix.append({
'segment': i,
'avg_bgr': avg_bgr,
'target_color': reversed_colors[i]
})
return conversion_matrix
def save_conversion_matrix(matrix, filename): ...
np.save(filename, matrix)
def apply_pure_colors(img, matrix): ...
h, w = img.shape[:2] ...
result = np.zeros_like(img)
if h > w:
# 縦長:縦方向に4分割
segment_h = h // 4
for entry in matrix:
i = entry['segment']
color = entry['target_color']
start_y = i * segment_h
end_y = (i + 1) * segment_h if i < 3 else h
result[start_y:end_y, :] = color
else:
# 横長:横方向に4分割
segment_w = w // 4
for entry in matrix:
i = entry['segment']
color = entry['target_color']
start_x = i * segment_w
end_x = (i + 1) * segment_w if i < 3 else w
result[:, start_x:end_x] = color
return result
#指定ディレクトリ内の全画像を処理
#ファイルごとに読込→変換行列作成・保存→純色置換画像生成→...
#例外処理も加味
def process_images(input_dir, output_dir, matrix_dir):
for fn in os.listdir(input_dir):
if not fn.lower().endswith(('.png','.jpg','.jpeg...
continue
path_in = os.path.join(input_dir, fn)
img = cv2.imread(path_in)
if img is None:
print(f"{fn} 読み込み失敗")
continue
matrix = create_color_conversion_matrix(img)
# 拡張子なしのファイル名を取得
base_name = os.path.splitext(fn)[0]
# .npy保存時には拡張子なしファイル名にする
save_path = os.path.join(matrix_dir, base_name +...
save_conversion_matrix(matrix, save_path)
pure_img = apply_pure_colors(img, matrix)
path_out = os.path.join(output_dir, fn)
cv2.imwrite(path_out, pure_img)
print(f"{fn} 処理完了:変換行列保存と純色画像保...
if __name__ == "__main__":
process_images(INPUT_DIR, OUTPUT_DIR, MATRIX_DIR) #メ...
このスクリプトは抽出したカラースケール画像の各色領域の平...
-なぜ色補正に「黄色」を加えたか
→多くの画像処理・印刷系カラースケールやカラーチャートで...
・黄色は「赤+緑」成分が強調された複合色であり、RGBチャ...
・実際のカメラ画像やプリンタ特性, スキャナ入力の現場では...
・黄色チャネルの平均値を併せて色補正に参入することで、「...
つまり「赤・緑・青」に加えて「黄」も参照することで現実的...
-Scalehosei3.py
import cv2
import numpy as np
import os
import shutil
# === 設定 ===
SCALE_DIR = "processed_patches2" # カラースケール抽出...
INPUT_DIR = "drone_images" # 元画像フォルダ
OUTPUT_DIR = "processed_patches3" # 補正後出力フォルダ
# 出力フォルダを初期化
if os.path.exists(OUTPUT_DIR):
shutil.rmtree(OUTPUT_DIR)
os.makedirs(OUTPUT_DIR, exist_ok=True)
print(f"🗂️ 出力フォルダを作成: {OUTPUT_DIR}")
# === 理想の純色(RGB順) ===
PURE_RGB = np.array([ # 赤、黄、緑、青、それぞれの理想的...
[255, 0, 0], # 赤
[255, 255, 0], # 黄
[0, 255, 0], # 緑
[0, 0, 255] # 青
], dtype=np.float32)
# === カラースケールの4色平均を取得 ===
#画像が縦長なら上→下に赤→黄→緑→青、横長なら左→右で青→緑→...
#各区画ごとにピクセル平均を計算し、BGR→RGB順に並べてリス...
#横長時は配列の順番を赤→黄→緑→青のRGB順に修正
def extract_scale_colors(scale_img):
"""カラースケール画像から4色の平均色(RGB順)を抽出"""
h, w = scale_img.shape[:2]
vertical = h > w
colors = []
if vertical:
# 縦長: 上から 赤→黄→緑→青
step = h // 4
for i in range(4):
region = scale_img[i*step:(i+1)*step if i<3 ...
avg_bgr = region.mean(axis=(0,1))
avg_rgb = avg_bgr[::-1] # BGR→RGB
colors.append(avg_rgb)
else:
# 横長: 左から 青→緑→黄→赤
step = w // 4
for i in range(4):
region = scale_img[:, i*step:(i+1)*step if i...
avg_bgr = region.mean(axis=(0,1))
avg_rgb = avg_bgr[::-1]
colors.append(avg_rgb)
# 横長は順序を赤→黄→緑→青に直す
colors = [colors[3], colors[2], colors[1], color...
return np.array(colors, dtype=np.float32)
# === 変換行列の計算 ===
#緑パッチの誤差を重視するために重み配列を生成
#観測色から理想色への変換を重み付き最小二乗法(線形)で...
def weighted_least_squares_matrix(observed, pure, green_...
weights = np.ones(4)
weights[2] = green_weight
W = np.diag(weights) # 重み付き最小二乗: (O^T W O...
A = np.linalg.solve(observed.T @ W @ observed, obser...
return A
# === 行列適用 ===
# 多項式補正関数(RGB各チャンネルに2次多項式)
#RGB各チャンネルに対し2次多項式補正(明るさ非線形)を加...
def polynomial_correction(rgb_flat, coeffs):
corrected = np.zeros_like(rgb_flat)
for c in range(3):
a, b, c0 = coeffs[c]
corrected[:, c] = a * (rgb_flat[:, c]**2) + b * ...
return corrected
# 行列適用+多項式+ガンマ補正
def apply_matrix(img, M, rgb_strength=(1.0, 0.7, 1.0), g...
"""
#入力画像をRGB変換→行列線形補正→強度倍率補正(チャン...
#img: 入力画像(BGR)
#M: 3x3変換行列
#rgb_strength: チャンネル毎の乗算強度
#gamma: チャンネル毎のガンマ補正値(べき乗の逆数をか...
#poly_coeffs: 各チャンネルの多項式係数(a,b,c)のリスト
"""
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB).astyp...
h, w = img_rgb.shape[:2]
flat = img_rgb.reshape(-1, 3)
# 線形変換
corrected_flat = np.dot(flat, M)
# 強度補正
for c in range(3):
corrected_flat[:, c] *= rgb_strength[c]
# 多項式補正
corrected_flat = polynomial_correction(corrected_fla...
# ガンマ補正(正規化してべき乗)
for c in range(3):
# 0-255範囲を0-1に変換して補正、戻す
corrected_flat[:, c] = np.power(np.clip(correcte...
corrected_flat = np.clip(corrected_flat, 0, 255).ast...
corrected = corrected_flat.reshape(h, w, 3)
return cv2.cvtColor(corrected, cv2.COLOR_RGB2BGR)
# === 誤差評価関数 ===
#4色の各パッチに対し、色差誤差を計算して合計
#とくに緑のB・R、赤のG・B、黄のR、青のG・Bの項目で誤差を...
def multi_patch_error(observed, pure, M, rgb_strength):
pred = np.dot(observed, M)
for i in range(4):
pred[i, 0] *= rgb_strength[0] # 赤
pred[i, 1] *= rgb_strength[1] # 緑
pred[i, 2] *= rgb_strength[2] # 青
e_red_G = np.abs(pred[0, 1] - 0)
e_red_B = np.abs(pred[0, 2] - 0)
e_yellow_R = np.abs(pred[1,0] - 255)
e_green_R = np.abs(pred[2, 0] - 0)
e_green_B = np.abs(pred[2, 2] - 0)
e_blue_G = np.abs(pred[3, 1] - 0)
e_blue_B = np.abs(pred[3, 2] - 255)
error_total = e_green_B + e_green_R + e_red_G + e_re...
return error_total
best_params = None
best_err = float('inf')
"""
# グリッド探索範囲
#グリッド探索範囲として、緑重み・RGB倍率の候補群を用意。
#最初の元画像1枚のパッチを観測値とし、
#全ての候補パラメータで「変換→誤差評価」
#最良パラメータを採用(loop脱出)。
#採用パラメータを表示。
"""
green_weights = np.arange(5, 81, 1) # "5"から"80...
red_values = np.arange(1.2, 1.3, 0.1) # 1.2固定
green_values = np.arange(1.3, 1.4, 0.1) # 1.3固定
blue_values = np.arange(1.2, 1.3, 0.1) # 1.2固定
# カラースケール画像1枚目だけでパラメータ探索
for fn in os.listdir(INPUT_DIR):
if not fn.lower().endswith((".jpg", ".jpeg", ".png",...
continue
base = os.path.splitext(fn)[0]
scale_path = os.path.join(SCALE_DIR, f"crop_{base}.j...
if not os.path.exists(scale_path):
continue
scale_img = cv2.imread(scale_path)
observed = extract_scale_colors(scale_img)
pure = PURE_RGB.copy()
for gw in green_weights:
for rv in red_values:
for gv in green_values:
for bv in blue_values:
rgb_strength = (rv, gv, bv)
M = weighted_least_squares_matrix(ob...
err = multi_patch_error(observed, pu...
if err < best_err:
best_err = err
best_params = dict(gw=gw, rv=rv,...
break
print(f"⭐ 最良パラメータ: weight={best_params['gw']}, r...
M = best_params["M"]
rgb_strength = best_params["rgb_strength"]
# 画像全体に最適化後の補正を適用
#フォルダ内の全画像を対象にスケールパッチ画像を抽出し補...
#結果を出力フォルダに保存し進捗を通知
for fn in os.listdir(INPUT_DIR):
if not fn.lower().endswith((".jpg", ".jpeg", ".png",...
continue
base = os.path.splitext(fn)[0]
scale_path = os.path.join(SCALE_DIR, f"crop_{base}.j...
img_path = os.path.join(INPUT_DIR, fn)
out_path = os.path.join(OUTPUT_DIR, fn)
if not os.path.exists(scale_path):
print(f"⚠️ カラースケールが見つかりません: {fn} ...
continue
scale_img = cv2.imread(scale_path)
observed = extract_scale_colors(scale_img)
pure = PURE_RGB.copy()
corrected = apply_matrix(cv2.imread(img_path), M, rg...
cv2.imwrite(out_path, corrected)
print(f"✅ 補正完了: {fn} → {out_path}")
print("\n🎯 全画像の色補正が完了しました。")
スクリプト前半で画像からカラースケール領域の抽出と各色の...
重要な補正式として「緑帯の誤差を重視」する重み調整ロジッ...
画像から特定パッチを切り出し平均RGB値を計算し、その値と...
複数の補正パラメータ(行列、強度、ガンマなど)を探索し、...
-Scalehosei4.py
◯プレート領域の白枠マスク・抽出スクリプト
基本設定・フォルダ操作
入出力フォルダ名などの初期設定
既存の出力先は削除し、新たに作り直します
プレート領域のマスク処理
mask_plate_regionで画像をグレースケール化して白黒マスク...
◯ノイズ除去(remove_noise)
細かな領域や切れぎみの線の補完を行った後に小面積のゴミ除...
◯輪郭取得(detect_plate_contour)
画像から白枠(矩形状)の輪郭を検出し、面積・中心・角度・...
基準に適合しない場合はGUIでユーザー4点指定もサポート
import cv2
import numpy as np
import os
import shutil
from glob import glob
# === 設定 ===
INPUT_DIR = "processed_patches3" # 入力画像フォルダ
OUTPUT_DIR = "processed_patches4" # 出力フォルダ
REF_WIDTH = 1000 # 出力画像の横サイズ
REF_HEIGHT = 750 # 出力画像の縦サイズ
os.makedirs(OUTPUT_DIR, exist_ok=True)
# === フォルダーの削除・作成 ===
folder_name = OUTPUT_DIR
if os.path.exists(folder_name):
shutil.rmtree(folder_name)
print(f"既存のフォルダ '{folder_name}' を削除しまし...
os.makedirs(folder_name, exist_ok=True)
print(f"新しいフォルダ '{folder_name}' を作成しました。")
# === 1. プレート領域のマスク処理 ===
def mask_plate_region(img, threshold_mode="adaptive"): #★
"""
撮影画像から白いプレート枠領域を抽出してマスク化する。
"""
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (7, 7), 0) #★
if threshold_mode == "otsu":
_, binary = cv2.threshold(blur, 0, 255, cv2.THRE...
elif threshold_mode == "adaptive":
binary = cv2.adaptiveThreshold(blur, 255,
cv2.ADAPTIVE_THRE...
cv2.THRESH_BINARY...
else:
raise ValueError("threshold_mode must be 'otsu' ...
# 白黒反転(白枠が白くなるように)
if np.mean(binary) < 127:
binary = cv2.bitwise_not(binary)
return binary
# === 2. ノイズ除去処理 ===
def remove_noise(binary_mask):
"""
二値化画像から小さなノイズを除去し、白枠を明確化する。
"""
kernel = np.ones((4, 4), np.uint8) #★
# Opening:白ノイズ除去
opened = cv2.morphologyEx(binary_mask, cv2.MORPH_OPE...
# Closing:枠線の途切れ補完
closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, k...
# Connected Componentで小領域除去
num_labels, labels, stats, _ = cv2.connectedComponen...
cleaned = np.zeros_like(closed)
for i in range(1, num_labels): # 0は背景
area = stats[i, cv2.CC_STAT_AREA]
if area > 500: #★
cleaned[labels == i] = 255
return cleaned
# === 3. 輪郭の取得と近似化 ===
def detect_plate_contour(img, cleaned_mask):
"""
白枠プレートの輪郭を検出し、内側スケール矩形のみを抽...
外枠(画像外周)は無視。
条件:
- 面積が 20000〜1450×1060 の範囲
- アスペクト比が 1.0〜a の範囲
- 図形中心が画像の外周b%以内でない
- 各角の角度が c°〜d° の範囲
"""
h, w = img.shape[:2]
max_allowed_area = 1450 * 1060
min_allowed_area = 630 * 340
# --- ① 外枠除外マスク ---
inner_mask = np.zeros_like(cleaned_mask)
margin = int(min(h, w) * 0.12) # 外周を無視
inner_mask[margin:h - margin, margin:w - margin] = 255
masked = cv2.bitwise_and(cleaned_mask, inner_mask)
# --- ② 輪郭検出 ---
contours, _ = cv2.findContours(masked, cv2.RETR_EXTE...
if not contours:
print("⚠️ 輪郭が検出されませんでした。")
return img, None
valid_candidates = []
for cnt in contours:
area = cv2.contourArea(cnt)
if area < min_allowed_area or area > max_allowed...
continue
rect = cv2.minAreaRect(cnt)
(cx, cy), (rw, rh), angle = rect
# --- ③ 中心位置チェック(外周に寄りすぎていない...
if cx < margin or cx > (w - margin) or cy < marg...
continue
# --- ④ アスペクト比チェック ---
aspect_ratio = max(rw, rh) / (min(rw, rh) + 1e-6)
if not (1.5 <= aspect_ratio <= 2.0):
continue
# --- ⑤ 近似と角度判定 ---
epsilon = 0.02 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
if len(approx) != 4:
continue
pts = approx.reshape(4, 2)
angles = []
for i in range(4):
p1, p2, p3 = pts[i], pts[(i + 1) % 4], pts[(...
v1, v2 = p1 - p2, p3 - p2
cos_angle = np.dot(v1, v2) / (np.linalg.norm...
angle = np.degrees(np.arccos(np.clip(cos_ang...
angles.append(angle)
if all(69 <= a <= 111 for a in angles):
valid_candidates.append((area, pts))
# --- ⑥ 最も適した候補を選択 ---
if not valid_candidates:
print("⚠️ 有効な内枠が見つかりませんでした。手動...
manual_points = []
def mouse_callback(event, x, y, flags, param):
if event == cv2.EVENT_RBUTTONDOWN: # 右クリ...
manual_points.append((x, y))
cv2.drawMarker(param, (x, y), (0, 255, 0...
cv2.imshow("manual_select", param)
print(f"✅ 追加: {len(manual_points)}点...
elif event == cv2.EVENT_LBUTTONDOWN: # 左ク...
if manual_points:
manual_points.pop()
print(f"↩️ 直前の点を削除。残り {len...
param_copy = img.copy()
for px, py in manual_points:
cv2.drawMarker(param_copy, (px, ...
cv2.imshow("manual_select", param_co...
temp = img.copy()
cv2.imshow("manual_select", temp)
cv2.setMouseCallback("manual_select", mouse_call...
print("🖱️ 右クリックで4点選択、左クリックで直前...
while True:
key = cv2.waitKey(0)
if key in [13, 10]: # Enter
break
cv2.destroyWindow("manual_select")
if len(manual_points) != 4:
print("⚠️ 4点が選択されませんでした。スキッ...
return img, None
best_pts = np.array(manual_points, dtype=np.floa...
contour_img = img.copy()
cv2.polylines(contour_img, [best_pts.astype(int)...
print("✅ 手動で内枠を確定しました。")
return contour_img, best_pts
best_area, best_pts = max(valid_candidates, key=lamb...
contour_img = img.copy()
cv2.drawContours(contour_img, [best_pts.astype(int)]...
print(f"🟩 内枠矩形を検出(面積={best_area:.0f}, ア...
return contour_img, best_pts
# === 4. メイン処理 ===
for fname in os.listdir(INPUT_DIR):
if not fname.lower().endswith((".jpg", ".jpeg", ".pn...
continue
img_path = os.path.join(INPUT_DIR, fname)
img = cv2.imread(img_path)
if img is None:
print(f"⚠️ 読み込み失敗: {fname}")
continue
# --- ステップ1: マスク化 ---
mask = mask_plate_region(img, threshold_mode="otsu")
-Scalehosei5.py
2. 緑枠検出と台形補正
○基本設定
台形補正に使う出力画像サイズ、検出用緑範囲、フォルダ再初...
○緑枠検出(detect_green_corners)
BGR→HSV変換し、緑色HSV域をinRangeで抽出
輪郭検出し、最大4点近似で「緑枠」四隅座標を取得
○透視変換による補正(perspective_correction)
4点座標を「左上→右上→右下→左下」に順序付け
指定したサイズに合わせてOpenCVの透視変換実行
import cv2
import numpy as np
import os
import shutil
# === 設定 ===
INPUT_DIR = "processed_patches4" # 緑枠付き画像フォルダ
OUTPUT_DIR = "processed_patches5" # 台形補正後の出力フ...
os.makedirs(OUTPUT_DIR, exist_ok=True)
# === 出力画像サイズ ===
# 縦8cm × 横14cm → 比率4:7 として例: 256×448 ピクセルに...
OUT_H, OUT_W = 256, 448
# === 緑色検出用のHSV範囲(調整可能) ===
HSV_LOWER = np.array([35, 80, 40]) # 緑の下限
HSV_UPPER = np.array([85, 255, 255]) # 緑の上限
# === フォルダ初期化 ===
if os.path.exists(OUTPUT_DIR):
shutil.rmtree(OUTPUT_DIR)
print(f"🧹 既存のフォルダ '{OUTPUT_DIR}' を削除しま...
os.makedirs(OUTPUT_DIR, exist_ok=True)
print(f"📁 新しいフォルダ '{OUTPUT_DIR}' を作成しました...
def detect_green_corners(img):
"""
緑枠の四隅を検出して返す。
成功すれば4点座標(np.float32)を返し、失敗時はNone。
"""
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, HSV_LOWER, HSV_UPPER)
# ノイズ除去
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (...
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel...
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kerne...
# 輪郭検出
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERN...
if not contours:
return None
# 面積の大きい順にソート
contours = sorted(contours, key=cv2.contourArea, rev...
for cnt in contours:
peri = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, 0.04 * peri, True)
if len(approx) == 4:
pts = approx.reshape(4, 2).astype(np.float32)
return pts # 四角形と認識されたら返す
return None
def order_points(pts):
"""4点を順序付きに整列(左上・右上・右下・左下)"""
rect = np.zeros((4, 2), dtype="float32")
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)] # 左上
rect[2] = pts[np.argmax(s)] # 右下
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)] # 右上
rect[3] = pts[np.argmax(diff)] # 左下
return rect
def perspective_correction(img, pts, out_size=(OUT_W, OU...
"""透視変換を行い、指定サイズにリサイズ"""
rect = order_points(pts)
dst = np.array([
[0, 0],
[out_size[0] - 1, 0],
[out_size[0] - 1, out_size[1] - 1],
[0, out_size[1] - 1]
], dtype="float32")
# dst の順番 (w,h)に注意:OpenCVでは横×縦
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(img, M, out_size)
return warped
# === メイン処理 ===
for fname in os.listdir(INPUT_DIR):
if not fname.lower().endswith((".jpg", ".jpeg", ".pn...
continue
path_in = os.path.join(INPUT_DIR, fname)
img = cv2.imread(path_in)
if img is None:
print(f"⚠️ 画像読み込み失敗: {fname}")
continue
corners = detect_green_corners(img)
if corners is None:
print(f"⚠️ 緑枠の四隅が検出できません: {fname}")
continue
warped = perspective_correction(img, corners, out_si...
path_out = os.path.join(OUTPUT_DIR, fname)
cv2.imwrite(path_out, warped)
print(f"✅ {fname} を補正・保存しました。")
print(f"\n🎯 台形補正が完了しました。出力フォルダ: {OUTP...
-Scalehosei6.py
○基本設定
目的ごとのフォルダ/クロップ範囲等を準備
○自動クロップ
各入力画像を一定サイズ(例:256x256)でNUM_CROPS枚だけ切...
保存ファイル名は評価点+連番で命名
import cv2
import os
import numpy as np
import shutil
# === 設定 ===
INPUT_DIR = "processed_patches5" # 抽出済み錆画像フォ...
OUTPUT_DIR = "processed_patches6" # 出力先
CROP_SIZE = 256 # 切り抜きサイズ(px)
NUM_CROPS = 4 # 1枚あたり生成枚数
OVERLAP_RATIO = 0.3 # オーバーラップ率(0...
# === 出力フォルダ初期化 ===
if os.path.exists(OUTPUT_DIR):
shutil.rmtree(OUTPUT_DIR)
os.makedirs(OUTPUT_DIR, exist_ok=True)
print(f"🗂️ 出力フォルダを作成: {OUTPUT_DIR}")
# === メイン処理 ===
for fname in os.listdir(INPUT_DIR):
if not fname.lower().endswith((".jpg", ".jpeg", ".pn...
continue
# 元画像の評点(例: "1S_0001.jpg" → score = 1)
score = fname[0]
img_path = os.path.join(INPUT_DIR, fname)
img = cv2.imread(img_path)
if img is None:
print(f"⚠️ 読み込み失敗: {fname}")
continue
h, w = img.shape[:2]
# 切り抜き可能数を超える場合は調整
crop_w = CROP_SIZE
crop_h = CROP_SIZE
# オーバーラップを考慮して切り抜き開始座標を決定
x_step = int((w - crop_w) / (NUM_CROPS - 1)) if NUM_...
y_step = int((h - crop_h) / (NUM_CROPS - 1)) if NUM_...
# オーバーラップの設定(比率で調整)
x_step = max(int(crop_w * (1 - OVERLAP_RATIO)), 1)
y_step = max(int(crop_h * (1 - OVERLAP_RATIO)), 1)
# スライドウィンドウ的に切り抜き
crop_count = 0
for y in range(0, max(h - crop_h + 1, 1), y_step):
for x in range(0, max(w - crop_w + 1, 1), x_step):
crop = img[y:y+crop_h, x:x+crop_w]
if crop.shape[0] != CROP_SIZE or crop.shape[...
continue
crop_count += 1
save_name = f"{score}_{crop_count}.jpg"
save_path = os.path.join(OUTPUT_DIR, save_na...
cv2.imwrite(save_path, crop)
if crop_count >= NUM_CROPS:
break
if crop_count >= NUM_CROPS:
break
print(f"✅ {fname}: {crop_count}枚の256×256画像を生...
print("🎯 全画像の切り抜き完了。")
-11/7
カラーの錆画像を256ピクセル四方で切り抜くことができた。11...
-色補正・切り抜き手順
--1, 始めにカラーの錆画像(drone_images)を用意する
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--2, Scalehosei1.pyを実行する そうすると元画像からカラー...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--3, Scalehosei2.pyを実行する 純色に変換された切り抜かれ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--4, Scalehosei3.pyを実行する 3の変換行列をもとに画像全...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--5, Scalehosei4.pyを実行する カラースケールの内側に緑色...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--6, Scalehosei5.pyを実行する 枠から錆を切り抜く
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--7, Scalehosei6.pyを実行する 切り抜いた画像を学習用に25...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
抽出の際に使用した緑の枠が残ってしまっているがとりあえず...
-11/9
カラーの錆画像で学習を行った。(評点1:6枚, 評点2:54枚, 評...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-11/11
-DenseNet121 単体で3回学習(同一条件)を行ったところ以下...
, Run , MAE , 正答率 , F1スコア
, 1 , 0.282702960866563 , 0.553191489361702 , 0.366171839...
, 2 , 0.237414197718844 , 0.553191489361702 , 0.381286549...
, 3 , 0.237434574898253 , 0.531914893617021 , 0.383338632...
カラーの錆画像を学習させた結果( .keras ファイル)を用い...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
, 画像 , 評点 , 錆割合 , 錆数
, 1_1.jpg , 3 , 0.996 , 1
, 1_2.jpg , 3 , 0.530 , 138
, 1_3.jpg , 3 , 0.999 , 1
, 2_1.jpg , 3 , 1.000 , 1
, 3_1.jpg , 3 , 0.996 , 1
, 3_2.jpg , 3 , 1.000 , 1
, 3_3.jpg , 3 , 1.000 , 1
, 4_1.jpg , 3 , 1.000 , 1
, 4_4.png , 3 , 0.423 , 956
, 4_5.png , 3 , 0.396 , 1038
, 4_61.jpg , 3 , 0.477 , 26143
, 4_62.jpg , 3 , 0.482 , 11092
, 4_63.jpg , 4 , 0.729 , 3324
, 4_64.jpg , 3 , 0.374 , 66397
, 4_65.jpg , 2 , 0.330 , 217611
ドローン撮影画像では照明・影・反射の影響が大きく学習済みC...
-11/12
ドローンで撮影した画像から評点を求めることを行った。錆の...
-β版(最初モノクロに変換してから領域と評点を自動で抽出 → ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-改良版(白, 黒の強弱を調節し橋梁側面の錆をより自動で抽出...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-錆領域手動切り抜き版(予め橋梁側面の錆領域を定義してその...
--錆領域切り抜き
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--評点領域を抽出
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
***11月第3週(11/13〜11/19) [#j417c1113]
-11/13
昨日行った評点領域抽出に関して納得の行く結果が得られなか...
-ヒートマップでの評点抽出
ドローンで撮影された橋梁表面の錆画像から錆の劣化程度を視...
○手順
--1, 入力画像の頂点選択
---対象となる橋梁のカラー画像を入力フォルダーに入力し、マ...
---マウス操作のコマンド一覧
, コマンド , 実行内容
, 左クリック , 頂点を追加
, 右クリック , 直前の頂点を削除
, Enterキー , 頂点選択確定 次の画像へ
, Escapeキー , 選択中止
---頂点選択を行う際には以下のようなウィンドウが出力される...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
--2, 領域分割と評点推定
---選択された領域を任意サイズ(例:256×256ピクセル)の小...
---各パッチ画像に対して事前に学習済みのCNNアンサンブルモ...
---評点は以下の基準で分類される
, 評点 , 劣化状態 , 表示色
, 1 , 重度の錆 , 🔴 赤
, 2 , 進行した錆 , 🟠 橙
, 3 , 中程度の錆 , 🟡 黄
, 4 , 軽微な錆 , 🟢 緑
, 5 , 錆がほとんどない , 🔵 水色
--3, ヒートマップ生成
---各パッチの推定評点に応じて対応する色を半透明で重ね合わ...
--4, 出力と付加情報
---生成されたヒートマップには以下の情報が自動的に付与される
, 情報記載位置 , 内容
, 左下 , ポリゴン内全体の平均評点値(Avg)
, 右下 , 各評点に対応する色凡例(Legend)
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-ヒートマップ抽出スクリプト
import cv2
import shutil
import numpy as np
from tensorflow.keras.models import load_model
import os
import glob
# === 設定 ===
MODEL_PATHS = [
"ensemble_model_1.keras",
"ensemble_model_2.keras",
"ensemble_model_3.keras"
]
INPUT_DIR = "drone_images"
OUTPUT_DIR = "drone_detect_result"
PATCH_SIZE = 256 # 任意変更可
# === フォルダー初期化 ===
if os.path.exists(OUTPUT_DIR):
shutil.rmtree(OUTPUT_DIR)
print(f"既存のフォルダ '{OUTPUT_DIR}' を削除しました...
os.makedirs(OUTPUT_DIR, exist_ok=True)
# === モデル読み込み ===
models = []
for path in MODEL_PATHS:
if os.path.exists(path):
model = load_model(path, compile=False)
models.append(model)
print(f"✅ モデル読み込み完了: {path}")
if not models:
raise FileNotFoundError("❌ 有効な .keras モデルが見...
# === 評点ごとの色設定 ===
score_colors = {
1: (0, 0, 255), # 赤
2: (0, 165, 255), # 橙
3: (0, 255, 255), # 黄
4: (0, 255, 0), # 緑
5: (255, 255, 0), # 水色
}
# === 評点推定関数 ===
def predict_score(crop_img):
resized = cv2.resize(crop_img, (256, 256))
x = resized.astype("float32") / 255.0
x = np.expand_dims(x, axis=0)
preds = [m.predict(x, verbose=0)[0][0] for m in mode...
avg_pred = np.mean(preds)
# 評点を5段階に丸める
if avg_pred < 1.9: score = 1
elif avg_pred < 2.6: score = 2
elif avg_pred < 3.3: score = 3
elif avg_pred < 4.0: score = 4
else: score = 5
return score, avg_pred
# === 手動ポリゴン選択 ===
points = []
def draw_polygon(event, x, y, flags, param):
global points
img_copy = param["img_copy"]
if event == cv2.EVENT_LBUTTONDOWN:
points.append((x, y))
cv2.circle(img_copy, (x, y), 5, (0, 0, 255), -1)
elif event == cv2.EVENT_RBUTTONDOWN and points:
points.pop()
img_copy[:] = param["img"].copy()
for px, py in points:
cv2.circle(img_copy, (px, py), 5, (0, 0, 255...
elif event == cv2.EVENT_MOUSEMOVE:
img_copy[:] = param["img"].copy()
for px, py in points:
cv2.circle(img_copy, (px, py), 5, (0, 0, 255...
if len(points) > 1:
cv2.polylines(img_copy, [np.array(points)], ...
# === 錆検出処理(ヒートマップ生成 + 凡例 + 平均) ===
def detect_rust_heatmap(img_path, patch_size=256):
global points
basename = os.path.basename(img_path).split('.')[0]
img = cv2.imread(img_path)
if img is None:
print(f"⚠️ 読み込み失敗: {img_path}")
return
# --- 画像を1980×1080で表示 ---
display_img = cv2.resize(img, (1980, 1080))
scale_x = img.shape[1] / 1980
scale_y = img.shape[0] / 1080
img_copy = display_img.copy()
cv2.namedWindow("Select Area", cv2.WINDOW_NORMAL)
cv2.setMouseCallback("Select Area", draw_polygon, {"...
print("🟢 左クリック=頂点追加 / 右クリック=戻す / ...
while True:
cv2.imshow("Select Area", img_copy)
key = cv2.waitKey(1) & 0xFF
if key == 13: # Enter
if len(points) >= 3:
print("✅ ポリゴン確定")
break
else:
print("⚠️ 頂点が3つ未満です")
elif key == 27: # ESC
print("🚫 中断されました")
cv2.destroyAllWindows()
return
# --- スケール戻す ---
scaled_points = np.array([[int(x * scale_x), int(y *...
points = []
# --- ポリゴンマスク作成 ---
mask = np.zeros(img.shape[:2], np.uint8)
cv2.fillPoly(mask, [scaled_points], 255)
# --- 領域内スライスしてスコア予測 ---
result = img.copy()
heatmap = np.zeros_like(img)
h, w = img.shape[:2]
scores_list = []
for y in range(0, h, patch_size):
for x in range(0, w, patch_size):
cx, cy = x + patch_size//2, y + patch_size//2
if cx >= w or cy >= h:
continue
# 中心がポリゴン内か確認
if mask[cy, cx] == 0:
continue
crop = img[y:y+patch_size, x:x+patch_size]
if crop.shape[0] < patch_size or crop.shape[...
continue
score, raw_pred = predict_score(crop)
scores_list.append(raw_pred)
color = score_colors[score]
overlay = np.full_like(crop, color, dtype=np...
# 透明重ね
cv2.addWeighted(overlay, 0.4, result[y:y+pat...
# --- 平均スコア ---
avg_score = np.mean(scores_list) if scores_list else 0
print(f"📊 平均スコア: {avg_score:.2f}")
# --- ポリゴン枠 + 平均スコア表示(左下) ---
cv2.polylines(result, [scaled_points], True, (255, 2...
h, w = result.shape[:2]
cv2.putText(result, f"Avg: {avg_score:.2f}", (30, h ...
cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255,255,...
# --- 凡例描画(右下) ---
legend_x = w - 250
legend_y = h - 180
cv2.rectangle(result, (legend_x - 20, legend_y - 30)...
cv2.putText(result, "Legend", (legend_x, legend_y - ...
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255,255,...
for i, (score, color) in enumerate(score_colors.item...
y_pos = legend_y + i * 35
cv2.rectangle(result, (legend_x, y_pos), (legend...
cv2.putText(result, f"{score}", (legend_x + 45, ...
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255,...
# --- 保存 ---
out_path = os.path.join(OUTPUT_DIR, f"{basename}_hea...
cv2.imwrite(out_path, result)
print(f"🟩 ヒートマップを保存: {out_path}")
cv2.destroyAllWindows()
# === メイン ===
def batch_process_images():
image_files = sorted(glob.glob(os.path.join(INPUT_DI...
for idx, path in enumerate(image_files, 1):
print(f"📸 ({idx}/{len(image_files)}) {path}")
detect_rust_heatmap(path, patch_size=PATCH_SIZE)
if __name__ == "__main__":
batch_process_images()
-11/14
○ピクセル数を変化させると評点に変化がでるのか
--256ピクセル
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
, 画像 , 平均スコア
, 左1 , 3.08
, 左2 , 3.12
, 中央 , 2.99
, 右2 , 3.06
, 右1 , 3.02
--128ピクセル
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
, 画像 , 平均スコア
, 左1 , 3.01
, 左2 , 3.03
, 中央 , 2.99
, 右2 , 3.01
, 右1 , 3.02
--64ピクセル
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
, 画像 , 平均スコア
, 左1 , 3.34
, 左2 , 3.34
, 中央 , 3.18
, 右2 , 3.39
, 右1 , 3.28
--32ピクセル
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
, 画像 , 平均スコア
, 左1 , 3.44
, 左2 , 3.62
, 中央 , 3.49
, 右2 , 3.49
, 右1 , 3.50
出力時間を考慮したら128ピクセル、精度を考慮したら64ピクセ...
-11/17
これまで橋側面からSAM (Segment Anything Model)を用いて錆...
--SAMでの抽出(アンサンブルとCNNモデル単体比較)上段から...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
大きく評点が変わることは無かったがヒートマップにはそれな...
-11/19
U-netを用いて錆領域の自動判別を行った。錆側面以外にも護岸...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
***11月第4週(11/20〜11/26) [#j417c1120]
-11/20
import os
import sys
import cv2
import json
import time
import glob
import shutil
import random
import datetime
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers, models, Input, Model
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import ModelCheckpoint, ...
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
# -------------------------
# CONFIGURATION - 必要に応じて書き換えてください
# -------------------------
TRAIN_IMAGES_DIR = "train_images" # アノテ・学習用...
MASKS_DIR = "masks" # アノテ結果(0=bg...
MODELS_DIR = "models" # 保存先フォルダ
UNET_MODEL_PATH = os.path.join(MODELS_DIR, "unet.keras")
DRONE_IMAGES_DIR = "drone_images" # 推論対象ドロー...
RESULTS_DIR = "results_unet" # 推論出力(ヒー...
# パッチ・ヒートマップ設定
PATCH_SIZE = 256
STRIDE = 126 # スライド幅(任意で調整)
DISPLAY_MAX = (1980, 1080) # 表示最大解像度(要求により...
# CNN評点用の事前学習済みモデル群(任意)
# ここに .keras ファイルを列挙すると、アンサンブルでスコ...
ENSEMBLE_MODELS = [
"ensemble_model_1.keras",
"ensemble_model_2.keras",
"ensemble_model_3.keras"
]
RANDOM_SEED = 42
np.random.seed(RANDOM_SEED)
tf.random.set_seed(RANDOM_SEED)
random.seed(RANDOM_SEED)
# -------------------------
# ユーティリティ
# -------------------------
def ensure_dirs():
os.makedirs(MASKS_DIR, exist_ok=True)
os.makedirs(MODELS_DIR, exist_ok=True)
os.makedirs(RESULTS_DIR, exist_ok=True)
# --- 錆マスクから bridge マスクを自動生成するユーティリ...
def generate_bridge_mask_from_rust_mask(rust_mask_path, ...
dilate_iter=20, ...
"""
rust_mask_path: 二値 or ラベルマスク(255=rust など...
out_bridge_mask_path: 保存先(png)。None の場合は r...
戻り値: bridge_mask (H,W) uint8 (0/255)
パラメータ調整: - dilate_iter: 錆領域をどれだけ...
- min_area: 小領域を無視する閾値
"""
m = cv2.imread(rust_mask_path, cv2.IMREAD_UNCHANGED)
if m is None:
raise FileNotFoundError(f"rust mask not found: {...
# もしラベル(maskが0/1/2)なら rust に対応する値を抽出
if m.ndim == 3:
m_gray = cv2.cvtColor(m, cv2.COLOR_BGR2GRAY)
else:
m_gray = m.copy()
# 二値化(非ゼロを錆とみなす)
_, bw = cv2.threshold(m_gray, 1, 255, cv2.THRESH_BIN...
# 膨張で周囲をカバー(橋側面を含めたいので大きめ)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE...
dil = cv2.dilate(bw, kernel, iterations=dilate_iter)
# 小領域除去(ノイズ排除)
num_labels, labels, stats, _ = cv2.connectedComponen...
mask_clean = np.zeros_like(dil)
for i in range(1, num_labels):
area = stats[i, cv2.CC_STAT_AREA]
if area >= min_area:
mask_clean[labels == i] = 255
# 凸包で外形を滑らかに(オプション)
if convexify:
contours, _ = cv2.findContours(mask_clean, cv2.R...
hull_mask = np.zeros_like(mask_clean)
for cnt in contours:
if cv2.contourArea(cnt) < min_area:
continue
hull = cv2.convexHull(cnt)
cv2.drawContours(hull_mask, [hull], -1, 255,...
mask_final = hull_mask
else:
mask_final = mask_clean
# 少し膨らませて余裕を持たせる(bridge全体を覆う)
kernel2 = cv2.getStructuringElement(cv2.MORPH_ELLIPS...
mask_final = cv2.dilate(mask_final, kernel2, iterati...
if out_bridge_mask_path is None:
base = os.path.splitext(rust_mask_path)[0]
out_bridge_mask_path = base + "_bridge.png"
cv2.imwrite(out_bridge_mask_path, mask_final)
return mask_final
def resize_for_display(img, max_size=DISPLAY_MAX):
h, w = img.shape[:2]
scale = min(max_size[0] / w, max_size[1] / h, 1.0)
if scale < 1.0:
return cv2.resize(img, (int(w*scale), int(h*scal...
else:
return img, 1.0
# -------------------------
# 1) アノテーションツール(橋側面: 1ポリゴン、錆: 複数ポ...
# -------------------------
def annotate_mode(input_dir=TRAIN_IMAGES_DIR, masks_dir=...
ensure_dirs()
files = sorted([f for f in os.listdir(input_dir) if ...
if not files:
print("No images found in", input_dir); return
for fname in files:
img_path = os.path.join(input_dir, fname)
img = cv2.imread(img_path)
if img is None:
print("Failed to load", img_path); continue
basename = os.path.splitext(fname)[0]
print(f"\n=== Annotate bridge only: {fname} ===")
poly = polygon_draw_ui(img, title=f"Draw Bridge ...
if poly is None:
print("Skipped:", fname)
continue
mask = np.zeros(img.shape[:2], dtype=np.uint8)
cv2.fillPoly(mask, [np.array(poly)], 1) # 1 = ...
save_path = os.path.join(masks_dir, f"{basename}...
# if existing rust mask exists, merge later duri...
cv2.imwrite(save_path, mask)
print("Saved bridge mask:", save_path)
def polygon_draw_ui(img, title="Draw polygon", allow_qui...
win = title
img_work = img.copy()
img_display = img.copy()
points = []
# ▼ 初期縮小(ウィンドウに合わせる)
disp_img, scale = resize_for_display(img_display)
def mouse_cb(event, x, y, flags, param):
nonlocal img_display, points, scale
# ▼ 表示画像 → 元画像座標へ変換
orig_x = int(x / scale)
orig_y = int(y / scale)
if event == cv2.EVENT_LBUTTONDOWN:
points.append((orig_x, orig_y))
elif event == cv2.EVENT_RBUTTONDOWN:
if points:
points.pop()
# 描画は元画像サイズで行う
img_display = img_work.copy()
for px, py in points:
cv2.circle(img_display, (px, py), 4, (0,0,25...
if len(points) > 1:
cv2.polylines(img_display, [np.array(points)...
cv2.namedWindow(win, cv2.WINDOW_NORMAL)
cv2.setMouseCallback(win, mouse_cb)
print("Instructions:")
print(" - Left click: add vertex")
print(" - Right click: undo last vertex")
print(" - Enter: confirm polygon")
print(" - Esc: cancel")
while True:
# ▲ 表示ごとに縮尺計算(ウィンドウサイズが変わる...
disp_img, scale = resize_for_display(img_display)
cv2.imshow(win, disp_img)
k = cv2.waitKey(20) & 0xFF
if k == 13: # Enter
if len(points) >= 3:
cv2.destroyWindow(win)
return points.copy()
else:
print("Need at least 3 points.")
elif k == 27: # Esc
cv2.destroyWindow(win)
return None
elif allow_quit_key and k == ord(allow_quit_key):
cv2.destroyWindow(win)
return None
# -------------------------
# 2) U-Net モデル定義(軽量版)
# -------------------------
def build_unet(input_shape=(256,256,3), n_classes=2, bas...
inputs = Input(shape=input_shape)
# Encoder
c1 = conv_block(inputs, base_filters)
p1 = layers.MaxPool2D()(c1)
c2 = conv_block(p1, base_filters*2)
p2 = layers.MaxPool2D()(c2)
c3 = conv_block(p2, base_filters*4)
p3 = layers.MaxPool2D()(c3)
c4 = conv_block(p3, base_filters*8)
p4 = layers.MaxPool2D()(c4)
b = conv_block(p4, base_filters*16)
# Decoder
u1 = layers.UpSampling2D()(b)
u1 = layers.Concatenate()([u1, c4])
c5 = conv_block(u1, base_filters*8)
u2 = layers.UpSampling2D()(c5)
u2 = layers.Concatenate()([u2, c3])
c6 = conv_block(u2, base_filters*4)
u3 = layers.UpSampling2D()(c6)
u3 = layers.Concatenate()([u3, c2])
c7 = conv_block(u3, base_filters*2)
u4 = layers.UpSampling2D()(c7)
u4 = layers.Concatenate()([u4, c1])
c8 = conv_block(u4, base_filters)
outputs = layers.Conv2D(n_classes, kernel_size=1, ac...
model = Model(inputs, outputs)
model.compile(optimizer=Adam(1e-4), loss='sparse_cat...
return model
def conv_block(x, filters):
x = layers.Conv2D(filters, 3, padding='same', activa...
x = layers.BatchNormalization()(x)
x = layers.Conv2D(filters, 3, padding='same', activa...
x = layers.BatchNormalization()(x)
return x
# -------------------------
# 3) Data loader for training
# -------------------------
def load_dataset(img_dir=TRAIN_IMAGES_DIR, mask_dir=MASK...
"""
- mask が missing の場合、可能なら rust-only mask ...
- 統合後: mask values -> 0(background) / 2(rust)
"""
imgs = []
masks = []
files = sorted([f for f in os.listdir(img_dir) if f....
for f in files:
base = os.path.splitext(f)[0]
img_path = os.path.join(img_dir, f)
mask_path = os.path.join(mask_dir, base + "_mask...
img = cv2.imread(img_path)
if img is None:
print("Failed to load image:", img_path); co...
# ---- マスクが存在しない場合の自動処理 ----
if not os.path.exists(mask_path):
# try to find a rust-only mask: either base+...
rust_candidates = [
os.path.join(mask_dir, base + "_rust.png...
os.path.join(mask_dir, base + "_rustmask...
os.path.join(mask_dir, base + "_r.png"),
os.path.join(mask_dir, base + ".png"),
]
rust_found = None
for rc in rust_candidates:
if os.path.exists(rc):
rust_found = rc
break
if rust_found:
# generate bridge mask from rust mask
bridge_mask = generate_bridge_mask_from_...
out_bridge_mask_pat...
# Now combine: create a 0/1/2 mask: star...
combined = np.zeros(img.shape[:2], dtype...
combined[bridge_mask > 0] = 1
rust_img = cv2.imread(rust_found, cv2.IM...
if rust_img is not None:
if rust_img.ndim == 3:
rust_gray = cv2.cvtColor(rust_im...
else:
rust_gray = rust_img
combined[rust_gray > 0] = 2
mask = combined
# save combined mask
cv2.imwrite(mask_path, mask)
print("Auto-generated mask saved:", mask...
else:
print("Mask not found and no rust candid...
continue
else:
mask = cv2.imread(mask_path, cv2.IMREAD_UNCH...
if mask is None:
print("Invalid mask for", f, "- skipping...
# resize and append
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_resized = cv2.resize(img_rgb, img_size)
mask_resized = cv2.resize(mask, img_size, interp...
imgs.append(img_resized.astype(np.float32) / 255...
masks.append(mask_resized.astype(np.uint8))
if not imgs:
raise RuntimeError("No training pairs found. Run...
X = np.stack(imgs, axis=0)
Y = np.stack(masks, axis=0)
return X, Y
# -------------------------
# 4) Training function
# -------------------------
def train_unet(epochs=20, batch_size=8, img_size=(256,25...
ensure_dirs()
X, Y = load_dataset(img_size=img_size)
X_train, X_val, Y_train, Y_val = train_test_split(X,...
model = build_unet(input_shape=(*img_size, 3), n_cla...
print(model.summary())
ckpt = ModelCheckpoint(UNET_MODEL_PATH, save_best_on...
es = EarlyStopping(patience=6, restore_best_weights=...
history = model.fit(X_train, Y_train, validation_dat...
epochs=epochs, batch_size=batch_...
print("Training finished. Model saved to", UNET_MODE...
return model, history
# -------------------------
# 5) U-Net 推論(bridge + rust マスクを返す)
# -------------------------
def unet_predict(img_bgr, model=None, img_size=(256,256)):
"""
img_bgr: OpenCV BGR image
returns: pred_mask (H,W) with values 0 or 1 (argmax ...
"""
if model is None:
if not os.path.exists(UNET_MODEL_PATH):
raise RuntimeError("No UNET model found. Tra...
model = tf.keras.models.load_model(UNET_MODEL_PA...
orig_h, orig_w = img_bgr.shape[:2]
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
inp = cv2.resize(img_rgb, img_size).astype(np.float3...
pred = model.predict(np.expand_dims(inp, axis=0), ve...
pred_mask = np.argmax(pred, axis=-1).astype(np.uint8...
# resize back to original size
pred_mask = cv2.resize(pred_mask, (orig_w, orig_h), ...
return pred_mask
# -------------------------
# 6) Load ensemble CNN models (optional) for score predi...
# -------------------------
def load_ensemble_models(model_paths):
models_list = []
for p in model_paths:
if os.path.exists(p):
try:
m = tf.keras.models.load_model(p, compil...
models_list.append(m)
print("Loaded model:", p)
except Exception as e:
print("Failed to load", p, ":", e)
else:
print("Model not found, skip:", p)
return models_list
ensem_models = load_ensemble_models(ENSEMBLE_MODELS)
def predict_patch_score(patch_bgr):
# パッチが 1px でも欠けていると CNN に入れられないた...
if patch_bgr is None or patch_bgr.shape[0] < 16 or p...
return 0.0 # または skip
# CNN の入力は必ず 256×256 に統一
resized = cv2.resize(patch_bgr, (256, 256))
x = resized.astype(np.float32) / 255.0
x = np.expand_dims(x, axis=0)
if not ensem_models:
return float(x.mean()*5.0)
preds = []
for m in ensem_models:
pred = m.predict(x, verbose=0)[0][0]
preds.append(pred)
return float(np.mean(preds))
# -------------------------
# 7) Detection & scoring pipeline (for drone images)
# -------------------------
SCORE_COLORS = {
1: (0, 0, 255), # 赤
2: (0, 165, 255), # 橙
3: (0, 255, 255), # 黄
4: (0, 255, 0), # 緑
5: (255, 255, 0), # 水色
}
def score_to_color(score):
# score in continuous range ~ [0..5.5], convert to d...
if score < 1.9: s = 1
elif score < 2.6: s = 2
elif score < 3.3: s = 3
elif score < 4.0: s = 4
else: s = 5
return SCORE_COLORS[s], s
def detect_and_score(drone_dir=DRONE_IMAGES_DIR, out_dir...
ensure_dirs()
if not os.path.exists(UNET_MODEL_PATH):
print("UNET model not found. Please train first.")
return
unet_model = tf.keras.models.load_model(UNET_MODEL_P...
files = sorted([f for f in os.listdir(drone_dir) if ...
for fname in files:
path = os.path.join(drone_dir, fname)
img = cv2.imread(path)
if img is None:
print("Failed to read", path); continue
orig = img.copy()
print("Processing:", fname)
# Predict mask (0=bg,1=rust)
pred_mask = unet_predict(img, model=unet_model, ...
rust_mask = (pred_mask == 1).astype(np.uint8) * ...
# 使用する領域:錆が検出されたバウンディングボッ...
ys, xs = np.where(rust_mask > 0)
if len(ys) == 0:
print("No rust found in", fname);
# それでも出力用フォルダは作る
out_base = os.path.join(out_dir, os.path.spl...
os.makedirs(out_base, exist_ok=True)
cv2.imwrite(os.path.join(out_base, "orig.jpg...
cv2.imwrite(os.path.join(out_base, "pred_mas...
cv2.imwrite(os.path.join(out_base, "rust_mas...
continue
miny, maxy = ys.min(), ys.max()
minx, maxx = xs.min(), xs.max()
heatmap = np.zeros((img.shape[0], img.shape[1]),...
countmap = np.zeros_like(heatmap)
# スライディング窓を錆のバウンディング箱だけで回す
y_start = max(miny - patch_size, 0)
y_end = min(maxy + patch_size, img.shape[0])
x_start = max(minx - patch_size, 0)
x_end = min(maxx + patch_size, img.shape[1])
for y in range(y_start, y_end - patch_size + 1, ...
for x in range(x_start, x_end - patch_size +...
patch_rust = rust_mask[y:y+patch_size, x...
# 錆ピクセルが十分に含まれるパッチのみ評価
if patch_rust.sum() < 10:
continue
patch = img[y:y+patch_size, x:x+patch_si...
score = predict_patch_score(patch) # 連...
heatmap[y:y+patch_size, x:x+patch_size] ...
countmap[y:y+patch_size, x:x+patch_size]...
# 平均マップ計算(count>0 のみ)
avg_map = np.zeros_like(heatmap)
mask_count = countmap > 0
avg_map[mask_count] = heatmap[mask_count] / coun...
# for visualization, create colored overlay
overlay = img.copy()
legend_items = []
# create color-coded overlay by mapping each cel...
disp = img.copy()
for y in range(0, img.shape[0]-patch_size+1, 4):...
for x in range(0, img.shape[1]-patch_size+1,...
val = avg_map[y, x]
if val <= 0: continue
color, s = score_to_color(val)
cv2.rectangle(disp, (x, y), (x+4, y+4), ...
# blend original and disp
blended = cv2.addWeighted(img, 0.6, disp, 0.4, 0)
# compute overall average score for area
if mask_count.any():
global_avg = float(avg_map[mask_count].mean())
else:
global_avg = 0.0
# annotate global avg left-bottom, legend right-...
text = f"avg_score: {global_avg:.2f}"
cv2.putText(blended, text, (10, img.shape[0]-30)...
# legend
legend_h = 160
legend_w = 200
legend = np.zeros((legend_h, legend_w, 3), dtype...
cv2.putText(legend, "Legend (score)", (10,20), c...
y0 = 40
i = 0
for s in sorted(SCORE_COLORS.keys()):
color = SCORE_COLORS[s]
cv2.rectangle(legend, (10, y0+i*24), (30, y0...
cv2.putText(legend, f"{s}", (40, y0+12+i*24)...
i += 1
# paste legend on blended bottom-right
lh, lw = legend.shape[:2]
blended[-lh-10:-10, -lw-10:-10] = legend
# save outputs
out_base = os.path.join(out_dir, os.path.splitex...
os.makedirs(out_base, exist_ok=True)
cv2.imwrite(os.path.join(out_base, "orig.jpg"), ...
cv2.imwrite(os.path.join(out_base, "pred_mask.pn...
cv2.imwrite(os.path.join(out_base, "rust_mask.pn...
cv2.imwrite(os.path.join(out_base, "heatmap_over...
print("Saved results to", out_base)
# -------------------------
# 8) small CLI to run steps
# -------------------------
def print_menu():
print("""
rust_unet_system - menu:
1) Annotate images (create masks)
2) Train UNet (from masks)
3) Run detection & scoring on drone_images
4) Exit
""")
def main():
ensure_dirs()
while True:
print_menu()
c = input("Choose option: ").strip()
if c == '1':
print("Starting annotation mode...")
annotate_mode()
elif c == '2':
print("Starting training...")
epochs = int(input("epochs [default 20]: ") ...
batch = int(input("batch_size [default 8]: "...
img_s = int(input("image_size (square) [defa...
train_unet(epochs=epochs, batch_size=batch, ...
elif c == '3':
print("Running detection & scoring...")
detect_and_score()
elif c in ('4','q','quit','exit'):
print("Exit.")
break
else:
print("Unknown option.")
if __name__ == "__main__":
main()
-① 画像アノテーション(マスク作成)モード
--目的:U-Net が学習するための「教師データ(錆領域マスク...
--動作内容
---rain_images/ 内の画像を1枚ずつ表示して錆領域をポリゴン...
--ポイント
---ポリゴンは1つだけ(錆領域のみ)
---以前のように「bridge」「rust」の2段階の描画ではない
---一度マスクが作られたら、その後は何度も学習に再利用可能
-② U-Net の学習モード
--目的:アノテーション済みマスクを使って錆領域を自動検出...
--処理内容
---train_images/ の画像とmasks/ 内の対応マスク(0:背景/2...
---画像とマスクを (256×256) にリサイズしセットを作る
---U-Net(簡易軽量版)を構築
--学習後に得られるもの
---錆の領域をピクセル単位で推定できる U-Netファイル
-③ ドローン画像 → 錆領域抽出 → 評点ヒートマップ生成モード
-目的:ドローンで撮影した広い橋側面画像に対してU-Net で錆...
・処理詳細
--(1) U-Net により錆領域の抽出
---入力画像を読み込み U-Net で推定 (pred_mask==0 → 背景,...
---必要なのは錆領域のみなのでrust_mask = (pred_mask == 2)...
--(2) 錆領域の周囲でスライドウィンドウ実施
---錆領域の bounding box(範囲)を計算し、その範囲でパッ...
--(3) パッチごとに CNN でスコア予測
---スライスされたパッチは CNN(あなたが学習した3つのDense...
--(4) スコアをヒートマップ領域に加算し、ピクセルごとの平...
--(5) 凡例と平均スコアを追記して保存
---左下:全体平均評点(avg_score)
---右下:凡例(1〜5の色)
-「錆の領域抽出(U-Net)+ ヒートマップで評点可視化」 を...
-11/21
ヒートマップを作成するのに必要な撮影画像のピクセルサイズ...
上記のスクリプトを実行して錆とそうでない部分(ここでは背...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
-11/25
錆だけを予め学習させた状態でドローン撮影画像にヒートマッ...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
&ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/mori...
***11月第5週(11/27〜12/3) [#j417c1127]
-11/26
◯ 1. U-Net を用いた錆領域抽出の開始
従来の手法では錆の抽出に SAM(Segment Anything Model)や...
-ピクセル単位の分類能力が高い
-少量の教師データでも学習しやすい
-橋面に対して斜めから撮影された画像でも比較的安定した結果...
◯ 2. 必要なクラス構造(背景・橋側面・錆)
U-Net 導入前までは「錆(対象)」と「それ以外(背景)」と...
, クラス(class)番号 , 分類内容 , 役割
, class0 , 背景 , 河川、空、堤防、民家、植物など「錆領域...
, class1 , 橋側面 , U-Net が抽出すべき橋のみを特定
, class2 , 錆 , 評点推定の入力として利用
◯ 3. 教師データの整備
教師データは以下の 3 種類で構成される
, 教師データ , クラス番号 , データ利用方法
, 近接錆画像(256×256ピクセル四方), class2(= 錆) , 錆の...
, 近影画像(橋側面) , class1(= 橋側面) , 背景が写り込む...
, 背景画像(ただの風景画), class0(= 背景) , 橋が写ってい...
◯ 4. マスク画像(≒モノクロ画像)の作成
-① ポリゴン選択 UI により橋側面を手動で指定
-② class1 として塗りつぶし
-③ 錆部分がある場合は追加で class2 を塗り分ける
-④ 近接錆画像と背景画像は、自動で class2 / class0 として...
◯ 5. U-Net の学習
U-Net の入出力は以下の通り
-入力:256×256 RGB値
-出力:256×256 の 3 クラス分類マップ(0,1,2)
学習プロセスでは EarlyStopping を導入しベストなエポックで...
◯ 6. 橋側面の自動抽出
U-Net によって作成された「class1=橋側面」領域は自動的に抽...
◯7. 錆ヒートマップ生成(class2 → 評点)
橋側面マスクが生成されたら次に錆領域を小領域パッチに分割...
-錆ヒートマップ(5 色)
-橋側面マスク(白黒)
-予測された錆マスク
-元画像
スクリプトの改良を行ったところヒートマップが表示されなく...
**12月 [#j417c1200]
***12月第1週(12/4〜12/10) [#j417c1204]
パラメータを調節しながら結果を見比べている
-検討要素
--画像の水増し…現在は撮影した画像そのものを学習させたいた...
--学習データ比較…1回各要素の学習枚数を1枚とかに変えて学...
-12/8
スクリプトに何度も修正を加えたことで原型を留めていない事...
なので今月の中間発表に向けて使用するスクリプトとアプロー...
-12/10
錆を検出して mask 画像を生成するシステムの改善に取り組み...
[[↑上がるゥ?>#contents]]
**画像のスキャンについて [#j991c400]
錆画像スキャン方法
・スキャナーは研究室にあるものを使用する
・使用するソフトはXSane(デスクトップの左上 "メニュー → ...
-1, XSane を立ち上げてOKを押す
-2, ウィンドウを消したい or 表示させたい場合はウィンドウ...
-3, 様々な色が表示されているアイコンでカラー, モノクロに...
-4, 3の2つ下にある部分で解像度の変更が可能
[[XSaneの詳しい使い方はこちら >https://kledgeb.blogspot.c...
*創造工房実習 [#v035a40a]
**4/18 [#j417c418]
創造工房実習でI型断面等の三角形分布や塑性状態がどういう...
-I型断面に10Nの面載荷をかけたとき
--梁の断面に赤と青で濃く色分けされており、上下縁の両方は...
--固定端近くでは、直応力σxxがほぼ対称に上縁で圧縮、下縁で...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/1/t...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/1/t...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/1/t...
-15Nの面載荷をかけたとき
--10Nの荷重をかけたときよりも梁の上下縁にかなりの力がかか...
--固定端近くでは10Nのときと同様、真ん中から先端付近にかけ...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/1/t...
-5N, 6Nの面載荷をかけたとき
--固定面側は最大281MPaの直応力が加わっている上縁がありこ...
--下端側よりも上端側のほうが直応力の値が高く出る傾向にあ...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/1/t...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/1/t...
上端
・ーーーーーーーーーー
| ーーーーー
・ーーーーーーーーー ーーーー|
下端 ーーーーー |↓集中荷重位置...
ーーーーー| ...
**4/17 [#j417c906]
-SALOME2021でメッシュが計算されない事例が発生
--1, メッシュの編集画面を開く
--2, アルゴリズムで"NETGEN 1D-2D-3D" を選択
--3, その下にある詳細設定右側の歯車をクリックして "NETGEN...
--4, 最大サイズと最小サイズに数値を入れてOKをクリック (最...
--5, メッシュの編集画面に戻るので"適用して閉じる"をクリック
--6, 左側のオブジェクトブラウザーにある"Mesh_1"を右クリッ...
**4/14 [#ja17c906]
-載荷点での荷重、たわみのプロット
1ステップごとに面に載荷した荷重の10分の1を加えたもの...
-軸方向直応力が最大となる点での直応力-直ひずみのプロット...
知りたい方向のEPSI_NOEUーSIGM_NOEUとのグラフを作成 (0...
-その点での相当応力(ミーゼス応力)―相当ひずみのプロット...
知りたい方向のSIEQ_NOEUーEPSI_NOEUのx,y,zの直ひずみ,...
-曲げモーメントが最大となる断面の軸方向直応力$\sigma_{zz}...
曲げモーメント最大は固定端(片持ち梁の場合)なので0に限...
**4/10 [#ja87c906]
-片持ち梁にかける荷重を6.25N以上にするとParavisでtime ste...
-6.25Nで解析を行うと実際の結果が降伏応力に収束しなくなる...
-明日やること
--応力(ミーゼス)-ひずみ,応力-たわみのグラフを作成
--スライドの作成
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/kek...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/kek...
,経過時間,↖たわみ(mm),↖応力(MPa),↗たわみ(mm),↗応力(MPa)
,0,0,0,0,0
,1,0.000140332,34.4925,0.000184238,44.0832
,2,0.000280661,68.9829,0.000368391,88.1643
,3,0.000420986,103.471,0.000552457,132.243
,4,0.000561308,137.957,0.000736434,176.318
,5,0.000701624,172.44,0.000923909,220.147
,6,0.000842636,206.506,0.00123269,241.658
,7,0.00101987,237.23,0.002051,244.44
,8,0.00130101,257.808,0.00765896,243.452
,9,0.00191133,268.432,0.0109555,241.582
,10,0.00377436,280.533,0.0148525,239.658
,11,ー,ー,0.0191575,237.724
,12,ー,ー,0.0237442,235.762
,13,ー,ー,0.0285207,233.804
,14,ー,ー,0.0333983,231.819
,15,ー,ー,0.0383132,229.768
,16,ー,ー,0.043212,227.625
**4/7 [#ja87c006]
-Paravisでのグラフ作成方法
--https://www.rccm.co.jp/icem/pukiwiki/index.php?%E9%81%B...
--https://www.rccm.co.jp/icem/pukiwiki/index.php?%E3%83%9...
--Salomeで荷重をかける段階を設定する際に1.0と入力する箇所...
⇒一度01と入力しOKを押した後再度1.0と入力する画面に戻ると...
--また弾塑性解析を行う際にSalome-Meca演習_弾塑性解析(2021...
⇒原因は関数がある範囲までしか定義されていないの事。Funct...
**2/7 [#ja87c596]
春休みの課題
-弾塑性班
--鋼材の長方形断面の梁(想像しやすい大きさ)の片持ち梁と...
--軸方向:$z$, たわみ方向:$y$
--SS400ぐらいのヤング率と降伏応力で弾塑性解析の設定
--載荷点での荷重、たわみのプロット
--軸方向直応力が最大となる点での直応力-直ひずみのプロット...
--その点での相当応力(ミーゼス応力)―相当ひずみのプロット...
--曲げモーメントが最大となる断面の軸方向直応力$\sigma_{zz...
--上記の三角形分布において、上下縁から徐々に降伏が入って...
--上記を確認できたら、I型断面とかの三角形分布や全塑性が、...
**12/20 [#w0335ea2]
今日はviを用いて論文に画像を貼る方法について学んだ。
**12/13 [#w0335ea3]
今日はviを用いた論文の書き方を学んだ。
・文頭に%をつけるとその行に書かれた文章は反映されない
・強制改行したい場合は"\\"を入力する
・更新→:!pdfplatexsibup2
○式を書く(書き方は編集画面から)
$v=\frac{P\ell^{3}}{48EI} + \frac{P\ell}{4kGA}$
\begin{eqnarray}
v=\frac{P\ell^{3}}{48EI} + \frac{P\ell}{4kGA}
\\
I=\int_{A}y^{2}dA
\end{eqnarray}
**11/29 [#ceb57a7c]
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/san...
今回は鋼材で木材を挟んだサンドイッチ梁の解析を行い、縦軸...
メッシュ長さの大小関係なく理論値と20%の誤差が生じ、これ以...
全員で作成した解析結果のグラフを下に示しておく
,メッシュ長さ,要素数,先端変位(4隅の平均値)[mm],相対誤差($...
,0.7,155419,0.0772,26.943,湊
,0.8,138734,0.0775,26.452,湊
,0.9,82935,0.0774,26.614,湊
,1.1,38671,0.0766,27.937,森井
,1.2,32044,0.0770,27.273,森井
,1.3,28599,0.0768,27.604,森井
,1.4,23950,0.07640,22.04,米谷
,1.5,19998,0.07641,22.03,米谷
,1.6,19448,0.07715,21.28,米谷
,1.7,13801,0.07567,22.79,米谷
,1.8,12677,0.07736,21.06,沼野
,1.9,11464,0.07546,23.00,沼野
,2,10699,0.07404,24.45,沼野
,3,3579,0.08414,15.004,國井
,4,1628,0.08279,16.37,國井
,5,1016,0.08303,16.26,國井
,6,839,0.08288,16.26,西澤
,7,554,0.08087,18.28,西澤
,8,285,0.07898,19.20,西澤
,9,261,0.01421,85.49,真庭
,10,232,0.03380,65.51,真庭
,11,208,0.00913,90.68,真庭
**11/22 [#ceb57a7d]
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/kad...
今回は単純異方性と等方性の解析を行い、縦軸に変位(mm), 横...
全員で作成した解析結果のグラフを下に示しておく
,メッシュ長さ,要素数,変位(異方性)[mm],相対誤差-異方性($\f...
,0.7,171996,0.5068,2.993,0.4301,3.141,湊
,0.8,161561,0.5069,2.999,0.4300,3.116,湊
,0.9,94185,0.5021,2.071,0.4301,3.139,湊
,1.1,47998,0.4957,0.814,0.4122,1.056,森井
,1.2,47343,0.4952,0.712,0.4300,3.217,森井
,1.3,42112,0.4941,0.488,0.4298,3.169,森井
,1.4,38960,0.4937,0.407,0.4299,3.193,森井
,1.5,15041,0.4845,1.460,0.4298,3.179,米谷
,1.6,16071,0.4849,1.380,0.4298,3.157,米谷
,1.7,12933,0.4845,1.460,0.4299,3.182,米谷
,1.8,12993,0.4832,1.73,0.4298,3.19,沼野
,1.9,11235,0.4783,2.73,0.4295,3.10,沼野
,2,11456,0.4982,1.32,0.4296,3.12,沼野
,3,2514,0.4369,4.87,0.4293,3.05,國井
,4,1461,0.4341,4.20,0.4293,3.05,國井
,5,433,0.2803,32.7,0.4284,2.83,國井
,6,356,0.4283,2.80,0.3437,17.5,西澤
,7,102,0.4260,2.26,0.2225,46.6,西澤
,8,93,0.4260,2.26,0.1123,73.0,西澤
,9,81,0.2212,54.9,0.4255,2.13,真庭
,10,84,0.2051,58.3,0.4247,1.95,真庭
,11,74,0.2260,54.0,0.4246,1.91,真庭
**11/15 [#ceb57a7e]
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/kad...
単純梁の解析結果から縦軸に変位(mm), 横軸にボリューム数を...
前回と同じくメッシュの長さを長くすると接点変位は小さくな...
全員で作成した解析結果のグラフを下に示しておく
,メッシュ長さ,要素数,先端変位(4隅の平均値)[mm],相対誤差($...
,0.7,171996,0.4260,2.207,湊
,0.8,161561,0.4256,2.115,湊
,0.9,94185,0.4169,0.0719,湊
,1.1,47998,0.4122,1.067,森井
,1.2,47343,0.4118,1.166,森井
,1.3,42112,0.4113,1.289,森井
,1.4,38960,0.4112,1.313,森井
,1.5,15041,0.3978,4.516,米谷
,1.6,16071,0.3999,4.002,米谷
,1.7,12993,0.3971,4.687,米谷
,1.8,12203,0.3964,4.85,沼野
,1.9,11235,0.3942,5.38,沼野
,2,11456,0.3991,4.20,沼野
,3,2514,0.2141,21.4,國井
,4,1461,0.34028,18.4,國井
,5,433,0.1354,67.8,國井
,6,356,0.2135,48.8,西澤
,7,102,0.11,73.6,西澤
,8,93,0.112,73.0,西澤
,9,81,0.1125,73.0,真庭
,10,84,0.0794,80.9,真庭
,11,74,0.1297,68.9,真庭
**11/8 [#ceb57a7f]
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/kad...
全員で行った解析の結果を縦軸に変位, 横軸にボリューム数を...
メッシュの長さを長くすると接点変位は小さくなり、相対誤差...
一方でメッシュの長さを小さくするほど接点変位は断面二次モ...
メッシュ長さを0.5や0.3として解析を行えばもっと理論値に値...
全員で作成した解析結果のグラフを下に示しておく
,メッシュ長さ,要素数,先端変位(4隅の平均値)[mm],相対誤差($...
,0.7,107380,6.47,2.96,湊
,0.8,57821,6.44,3.62,湊
,0.9,57698,6.43,3.73,湊
,1.1,57980,6.44,3.57,湊
,1.2,52123,6.41,3.90,森井
,1.3,45549,6.34,4.98,森井
,1.4,26951,6.32,5.31,森井
,1.5,16904,6.25,6.32,米谷
,1.6,14296,6.20,7.05,米谷
,1.7,13596,6.21,6.81,米谷
,1.8,6299,5.74,13.9,沼野
,1.9,6001,5.73,14.1,沼野
,2,5617,5.65,15.3,沼野
,3,2309,5.48,17.8,國井
,4,617,3.62,45.6,國井
,5,494,3.85,42.3,國井
,6,581,2.51,62.4,西澤
,7,133,1.41,78.8,西澤
,8,78,1.29,80.7,西澤
,9,72,1.288,80.69,真庭
,10,60,1.226,81.62,真庭
,11,65,1.231,81.54,真庭
**11/1 [#w68ec49g]
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/abc...
http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/123...
今日(11/1)はwikiにグラフの貼り付けとviとlinuxを使ってグラ...
上のグラフは自身で作成したもの、下のグラフは過去の先輩方...
先輩方が作成したグラフの数値を下に示しておく
,メッシュの長さ ,要素数 ,変位[mm],相対誤差,計算者
,0.7,155192,0.08378905246,15.365,安藤
,0.8,138808,0.08380386491,15.350,安藤
,0.9,82587,0.083707073981,15.45,兼田
,1.1,38671,0.084201207602,14.95,兼田
,1.2,31929,0.083688,15.466,柴田
,1.3,28621,0.083669,15.4857,柴田
,1.4,28854,0.08368,15.47,佐藤
,1.5,20015,0.084052,15.10,佐藤
,1.6,19448,0.0835402938,15.62,皆川
,1.7,13801,0.0834355098,15.72,皆川
,1.8,12528,0.083733,15.42,永山
,1.9,11769,0.083924,15.23,永山
,2,10699,0.084076876559,15.074,辻
,3,3579,0.08414561753,15.004,辻
,4,1628,0.082794,16.37,服部
,5,1016,0.083033,18.89,服部
,6,839,0.082882,16.26,梶原
,7,554,0.080871,18.28,梶原
,8,285,0.079995,19.20,工藤
,9,261,0.078980,20.22,工藤
,10,232,0.081911,17.26,佐々木
,11,208,0.075676,23.56,佐々木
**10/4 [#w0335eb3]
今日は顔合わせをした
頑張りたい
"いきものがかり"についた
ページ名: