目的
2値画像の形状を強調したいです.
入力2値画像(図1左上)を適当なサイズのガウシアンフィルタでぼかして(図1右上)から
(白と黒の中間値を閾値として)2値化すれば「形状が丸く」なります(図1右下).
とがっていた部分は丸く,輪郭部における白と黒のせめぎ合い具合が弱くなります.
やりたいことはこれとは逆方向の処理です.
つまり,白と黒が互いにより浸食し合うような形状を生成する方法を得たいです.
結果形状のイメージを図2に赤線で示します(これはイメージを手で描いたものです).
処理方法をご教示願いたく,宜しくお願い申し上げます.
図1
左上:入力,右上:ガウシアンフィルタでぼかしたもの
左下:後述のやってみた方法の結果,右下:形状が丸くなる処理の結果
図2:欲しい処理結果のイメージ
自身でやったこと
輪郭の座標値をオフセットする方法を思いつき,試したのですが,処理結果(図1左下)は思わしくありません.
- 輪郭形状が変.
全体的に細かくギザギザしているし,先端部はふにゃふにゃな形になっている. - 図内左上の円形が大きくなってしまうのが所望の結果と異なる.
また,できればこのような方法ではなくて,
「丸く」する側の処理のような,輪郭座標値を陽に扱わない方法が希望です.
(ぼかした結果に対して画素毎に異なる閾値を与えて2値化すれば所望の形状が得られるのではないか?と考えているのですが,
肝心の妥当な閾値を決める処理をどうすればよいのか?というところで行き詰っております.)
C++
1//思いつきでやってみた形状強調処理(結果は思わしくない) 2// 3//処理概要: 4// 入力Contour[k]に関する,結果Result[k]は, 5// 近隣(Contour[k-Wnd]~Contour[k+Wnd])の座標平均GとContour[k]との差deltaを用いて, 6// Result[k] = Contour[k] + Rate*delta 7// where delta = Contour[k] - G 8// ただし,deltaが閾値以下の場合は 9// Result[k] = Contour[k] 10// とする. 11//[Args] 12// Contour : 入力形状(外周画素群の座標) 13// WndR : 参照する近隣範囲 14// Rate : 強調率 15// IgnoreDeltaThresh : ごく小さい変化を強調しない用の閾値 16//[Ret] 17// 結果形状(外周画素群の座標) 18std::vector<cv::Point> EnhanceShape( const std::vector< cv::Point > &Contour, int WndR, float Rate, float IgnoreDeltaThresh=1.0f ) 19{ 20 WndR = std::max( 1, WndR ); 21 const int WndSize = 2*WndR + 1; 22 const float SqThresh = IgnoreDeltaThresh * IgnoreDeltaThresh; 23 24 const int N = (int)Contour.size(); 25 std::vector< cv::Point > Result = Contour; 26 27 for( int i=0; i<N; ++i ) 28 { 29 //近隣重心G 30 int SumX=0; 31 int SumY=0; 32 for( int k=-WndR; k<=WndR; k++ ) 33 { 34 int kk = i+k; 35 if( kk>=N )kk-=N; 36 if( kk<0 )kk+=N; 37 38 SumX += Contour[kk].x; 39 SumY += Contour[kk].y; 40 } 41 float GX = (float)SumX / WndSize; 42 float GY = (float)SumY / WndSize; 43 //delta 44 float dx = Contour[i].x - GX; 45 float dy = Contour[i].y - GY; 46 //Result 47 if( dx*dx + dy*dy > SqThresh ) 48 { 49 Result[i].x += cvRound( Rate*dx ); 50 Result[i].y += cvRound( Rate*dy ); 51 } 52 } 53 return Result; 54} 55 56int main() 57{ 58 //原画(2値画像とする) 59 cv::Mat Src = cv::imread( "Blobs.png", cv::IMREAD_GRAYSCALE ); 60 cv::imshow( "Src", Src ); 61 62 {//形状が丸くなる処理 63 cv::Mat Blurred; 64 const int KernelSize = 41; 65 cv::GaussianBlur( Src, Blurred, cv::Size(KernelSize,KernelSize), 0 ); 66 cv::imshow( "Blurred", Blurred ); 67 68 cv::Mat Rounded; 69 cv::threshold( Blurred, Rounded, 128, 255, cv::THRESH_BINARY ); 70 cv::imshow( "Rounded(Thresh128)", Rounded ); 71 } 72 73 {//形状を強調したい 74 //外周画素座標をいじくってみるテスト 75 std::vector< std::vector< cv::Point > > Contours; 76 cv::findContours( Src, Contours, cv::RETR_LIST, cv::CHAIN_APPROX_NONE ); 77 for( auto &Cont : Contours ) 78 { Cont = EnhanceShape( Cont, 15, 4 ); } 79 80 {//結果表示 81 cv::Mat Show( Src.rows, Src.cols, CV_8UC3 ); 82 Show = cv::Scalar(255,0,0); 83 Show.setTo( cv::Scalar(0,96,0), Src ); 84 for( int i=0; i<(int)Contours.size(); ++i ) 85 { cv::drawContours( Show, Contours, i, cv::Scalar(0,0,255) ); } 86 cv::imshow( "TestResult", Show ); 87 } 88 } 89 90 cv::waitKey(); 91 return 0; 92}
やったことの追記
ぼかした結果に対して画素毎に異なる閾値を与えて2値化
に関して,質問後に試行した内容を追記しておきます.(もちろん,解決していません.)
各位置の閾値を高くすべきか低くすべきか?のヒントは,元画像と形状を丸めた画像との差分から得られるであろう,と考えました.
(元画像で白だった部分が丸めた結果黒になったならば,その逆処理を行うにはその付近の閾値を小さく与えれば良く,元画像で黒だった部分が白に変わった箇所の付近では閾値を大きく与えればよい)
問題は,「付近」にその情報をどのようにして伝達すればよいか?という部分で,ここがまだ未解決です.
図3は,とりあえず簡単に試せる伝達手段として,モルフォロジとガウシアンフィルタを用いてみた結果です.
図3左が閾値マップで,図3右がそれを用いて図1右上のぼかし画像を2値化した結果です.
大まかな形はなんとなく図2の方向に向かっているようにも見えますが,
元の形状でとがっていた部分が残念な形になっています→これは閾値マップ生成時の「付近への伝達」手段に点広がり的な方法を用いたためであると思います.
何か指向性のある(?)伝達手段を用意できれば,改善するのかもしれません.
図3:変動閾値試行結果

回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/08/06 01:03
2019/08/08 15:41 編集
2019/08/09 01:22
2019/08/09 01:42