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

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

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

Re[3]: Bitmap.LockBits() を使った場合の画像サイズ


(過去ログ 60 を表示中)

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

■34794 / inTopicNo.1)  Bitmap.LockBits() を使った場合の画像サイズ
  
□投稿者/ Jane (1回)-(2009/04/09(Thu) 16:25:26)

分類:[.NET 全般] 

はじめまして。

Bitmap.LockBits() Bitmap.UnlockBits() を使って画像処理を行うクラスの作成を行っていたのですが、行き詰ってしまいました。
サイズの変更を行わない画像処理であれば問題ないのですが、
画像サイズを変える場合上手くいきません。
画像サイズの変更は、Bitmap.LockBits()で取得したBitmapDataインスタンスのWidth,Height,Strideを変更することで対応しようとしていますが、
自作の処理が終了し、描画に入った時に、以下の症状が発生します。

・元の画像より小さいピクセル数に変更した場合、画像の左上に縮小した画像が表示されるが、画像サイズは変わらない
・元の画像より大きいピクセル数に変更した場合、アプリケーションが終了する。(try〜catchでは捕まらない)

おそらく元のBitmapの大きさが変わっていないため、BitmapとBitmapDataのサイズが不整合を起こしているため発生するのだと思われますが、
BitmapのWidth,HeightはReadonlyのため、対応する方法の見当が付きません。

サイズが変わった場合は変更後のサイズのBitmapを作成し、そちらにコピーするという方法も考えましたが、
出来るのであれば同じBitmapのインスタンスで対応できるのが希望です。

無理、もしくはここを直せばいける、というアドバイスを頂けないでしょうか?


環境
# Windows Vista
# Visual Studio 2008 (C#)


途中に以下の4つのクラスが現われますが、枝葉だと思われますので省かせて頂きました。
NoSizeChangeFilter/SizeChangeFilter/Filter (コメントアウトしているため実際の処理はしてない)
SizeInfo (Width,Height,Stride等を保持するだけ)



public class Manager :IDisposable
{

    private Bitmap _bmp;
    private BitmapData _bmpData;
    private Int32 _width;
    private Int32 _height;
    private Int32 _stride;
    private Int32 _size;

    private Int32 _pixelSize = 3;

    private Int32 _byteShiftRed = 2;
    private Int32 _byteShiftGreen = 1;
    private Int32 _byteShiftBlue = 0;

    //Format24bppRgbのみ対応
    public Manager(Bitmap bmp)
    {
        _width = bmp.Width;
        _height = bmp.Height;
        _bmp = bmp;

        // Lock
        Rectangle rect = new Rectangle(0, 0, _width, _height);
        _bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
        _stride = _bmpData.Stride;
        _size = _stride * _height;
    }

    #region Dispose

    public void Dispose()
    {
        // Unlock
        if ((_bmp != null) && (_bmpData != null))
            _bmp.UnlockBits(_bmpData);
    }
    
    #endregion

    #region フィルタ適用

    public void ApplyFilter(List<Filter> filters)
    {

        if (filters.Count == 0)
            return;

        // ポインタ取得
        IntPtr ptr = _bmpData.Scan0;

        // 全Byteを格納する配列作成
        byte[] rgbBytes = new byte[_size];

        // 配列に全データをコピー
        System.Runtime.InteropServices.Marshal.Copy(ptr, rgbBytes, 0, _size);
        
        foreach (var i in filters)
        {
            if (i == null)
                continue;

            if (i is NoSizeChangeFilter)
            {
                //サイズ変更なしフィルタの適用

                //キャスト
                var f = i as NoSizeChangeFilter;

                //フィルタ適用
                //f.Apply(this, rgbValues);
            }
            else if (i is SizeChangeFilter)
            {
                //サイズ変更ありフィルタの適用

                //キャスト
                var f = i as SizeChangeFilter;

                //変更前の情報を保持
                var olfInfo = new SizeInfo(this);

                //変更後のサイズを算出
                Size newSize = f.ApplyedSize(this);
                Int32 newStride = newSize.Width * _pixelSize;
                //Strideを4Byte単位に調節
                newStride += (newStride % 4 == 0) ? (0) : (4 - newStride % 4);

                //サイズ変更
                _bmpData.Width = newSize.Width;
                _bmpData.Height = newSize.Height;
                _bmpData.Stride = newStride;

                //メンバ更新
                _width = newSize.Width;
                _height = newSize.Height;
                _stride = _bmpData.Stride;
                _size = _stride * _height;

                //新しいサイズの配列作成
                var newBytes = new Byte[_size];

                ////フィルタ適用
                //f.Apply(this, rgbValues, newValue, olfInfo);

                //配列の差し替え
                rgbBytes = newBytes;

            }

        }

        // 反映後のデータをbmpのポインタにコピーしなおす
        System.Runtime.InteropServices.Marshal.Copy(rgbBytes, 0, ptr, _size);

    }

    #endregion

}



引用返信 編集キー/
■34796 / inTopicNo.2)  Re[1]: Bitmap.LockBits() を使った場合の画像サイズ
□投稿者/ オショウ (144回)-(2009/04/09(Thu) 18:05:19)
> サイズが変わった場合は変更後のサイズのBitmapを作成し、そちらにコピーするという方法も考えましたが、
> 出来るのであれば同じBitmapのインスタンスで対応できるのが希望です。
>
> 無理、もしくはここを直せばいける、というアドバイスを頂けないでしょうか?

  これに、カキコしようかしまいか迷ったのですが・・・

  まず、無理です。

  それは簡単な理由です。
  Bitmapを管理・処理する方法に依存しています。

  よって、どうしても別インスタンスを作らないで行いたい。というので
  あれば、クラスライブラリで提供されている機能を使わず、独自にイン
  プリしなければなりません。

  ヒント的には・・・
  バイトの1次元配列にBitmap画像をすべて読み込み、ビット配列として
  処理するならば可能です。

  が、それらを.NET上で行うのは、時間的コストの無駄です。
  C言語でコーディングするのがよいでしょうが・・・
  やってやれないことはない。なんせ.NETが無い時代には独自インプリし
  て処理していたのだから・・・

  ただ、そのコストを賄ってもよい!と言う前提でです。

※ 諦めて素直にクラスライブラリ使って処理すれば?
  別インスタンス設けて・・・

以上。

引用返信 編集キー/
■34797 / inTopicNo.3)  Re[2]: Bitmap.LockBits() を使った場合の画像サイズ
□投稿者/ 渋木宏明(ひどり) (1110回)-(2009/04/09(Thu) 19:22:38)
渋木宏明(ひどり) さんの Web サイト
>   バイトの1次元配列にBitmap画像をすべて読み込み、ビット配列として
>   処理するならば可能です。

既にそうなっているようです。

なので、この場合、新規ビットマップを返すように ApplyFilters() メソッドのインターフェースを変更するだけぽ。

ただし、そうなると Manager クラスの設計も考え直すべきかもしれませんが。

引用返信 編集キー/
■34802 / inTopicNo.4)  Re[1]: Bitmap.LockBits() を使った場合の画像サイズ
□投稿者/ Azulean (351回)-(2009/04/09(Thu) 22:33:29)
No34794 (Jane さん) に返信
> サイズが変わった場合は変更後のサイズのBitmapを作成し、そちらにコピーするという方法も考えましたが、
> 出来るのであれば同じBitmapのインスタンスで対応できるのが希望です。
なぜ、1つのインスタンスにこだわるのでしょうか?
クラスの設計上の問題でしょうか?そうであれば、その問題に着目して改善する方向で動くべきかと思います。

他の方もレスでも語られていますが、基本的にはインスタンスを作り直すしかありません。
インスタンスを作り直さない手法を頑張っても、バイト配列で管理しても、画面に描画したり、ファイルに保存したりする段階で無理(あるいは無謀)かもしれません。


No34794 (Jane さん) に返信
> 画像サイズの変更は、Bitmap.LockBits()で取得したBitmapDataインスタンスのWidth,Height,Strideを変更することで対応しようとしていますが、
> 自作の処理が終了し、描画に入った時に、以下の症状が発生します。
BitmapDataクラスのWidth, Height, Strideプロパティを変更することは可能ですが、一般的な使い方ではないのでどんな動きになるか予想できません。
引用返信 編集キー/
■34803 / inTopicNo.5)  Re[3]: Bitmap.LockBits() を使った場合の画像サイズ
□投稿者/ 倉田 有大 (529回)-(2009/04/09(Thu) 22:47:16)
Cで書いてもBitmapの大きさを変えると言うことはメモリーを確保し直さないと行けないので、一度Bitmapオブジェクト作り直すのが手っ取り早いと思いますが。

LockBits中に、メモリー取り替えるというのは荒技じゃないかなー

>BitmapのWidth,HeightはReadonlyのため、対応する方法の見当が付きません。

ということはやはりBitmapオブジェクト作り直すのが吉だと思いますよー
引用返信 編集キー/
■34811 / inTopicNo.6)  Re[2]: Bitmap.LockBits() を使った場合の画像サイズ
□投稿者/ れい (839回)-(2009/04/10(Fri) 11:20:50)
No34802 (Azulean さん) に返信
> ■No34794 (Jane さん) に返信
>>画像サイズの変更は、Bitmap.LockBits()で取得したBitmapDataインスタンスのWidth,Height,Strideを変更することで対応しようとしていますが、
>>自作の処理が終了し、描画に入った時に、以下の症状が発生します。
> BitmapDataクラスのWidth, Height, Strideプロパティを変更することは可能ですが、一般的な使い方ではないのでどんな動きになるか予想できません。

LockBits後に変更するのは可能ですが、
Heightは見てくれないし、Widthも小さい場合しか見てくれなかったかと思います。
(PixelFormatも見てくれるのに…)
また、元Bitmapの大きさは変わりません。

No34803 (倉田 有大 さん) に返信
> LockBits中に、メモリー取り替えるというのは荒技じゃないかなー

取り替えるのも可能です。
でもその場合はLockBitsでImageLockMode.UserInputBufferを設定してやらないとだめです。

> ということはやはりBitmapオブジェクト作り直すのが吉だと思いますよー

Bitmapのイメージ部分は変更できるのにサイズが変更できないのは不便ですよねぇ。
サイズやフォーマットに関してはimmutableだと。
引用返信 編集キー/
■34813 / inTopicNo.7)  Re[3]: Bitmap.LockBits() を使った場合の画像サイズ
□投稿者/ 倉田 有大 (532回)-(2009/04/10(Fri) 12:41:06)
> 取り替えるのも可能です。
> でもその場合はLockBitsでImageLockMode.UserInputBufferを設定してやらないとだめです。

お、可能なんですか、失礼しました。
同じ大きさのビットマップを入れ替えるときに使うのかな。
引用返信 編集キー/
■34815 / inTopicNo.8)  Re[3]: Bitmap.LockBits() を使った場合の画像サイズ
□投稿者/ Jane (2回)-(2009/04/10(Fri) 14:08:17)
アドバイス、ありがとうございます。
Bitmapのインスタンスを作り直さないと駄目なようですね。

初めに書いておくべきでしたが、Bitmapのインスタンスを作り直さない方向で進めたいと思っていたのは、
PictureBoxに貼り付けてあるBitmapをManagerクラスで取り込んで、直接編集(拡大、縮小、回転等々含む)しようと思っていたためでした。

とりあえずインスタンス1つという方向性は諦めて、実装方法から検討します。

ありがとうございました。
引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -