質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

2141閲覧

byte配列を8bitグレースケール画像で保存する際の表示のずれを無くしたい

rnmr

総合スコア7

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2020/01/14 11:06

編集2020/01/16 00:18

byte配列を8bitグレースケール画像で保存する際の表示のずれを無くしたい

Visual studio 2019のWindowsフォームアプリケーション(.NET framework)を使用してこちらのサイト(https://symfoware.blog.fc2.com/blog-entry-2195.html)を参考に8bitグレースケール画像を作成しようとしておりますが、画像サイズの部分の値を変更するとピクセル数全体に色が割り当てられずに抜けがある画像が作成されてしまいます。割り当てられる領域に限界があることを考慮し、コードよりも大きいサイズでは(例500×300ピクセル)抜けがなくすべてのピクセルに指定した色が割り当てられていることを確認しました。
色を割り当てたい具体的な画像サイズとしては330×330ピクセルです。考えられる原因および解決策ございましたら是非ご教授願いたく思います。
よろしくお願いいたします。

発生している問題・エラーメッセージ

画像の下から1行目全てと2行9列目以降で指定した色(灰色)が割り当てられず黒色となっています。

該当のソースコード

using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices;//追加 namespace image { public partial class Form1:Form { public Form1() { int Gwidth = 330; int Gheight = 330; byte[] Gdata = new byte[Gwidth * Gheight]; for(int h = 0; h < Gheight; h++) { for (int w = 0; w < Gwidth; w++) { data[h * Gwidth + w] = (byte)225; } } Bitmap Gimg = new Bitmap(Gwidth, Gheight, PixelFormat.Format8bppIndexed); ColorPalette pal = Gimg.Palette; for (int i = 0; i < 256; ++i) { pal.Entries[i] = Color.FromArgb(i, i, i); } img.Palette = pal; BitmapData bmpdata = Gimg.LockBits( new Rectangle(0, 0, Gwidth, Gheight), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed ); Marshal.Copy(Gdata, 0, bmpdata.Scan0, Gdata.Length); Gimg.UnlockBits(bmpdata); Gimg.Save(@"D:¥sources¥new3.jpg"); } } } //以下変更後 public Form1() { ////byte配列をグレースケール画像で保存する//// Bitmap empty = new Bitmap(330, 330);//空の画像(入力) int Gwidth = 330; int Gheight = 330; byte[] Gdata = new byte[Gwidth*Gheight]; for (int h = 0; h < Gheight; h++) { for(int w =0; w < Gwidth; w++) { Gdata[h *Gwidth+ w] = (byte)numbers[h*Gwidth+w]; } } unsafe { //出力ビットマップの領域確保 Bitmap Gimg = new Bitmap(empty.Width, empty.Height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed); //出力ビットマップをシステムメモリにロック BitmapData dataRgb = empty.LockBits(new Rectangle(0, 0, empty.Width, empty.Height),ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb); BitmapData bmpdata = Gimg.LockBits(new Rectangle(0, 0, Gimg.Width, Gimg.Height), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed); byte* datas24bppRgb = (byte*)dataRgb.Scan0; byte* datas8bppGray = (byte*)bmpdata.Scan0; int lineIn = empty.Width * 3; int lineInNew = (lineIn % 4 != 0) ? ((lineIn / 4 + 1) * 4) : lineIn; int lineInDiff = lineInNew - lineIn; int offsetIn = 0; int lineOut = empty.Width; int lineOutNew = (lineOut % 4 != 0) ? ((lineOut / 4 + 1) * 4) : lineOut; int lineOutDiff = lineOutNew - lineOut; int offsetOut = 0; // ポインタを介して、ピクセル毎にグレースケール化 for (int y = 0; y < empty.Height; y++) { for (int x = 0; x < empty.Width; x++) { // 4バイト境界の考慮 datas8bppGray[y * empty.Width + x + offsetOut] = (byte)( ((int)(datas24bppRgb[y * empty.Width * 3 + x * 3 + offsetIn]) + (int)(datas24bppRgb[y * empty.Width * 3 + x * 3 + offsetIn + 1]) + (int)(datas24bppRgb[y * empty.Width * 3 + x * 3 + offsetIn + 2])) / 3); } // 4バイト境界の考慮 offsetIn += lineInDiff; offsetOut += lineOutDiff; } //パレット情報の設定 ColorPalette pal = Gimg.Palette; for (int i = 0; i < 256; ++i) { pal.Entries[i] = System.Drawing.Color.FromArgb(i, i, i); } Gimg.Palette = pal; Marshal.Copy(Gdata, 0, bmpdata.Scan0, Gdata.Length); //出力ビットマップのロック解除 empty.UnlockBits(dataRgb); Gimg.UnlockBits(bmpdata); Gimg.Save(@"D:\sources\new3.jpg"); } ////byte配列をグレースケール画像で保存する//// }

試したこと

333×333、330×330ピクセルでは抜けが発生しましたが
250×250、500×300では全てのピクセルに指定した輝度値(225)が割り振られていました。
2020/1/16追記「//以下変更点」のようにコードを変えてみましたがピクセル抜けが解消されませんでした。

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

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

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

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

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

Wind

2020/01/15 00:17

該当のソースコードが再現出来ないです。 using System.Runtime.InteropServices;を追加し、 dataをGdataに、imgをGimgに修正した物で良いのでしょうか?
rnmr

2020/01/15 03:17

>Wind様 申し訳ありません。記述し忘れました。参照と変数をそのように変更したもので出来ると思われますがいかがでしょうか?
rnmr

2020/01/16 00:22

>Wind様 情報提供誠にありがとうございます。 教えていただいたサイトのコードをこちらでも試してみましたが、やはり画像のズレおよび抜けが解消されませんでした。 当該コードを下部に追記しましたので間違っているところがあれば御教授願います。
Wind

2020/01/16 00:32 編集

画像が8bitグレースケールでしたら横スケールを4の倍数になるように追加しますが、 カラーでも良ければ、32bitカラーに置き換えれば4の倍数になります。 24bitカラーですと330x3÷4=247.5で4の倍数になりませんので、同じ現象が発生します。
rnmr

2020/01/16 00:41

>Wind様 ありがとうございます。 理解不足で大変申し訳ないのですが、24bitカラーでなく32bitカラーであれば4の倍数となり、 問題が解消するということでしょうか? 試しにPixelFormat.Format24bppRgb⇒PixelFormat.Format32bppArgbと変更してみましたが 改善されませんでした。
Wind

2020/01/16 01:29 編集

画像フォーマットが32bitでしたら必ず4で割り切れますので、この問題は発生しません。 32bitカラーを使用した330x330画像の作成ソースコードを回答に書きました。 画像フォーマットが8bitグレースケールで無ければならない場合は、目的に応じて横サイズを4で割り切れる様に画素を削除か補完してください。(例:330→2画素削除して328 or 2画素補完して332)
guest

回答2

0

ベストアンサー

32bitカラーで330x330のグレースケールを描画する場合を、
元のソースコードに加筆しました。(8bitグレースケールの再現と対策も試した後なので、汚いですが)

C#

1 int Gwidth = 330; 2// int Gwidth = 332; // 8bitグレースケールは4バイト境界なので横のサイズを4の倍数(332等)にするしか無い 3 int Gheight = 330; 4 5 // 8bitグレースケール 6// byte[] Gdata = new byte[Gwidth * Gheight]; 7 // 32bitカラーを使用する場合 8 byte[] Gdata = new byte[Gwidth * 4 * Gheight]; 9 10 for(int h = 0; h < Gheight; h++) 11 for (int w = 0; w < Gwidth * 4; w++) 12 if(w % 4 != 3) 13 Gdata[h * Gwidth * 4 + w] = (byte)225; // グレースケールの色 14 else 15 Gdata[h * Gwidth * 4 + w] = (byte)255; // 透過度(255固定) 16 17// Bitmap Gimg = new Bitmap(Gwidth, Gheight, PixelFormat.Format8bppIndexed); 18 Bitmap Gimg = new Bitmap(Gwidth, Gheight, PixelFormat.Format32bppArgb); 19 20/* 21 ColorPalette pal = Gimg.Palette; 22 for (int i = 0; i < 256; ++i) { 23 pal.Entries[i] = Color.FromArgb(i, i, i); 24 } 25 Gimg.Palette = pal; 26*/ 27 28 BitmapData bmpdata = Gimg.LockBits( 29 new Rectangle(0, 0, Gwidth, Gheight), 30 ImageLockMode.WriteOnly, 31// PixelFormat.Format8bppIndexed 32 PixelFormat.Format32bppArgb 33 ); 34 35 Marshal.Copy(Gdata, 0, bmpdata.Scan0, Gdata.Length); 36 Gimg.UnlockBits(bmpdata); 37 38 Gimg.Save(@"D:¥sources¥new3.jpg"); 39

投稿2020/01/16 01:04

編集2020/01/16 01:24
Wind

総合スコア442

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

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

rnmr

2020/01/16 02:01 編集

>Wind様 ソースコードまで記述してくださってありがとうございます。 これで確かに32bitで330×330ピクセルの画像が作成されることは確認致しました。ここで私の場合予め330×330(108900)の配列の中に輝度値を割り振っており、「グレースケールの色」の部分の225をこの配列の値にして1ピクセル毎に色が変わるようにしたいと考えておりますが、 Gdata[h * Gwidth * 4 + w] = (byte)225⇒Gdata[h * Gwidth * 4 + w] = (byte)numbers[h*Gwidth+w]とすると「配列の境界外です」とエラーが発生してしまいますが、これは8bitの画像でないと実現は不可能でしょうか? お手数をお掛けして申し訳ありませんが宜しくお願い致します。
Wind

2020/01/16 02:20

こちらで同じサイズの輝度値配列を作成して確認したところ、forでwの範囲を4倍にしているので、h=327 w=990(327x330+990=108900) で止まりました。 参照している輝度値配列側のwを4で割れば良いかと思います。 Gdata[h * Gwidth * 4 + w] = (byte)numbers[h * Gwidth + w / 4];
rnmr

2020/01/16 02:28

>Wind様 急な変更点にも対応していただき誠にありがとうございます。 おかげで目的の画像を作成することが出来ました。
guest

0

Bitmapですよね。
画像サイズに依存しているなら、Bitmapには1行のサイズがきりのいい値ではない場合にパディングが勝手に入ります。
水平1ラインあたりのバイト数は必ず4の倍数になっていることという決まりがあるのでこれに該当してますでしょうか?

投稿2020/01/14 11:32

s.falken

総合スコア8

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

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

rnmr

2020/01/14 23:04

>s.falken様 回答誠にありがとうございます。 Bitmapです。 なるほど、調べると4の倍数で割り切れない余りの部分は「0」で補完されるということだったんですね。 とすると割り切れないサイズの画像を正しく表示するには、4の倍数で画像より大きなサイズのBitmapを用意して途中で改行するように画像のピクセルを表示しないといけないということでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問