C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[12]: ビットマップの余白のトリミング


(過去ログ 97 を表示中)

[トピック内 15 記事 (1 - 15 表示)]  << 0 >>

■58070 / inTopicNo.1)  ビットマップの余白のトリミング
  
□投稿者/ レーモン (1回)-(2011/03/24(Thu) 20:14:16)

分類:[VB.NET/VB2005 以降] 

VB2008 Expressを使用しています。
ビットマップの画像の上下左右に余白がある場合、これをプログラム上でトリミングしたいと思うのですが、
思いつく方法としては、ビットマップの上下左右で余白でないと判定するピクセルが現れるまでスキャンを行い
トリミングする領域を得るという方法ですが、これよりも高速に行える方法はないでしょうか?
なお、余白でないピクセルであるかどうかの判断は、ピクセルの色(白またはRGBの値を用いて計算)で行います。
どうぞよろしくお願いします。
引用返信 編集キー/
■58074 / inTopicNo.2)  Re[1]: ビットマップの余白のトリミング
□投稿者/ shu (549回)-(2011/03/24(Thu) 21:24:13)
No58070 (レーモン さん) に返信
> VB2008 Expressを使用しています。
> ビットマップの画像の上下左右に余白がある場合、これをプログラム上でトリミングしたいと思うのですが、
> 思いつく方法としては、ビットマップの上下左右で余白でないと判定するピクセルが現れるまでスキャンを行い
> トリミングする領域を得るという方法ですが、これよりも高速に行える方法はないでしょうか?
> なお、余白でないピクセルであるかどうかの判断は、ピクセルの色(白またはRGBの値を用いて計算)で行います。
> どうぞよろしくお願いします

基本的にそういう方法でいいと思いますが、スキャンの手順として左上から右下に(+1,+1)ずつしてスキャンしていき
色が変わったら(-1,0)と(0,-1)の色を確認し余白の色でない方に進み最終的に左上の境界点を探す。右下の境界点も
右下から同様に開始して探すとよいかと思います。ただし余白部分の色が余白でないところに存在するともう少し調整が
必要になると思います。最初の(+1,+1)の部分を大き目にスキャンすると多少速度を上げることが出来るかもしれません。
引用返信 編集キー/
■58086 / inTopicNo.3)  Re[2]: ビットマップの余白のトリミング
□投稿者/ レーモン (2回)-(2011/03/25(Fri) 09:10:34)
No58074 (shu さん) に返信
shu様、コメントありがとうございます。やはりスキャンの方法しかないですか...orz
ちなみに、余白の部分の色は画像本体の中にも含まれていますので、アルゴリズムについては
色々と考えてみます。
結果につきましてはまた報告します。
引用返信 編集キー/
■58100 / inTopicNo.4)  Re[3]: ビットマップの余白のトリミング
□投稿者/ レーモン (3回)-(2011/03/25(Fri) 14:54:16)
やはりビットマップ上のスキャンでは時間がかかるため、
Imaging.BitmapDataよりビットマップの色コードをバイト配列で取得しようと思います。

Dim bmp As Bitmap
Dim bmData As Imaging.BitmapData
Dim rect As Rectangle
Dim intStride As Integer
Dim Scan0 As System.IntPtr
Dim bytBits As Byte()

'bmpのフォーマットはImaging.PixelFormat.Format32bppArgbである。

rect = New Rectangle(0, 0, bmp.Width, bmp.Height)
bmData = bmp.LockBits(rect, _
Imaging.ImageLockMode.ReadOnly, _
Imaging.PixelFormat.Format24bppRgb)

intStride = bmData.Stride
Scan0 = bmData.Scan0
bytBits = New Byte(intStride * bmp.Height - 1) {}

Marshal.Copy(Scan0, bytBits, 0, intStride * bmp.Height)
bmp.UnlockBits(bmData)


このバイト配列内を検索した方が処理が速いようですので、この方法で行おうと思いますが、
BitmapDataを取得する際、PixelFormatをFormat24bppRgbにしているので、BitmapDataの
Strideがビットマップの横ピクセル数×3(RGB分)より若干多いようなのですが
(画像によって違うようである)、このバイト配列の色情報を見ようとするとき、
Stride毎に区切ったデータにおいて、ビットマップの横ピクセル数×3をはみ出た部分は
ビットマップの色情報とは関係ないですよね?
引用返信 編集キー/
■58102 / inTopicNo.5)  Re[4]: ビットマップの余白のトリミング
□投稿者/ shu (551回)-(2011/03/25(Fri) 15:23:09)
No58100 (レーモン さん) に返信

> Strideがビットマップの横ピクセル数×3(RGB分)より若干多いようなのですが
> (画像によって違うようである)、このバイト配列の色情報を見ようとするとき、
> Stride毎に区切ったデータにおいて、ビットマップの横ピクセル数×3をはみ出た部分は
> ビットマップの色情報とは関係ないですよね?
Strideは4バイト境界に切り上げたスキャン幅なので多くなっているかと思います。
byte配列の大きさはwidth * height * 3で求めたほうが良いかと思います。



引用返信 編集キー/
■58107 / inTopicNo.6)  Re[5]: ビットマップの余白のトリミング
□投稿者/ レーモン (4回)-(2011/03/25(Fri) 16:01:17)
No58102 (shu さん) に返信
shu様、コメントありがとうございます。

> byte配列の大きさはwidth * height * 3で求めたほうが良いかと思います。

これは、最初の

> bytBits = New Byte(intStride * bmp.Height - 1) {}

の部分を

bytBits = New Byte(bmp.Width * bmp.Height * 3 - 1) {}

とすればいいのでしょうか?
その場合、バイト配列にコピーする際の

> Marshal.Copy(Scan0, bytBits, 0, intStride * bmp.Height)

の部分のコードはどのようになるでしょうか?
色々なサイトのサンプルを見よう見まねで書いているので、ちゃんと理解していない部分もありますが、
なにとぞご了承ください。
引用返信 編集キー/
■58111 / inTopicNo.7)  Re[6]: ビットマップの余白のトリミング
□投稿者/ shu (552回)-(2011/03/25(Fri) 16:14:35)
No58107 (レーモン さん) に返信

>>bytBits = New Byte(intStride * bmp.Height - 1) {}
>
> の部分を
>
> bytBits = New Byte(bmp.Width * bmp.Height * 3 - 1) {}
>
> とすればいいのでしょうか?
そういうことです


> その場合、バイト配列にコピーする際の
>
>>Marshal.Copy(Scan0, bytBits, 0, intStride * bmp.Height)
>
> の部分のコードはどのようになるでしょうか?

intStride * bmp.Height を bmp.Width * bmp.Height * 3とすれば良いです。


次のようにすると同じ計算をしなくて良く分かりやすいかと思います。
Dim intSize as Integer = bmp.Width * bmp.Height * 3

bytBits = New Byte(intSize - 1) {}
Marshal.Copy(Scan0, bytBits, 0, intSize)



引用返信 編集キー/
■58115 / inTopicNo.8)  Re[7]: ビットマップの余白のトリミング
□投稿者/ レーモン (5回)-(2011/03/25(Fri) 16:34:52)
No58111 (shu さん) に返信
shu様、早速のご回答ありがとうございます。

> Dim intSize as Integer = bmp.Width * bmp.Height * 3
>
> bytBits = New Byte(intSize - 1) {}
> Marshal.Copy(Scan0, bytBits, 0, intSize)

このようにすると、バイト配列のbytBits(bmp.Width * 3)の値を調べると0が入っておりました。
こちらが期待しているのは、この値がビットマップの2段目の一番左のピクセルのRの値を表わすことなのですが、
実際にビットマップのここの色は白(R,G,B=255)でしたので、このピクセルを表わす内容ではなさそうです。
このバイト配列のbytBits(bmp.Width * 3)の位置は、何を表わしているのでしょうか?
そうなると、やはりStrideを考慮しなくてはならないのでしょうか?
引用返信 編集キー/
■58119 / inTopicNo.9)  Re[8]: ビットマップの余白のトリミング
□投稿者/ shu (555回)-(2011/03/25(Fri) 16:59:34)
No58115 (レーモン さん) に返信

> このようにすると、バイト配列のbytBits(bmp.Width * 3)の値を調べると0が入っておりました。
> こちらが期待しているのは、この値がビットマップの2段目の一番左のピクセルのRの値を表わすことなのですが、
> 実際にビットマップのここの色は白(R,G,B=255)でしたので、このピクセルを表わす内容ではなさそうです。
> このバイト配列のbytBits(bmp.Width * 3)の位置は、何を表わしているのでしょうか?
> そうなると、やはりStrideを考慮しなくてはならないのでしょうか?

間違えていたようです。やはりStrideを使用しないといけなかったようです。
必要ない領域があるということでよかったみたいです。
引用返信 編集キー/
■58124 / inTopicNo.10)  Re[9]: ビットマップの余白のトリミング
□投稿者/ レーモン (6回)-(2011/03/25(Fri) 21:06:46)
No58119 (shu さん) に返信
shu様、いつもありがとうございます。
とりあえず、色々と試行錯誤した結果、BitmapのGetPixelを使ってスキャンするよりは
BitmapDataよりバイト配列を取得し、こちらをスキャンしたほうが速そうなので、
これで試してみたいと思います。

ところで、BitmapDataより取得したバイト配列の0番目は、必ず元のBitmapの(x,y)=(0,0)のピクセル、
つまりBitmapの左上端のピクセルのRの値になるのでしょうか?
前提として、このBitmapはVBで作成したImaging.PixelFormat.Format32bppArgbのフォーマットであり、
BitmapDataはこのBitmapのLockBitsメソッドで、このBitmapの全領域をImaging.PixelFormat.Format24bppRgbの
フォーマットで取得したものとします。

上記の認識で正しいのであれば、例えばこのBitmapの上余白を求めるには、BitmapDataより取得したバイト配列で、
Strideの余分を除いた配列要素のうち、余白でないと判断する値が出現する箇所より上の段のピクセルまでと
判断できると思うのですが、いかがでしょうか?
引用返信 編集キー/
■58125 / inTopicNo.11)  Re[10]: ビットマップの余白のトリミング
□投稿者/ shu (556回)-(2011/03/25(Fri) 22:23:22)
No58124 (レーモン さん) に返信

> ところで、BitmapDataより取得したバイト配列の0番目は、必ず元のBitmapの(x,y)=(0,0)のピクセル、
> つまりBitmapの左上端のピクセルのRの値になるのでしょうか?
> 前提として、このBitmapはVBで作成したImaging.PixelFormat.Format32bppArgbのフォーマットであり、
> BitmapDataはこのBitmapのLockBitsメソッドで、このBitmapの全領域をImaging.PixelFormat.Format24bppRgbの
> フォーマットで取得したものとします。
LockBitsで指定したPixelFormatに変換されて配列化されるのでその考えて良いです。


> 上記の認識で正しいのであれば、例えばこのBitmapの上余白を求めるには、BitmapDataより取得したバイト配列で、
> Strideの余分を除いた配列要素のうち、余白でないと判断する値が出現する箇所より上の段のピクセルまでと
> 判断できると思うのですが、いかがでしょうか?
判断の方法として確実で間違いはないと思います。大きい画像になると多少パフォーマンスに影響が出るかもしれません。
引用返信 編集キー/
■58128 / inTopicNo.12)  Re[9]: ビットマップの余白のトリミング
□投稿者/ Azulean (716回)-(2011/03/26(Sat) 00:00:59)
No58119 (shu さん) に返信
> 間違えていたようです。やはりStrideを使用しないといけなかったようです。
> 必要ない領域があるということでよかったみたいです。

Bitmap 周りは昔から 1 ラインを 4 の倍数バイトで扱うことが多いので、Width * 3 に対して 0 〜 3 バイト余分につくことがよくあります。
全領域のピクセルデータを取り出したいのであれば、1 ラインは Stride で得られるバイト数として Stride * Height でコピーし、データを見ていくときには y * Stride + x * 3 の位置を見ていくことになります。

多分、4 の倍数バイトで扱う方が効率良いという論理、あるいは時期があったんだと思いますが、このあたりの理由を説明できるほど詳しいわけではありません。

# 一応、Stride は負の値を取り得るとされていますが、どういったときかまでは知りません。
引用返信 編集キー/
■58129 / inTopicNo.13)  Re[10]: ビットマップの余白のトリミング
□投稿者/ レーモン (7回)-(2011/03/26(Sat) 10:09:10)
shu様、Azulean様、コメントありがとうございます。
とりあえず、 ビットマップを上下左右から単純にスキャンした場合に、
BitmapDataで取得したバイト配列の(y * Stride + x * 3)の要素を基準にR(基準)G(基準+1)B(基準+2)の内容を見ることによって、
BitmapのGetPixelでスキャンするよりも格段に高速であることが分かりました。
2000×3000程度のビットマップでも、(余白の度合いにもよりますが)スキャン処理にかかる時間は私のPCでは1秒もかかりませんでした。
(BitmapのGetPixelでスキャンした場合は7〜8秒かかった)

なお、Strideが負の場合は、ビットマップはボトムアップという記載がありますので、これはおそらくバイト配列の開始位置が
ビットマップの右下端から始まるということだと思いますが、検証したビットマップの中にはStrideが負のものがないので、
この点はもう少し調べます。
http://msdn.microsoft.com/ja-jp/library/system.drawing.imaging.bitmapdata.stride.aspx

とりあえず、これで解決としたいと思います。
ありがとうございました。
解決済み
引用返信 編集キー/
■58130 / inTopicNo.14)  Re[11]: ビットマップの余白のトリミング
□投稿者/ よねKEN (685回)-(2011/03/26(Sat) 10:54:42)
No58129 (レーモン さん) に返信
> BitmapDataで取得したバイト配列の(y * Stride + x * 3)の要素を基準にR(基準)G(基準+1)B(基準+2)の内容を見ることによって、
> BitmapのGetPixelでスキャンするよりも格段に高速であることが分かりました。

ちょっと気になることがあるのですが、BitmapData内の色の並び順はBGRですのでご注意を。
引用返信 編集キー/
■58131 / inTopicNo.15)  Re[12]: ビットマップの余白のトリミング
□投稿者/ レーモン (8回)-(2011/03/26(Sat) 11:30:06)
No58130 (よねKEN さん) に返信
よねKENさん、コメントありがとうございます。

> ちょっと気になることがあるのですが、BitmapData内の色の並び順はBGRですのでご注意を。

ありゃりゃ、そうでしたか。危ない所でした。
では、これにて改めて解決としたいと思います。
ありがとうございました。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -