Visual Basic 中学校 掲示板 投稿内容
タグのない投稿を抽出 統計 RSS

Visual Basic 中学校 > 投稿一覧 >

ぼかし・シャープネスと画像の縮み

タグの編集...

投稿者 kawori   (社会人)   投稿日時 2016/8/17 12:19:08
Windows10 64bit、VB Net2015使用です。
ピクチャビューアの一部として、サンプル集を元にTrackBarに連動し画像のぼかしとシヤープネスの強度が変るダイアログを作成したのですが、完成後気になる事が起きました。
ぼかし又はシャープネスの強度を上げると、上げた程度に比例し同ダイアログ内のPictureBoxに表示した画像が縮みます。
取り急ぎぼかし部分のコードを下記致しますので、対処方法教えて下されば幸いです。
宜しくお願いします。
Public Shared Function ApplyKernel(ByVal SourceImage As Image, ByVal Kernel(,) As Integer, ByVal Weight As Integer, Optional ByVal RUpper As Integer = 0, Optional ByVal GUpper As Integer = 0, Optional ByVal BUpper As Integer = 0) As Bitmap
        Dim i As Integer
        Dim j As Integer
        Dim X As Integer
        Dim Y As Integer
        Dim R As Integer
        Dim G As Integer
        Dim B As Integer
        Dim bmp1 As Bitmap
        Dim bmp2 As Bitmap
        Dim C As Color
        Dim Edge As Integer = (Kernel.GetLength(0) \ 2) * 2
        Dim MatrixSize As Integer = Kernel.GetLength(0)
        bmp1 = CType(SourceImage, Bitmap)
        bmp2 = New Bitmap(bmp1.Width, bmp1.Height)
        For i = 1 To bmp1.Height - Edge
            For j = 1 To bmp1.Width - Edge
                R = 0
                G = 0
                B = 0
                For Y = 0 To MatrixSize - 1
                    For X = 0 To MatrixSize - 1
                        C = bmp1.GetPixel(j + X - 1, i + Y - 1)
                        R = (C.R * Kernel(X, Y)) + R
                        G = (C.G * Kernel(X, Y)) + G
                        B = (C.B * Kernel(X, Y)) + B
                    Next X
                Next Y
                R = RGBRange((R + RUpper) \ Weight)
                G = RGBRange((G + GUpper) \ Weight)
                B = RGBRange((B + BUpper) \ Weight)
                bmp2.SetPixel(j, i, Color.FromArgb(R, G, B))
            Next j
        Next i
        Return bmp2
    End Function
Public Shared Function RGBRange(ByVal Value As Integer) As Integer
        Select Case Value
            Case Is < 0
                Return 0
            Case Is > 255
                Return 255
            Case Else
                Return Value
        End Select
    End Function
Public Shared Function CreateBlur(ByVal SourceImage As Image, Optional ByVal Value As Integer = 1) As Bitmap
        Dim Kernel(,) As Integer
        Value = 3 + 2 * (Value - 1)
        Kernel = CType(Array.CreateInstance(GetType(Integer), Value, Value), Integer(,))
        Dim X As Integer
        Dim Y As Integer
        Dim Center As Integer = Value \ 2
        For Y = 0 To Value - 1
            For X = 0 To Value - 1
                Kernel(X, Y) = 1
            Next
        Next
        Kernel(Center, Center) = 2
        Return ApplyKernel(SourceImage, Kernel, Value * Value + 1)
    End Function
Private Sub TrackBar1_Scroll(sender As Object, e As EventArgs) Handles TrackBar1.Scroll, NumericUpDown1.MouseDown
        If TrackBar1.Value = 0 Then
            Return
        End If
        PictureBox1.Image = CreateBlur(PictureBox1.Image, Decimal.ToInt32(TrackBar1.Value))
        NumericUpDown1.Value = TrackBar1.Value
    End Sub

投稿者 YuO   (社会人)   投稿日時 2016/8/17 23:20:13
bmp2は,bmp1の高さ,幅よりそれぞれEdgeだけ小さい領域しか描画されていません。
そのEdgeはKernel.GetLength(0)に依存し,Kernel.GetLength(0)はTrackBar1.Valueに依存しています。
よって,その幅だけ小さく描画されます。
# 正確には画像のサイズは変わっていないが,端に描画されていない領域が出来ている

Edge分も描画してやれば,描画している領域は変わらなくなります。
そこをどのように描画したいかは要求によるので,要求に合わせて描画することになるかと。
bmp2.SetPixelの座標が(1, 1)から書き出しているため,そこも(0, 0)から描画するように修正する必要があります。

投稿者 kawori   (社会人)   投稿日時 2016/8/18 13:12:17
レス有難うございます。画像大きさに関しては、此方も既にビューワに加えたコードを用いて調べた処、御指摘通り効果掛ける前と同じ事が確認出来ました。
ところで、幾つか質問です。
私の希望は①端(0,0)から端(bmp1.Height及びbmp1.Width一杯)まで描画(edge分迄描画)、②端にも特殊効果をかける(ぼかしではぼかす、シャープネスではシャープネスを掛ける)の2点です。
御指摘に関しては私も最初の質問前に対策を考え、①に関しては「For i = 1 To bmp1.Height - Edge」と「For j = 1 To bmp1.Width - Edge」の部分の「1」を「0」に、「- Edge」の部分を取除く又は「- Edge + Edge」に修正する方法を試したのですが、デバッグ時にエラーが出て駄目でした。②に関しては「R = 0」・「G = 0」・「B = 0」の部分が該当と考えたのですが、方法見付けられずおります。不勉強を晒しておりますが、具体的な手掛かり教えて頂ければ幸いです。宜しくお願いします。

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2016/8/18 14:19:47
細かい事を端折ってしまえば、いわゆる「ぼかし処理」というのは、
画素座標周辺の RGB 値の平均を求め、それを処理対象の画素に対して
一定の割合で「混ぜ合わせる」作業に当たりますよね。
(単純な平均ではなく、色や輝度や座標ごとに重み付けをして演算する事もありますが)


> 端にも特殊効果をかける

端の部分は、どのように演算させたいのでしょうか?

 □□□□□
 □■■■□ 周囲±1ピクセルの色(■)を集めて
 □■●■□ ぼかすべき場所(●)の色を決める
 □■■■□
 □□□□□


 ■■■□□
 ■●■□□ 周囲±1ピクセルの色(■)を集めて
 ■■■□□ ぼかすべき場所(●)の色を決める
 □□□□□
 □□□□□

☆☆☆   
☆●■□□□ 画像の端なので周辺部分が足りない…どうする?
☆■■□□□
 □□□□□ 周囲に別の色(たとえば White)があるものとして演算?
 □□□□□
 □□□□□ それとも溢れた上と左は無視し、残る右と下の色から演算?


投稿者 kawori   (社会人)   投稿日時 2016/8/18 19:58:28
レス有難うございます。端の演算に関しては考慮外でした。
御指摘後、少し調べた頁で見たPhotoshop等のアプリで端にぼかしを掛けたい場合、予め対象画像の四辺に耳(余白)を付け、作業後耳を切落すらしいとの情報得ました。
その事を鑑みて考えると、提示頂いた3つの図の中では真ん中が私の考えに当ると思います。
その上で、私としてはEdge分も描画する事で元画像と描画領域を同じに出来ればと考えています。


投稿者 魔界の仮面弁士   (社会人)   投稿日時 2016/8/19 00:31:12
> 真ん中が私の考えに当ると思います。

下段で 2 種類の案を設けただけで、
上段と中段は同じことを言っているつもりでした…。


> Edge分も描画する事で元画像と描画領域を同じに出来ればと考えています。
Edge 部分に何を描画しますか?

たとえば、(0,0)-(4,4) な 5x5 マスの画像があるとしましょう。

  ⓪①②③④
 ⓪□□□□□
 ①□□□□□
 ②□□□□□
 ③□□□□□
 ④□□□□□

「ぼかし」効果を与えるにあたり、とりあえずここでは
それぞれの座標に対して、指定画素の周辺 8 点の色の平均を取ってから、
『処理対象の座標の色』と『周辺の平均色』を2:1の割合でブレンドしていくものとします。

---------
  ■■■□□
  ■●■□□  ある点●の色を求めるためには、近傍の 8 点の色
  ■■■□□  すなわち上下左右斜めにある■の色も必要です
  □□□□□
  □□□□□

周辺のR成分の平均
 =( (x - 1, y - 1)のR成分 + (x + 0, y - 1)のR成分 + (x + 1, y - 1)のR成分
  + (x - 1, y + 0)のR成分                      + (x + 1, y + 0)のR成分
  + (x - 1, y + 1)のR成分 + (x + 0, y + 1)のR成分 + (x + 1, y + 1)のR成分
  )÷「8」

処理対象(x, y)をぼかしたときのR成分
 =( (x, y)のR成分 × 2 + 周辺のR成分の平均)÷ 3

Red だけでなく、Blue や Green の成分も同様に処理します。

そしてここで問題となってくるのが、「周辺8点」が存在しない『端』の領域です。
-----------------

(案1) エッジ部分はぼかさない

外周部は「周辺8点」の色を求めることができないため、
元の色のままにする方法です。

  ⓪①②③④     ⓪①②③④
 ⓪□□□□□    ⓪□□□□□
 ①□□□□□    ①□☆☆☆□  加工できるのは(1,1)-(3,3)の領域のみです
 ②□□□□□ →→ ②□☆☆☆□  周辺部は演算できないため、元画像の内容が
 ③□□□□□    ③□☆☆☆□  そのままぼけずに残ってしまうことになります
 ④□□□□□    ④□□□□□
                
-----------------

(案2) エッジ部分はサンプリング数を減らす

辺の部分は5点のみ、角の部分は3点のみで平均を取る方法です。

  ⓪①②③④     ⓪①②③④
 ⓪□□□□□    ⓪☆☆☆☆☆
 ①□□□□□    ①☆☆☆☆☆  全域に対してぼかし処理が行うことができますが
 ②□□□□□ →→ ②☆☆☆☆☆  『角』や『辺』の部分は、混ぜる色数が減るため
 ③□□□□□    ③☆☆☆☆☆  周辺部とそれ以外とでぼけ具合が異なってしまいます
 ④□□□□□    ④☆☆☆☆☆


  ⓪①②③④
 ⓪□□□□□  右下点(x,y)周辺のR成分の平均
 ①□□□□□   =( (x - 1, y - 1)のR成分
 ②□□□□□    + (x + 0, y - 1)のR成分
 ③□□□■■    + (x - 1, y + 0)のR成分
 ④□□□■●    )÷「3」

  ⓪①②③④  上辺点(x,y)周辺のR成分の平均
 ⓪□□■●■   =( (x - 1, y + 0)のR成分
 ①□□■■■    + (x + 1, y + 0)のR成分
 ②□□□□□    + (x - 1, y + 1)のR成分
 ③□□□□□    + (x + 0, y + 1)のR成分
 ④□□□□□    + (x + 1, y + 1)のR成分
           )÷「5」
-----------------

(案3) 予め対象画像の四辺に耳(余白)があるものとみなす

  ⓪①②③④      ⓪①②③④
 ⓪□□□□□     ⓪☆☆☆☆☆
 ①□□□□□     ①☆☆☆☆☆  全域に対してぼかし処理が行うことができますが
 ②□□□□□ →→  ②☆☆☆☆☆  『角』や『辺』の部分には背景色が染み込むため
 ③□□□□□     ③☆☆☆☆☆  指定する背景色に気を遣う必要があります
 ④□□□□□     ④☆☆☆☆☆

余白部の色を単色にしてしまうと、ぼかしたときに周辺の滲みが目立ってしまうので
余白部は単一色とするのではなく、画像全体を「一回り大きな画像サイズ」に拡大し
それを背景として使うのが良いかもしれません。


  ㊧⓪①②③④㊨
 ㊤◇◇◇◇◇◇◇
 ⓪◇□□□□□◇
 ①◇□□□□□◇  周囲に耳(余白)部を設け、それらの座標には
 ②◇□□□□□◇  背景色があるものとみなしてから処理させます
 ③◇□□□□□◇
 ④◇□□□□□◇
 ㊦◇◇◇◇◇◇◇

  ㊧⓪①②③④㊨
 ㊤◇◇◇◇◇◇◇
 ⓪◇□□□□□◇ 『角』の座標に対して「周辺のR成分の平均」を
 ①◇□□□□□◇ 求める場合は、実際に元画像から GetPixel するのは
 ②◇□□□□□◇ 案2 と同様に 8 点中の 3 点だけとなりますが
 ③◇□□□■■◆ 画像外となる残りの 5 点は背景部から抽出されます
 ④◇□□□■●◆
 ㊦◇◇◇◇◆◆◆


  ㊧⓪①②③④㊨
 ㊤◇◇◇◆◆◆◇
 ⓪◇□□■●■◇ 『辺』の座標に対して「周辺のR成分の平均」を
 ①◇□□■■■◇ 求める場合も同様で、元画像から GetPixel するのは
 ②◇□□□□□◇ 案2 と同様に 8 点中の 5 点だけとなりますが
 ③◇□□□□□◇ 画像外となる残りの 3 点は背景部から抽出されます
 ④◇□□□□□◇
 ㊦◇◇◇◇◇◇◇


投稿者 kawori   (社会人)   投稿日時 2016/8/19 14:46:38
失礼致しました。前回の上段と中段の図が今回のレスの案1、下段の図が案3と同義で妥当でしょうか。
案1~3の中ならば、私の考えは案3かと思います。
それと、気になる部分出た為追加質問です。
Edgeは四辺全てに設けられる筈ですが、実行すると黒のグラデーションの帯が描画領域内側の右辺と底辺に出ます(黒帯幅は左右非描画幅の合計と同じです)。
何故四辺全部に出ず右辺と底辺なのかという点と、Edgeを描画すれば直るかという点も教えて下さい。
宜しくお願い致します。