Visual C# 2005 または Visual Basic 2005 において、Graphics で加工した画像を 8Bit のビットマップとして保存する方法を教えてください。


下記は試して NG だった方法です。
1. コンストラクタ Bitmap (Int32, Int32, PixelFormat) で System.Drawing.Imaging.PixelFormat.Format8bppIndexed を指定する → Graphics を生成できない

2. 非パレットのビットマップに描画してから Bitmap.Clone(Rectangle, PixelFormat) で 8 Bit化 → "メモリ不足"の旨を告げるエラーが出てしまう。

3. SetPixel/GetPixel を使ってピクセルごとにコピー → パレットのビットマップでは SetPixel が例外を投げる。


宜しくお願いします。

回答の条件
  • 1人5回まで
  • 登録:2007/05/17 18:43:25
  • 終了:2007/05/24 18:45:03

回答(1件)

id:mj99 No.1

mj99回答回数138ベストアンサー獲得回数382007/05/21 03:14:50

ポイント60pt

いろいろ探したのですけど、使えそうなものがありませんね。。。

Visual C#を使用すると、 .gif ファイルに新しいカラー テーブルを保存する方法

上記URLは、グレースケールのGIF(モノクロ256階調)を作成するサンプルです。

----

結局、8bitで保存するには、自分で変換プログラムを書くしかなさそうです。

上記URLを参考に、フルカラーを256色に減色するコードを作ってみました。

(手元にVB.NETしかなかったので、申し訳ありませんが、VBのコードでカンベン)

Imports system.Drawing.Imaging

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        ' 下準備
        Dim bm As New Bitmap(300, 300) ' これが保存するサイズになる。とりあえず適当
        Dim gs As Graphics = Graphics.FromImage(bm)

        ' ここからGraphicsに描画するものとします。この例は24bitのpngをGraphicsに載せる
        Dim im As Image = Image.FromFile("c:\users\test_24bit.png")
        gs.DrawImage(im, New Point(0, 0))
        gs.Dispose()
        ' ここでGraphicsに描画が終わったものとします

        ' 8bppIndexedで保存する
        Save8bppIndexedBMP(bm, "c:\users\test_8bit.bmp")

        MsgBox("完了", MsgBoxStyle.Information)

    End Sub

    Public Sub Save8bppIndexedBMP(ByVal image As Image, ByVal filename As String)

        ' 目的のイメージと同じ大きさの8bitビットマップを作る
        Dim Width As Integer = image.Width
        Dim Height As Integer = image.Height
        Dim bitmap As Bitmap = New Bitmap(Width, Height, PixelFormat.Format8bppIndexed)

        ' 減色パレットを作る
        Dim pal As ColorPalette = bitmap.Palette
        Dim wr() As Byte = {0, 36, 72, 108, 144, 180, 216, 255} ' 赤8階調
        Dim wg() As Byte = {0, 36, 72, 108, 144, 180, 216, 255} ' 緑8階調
        Dim wb() As Byte = {0, 84, 168, 255}                    ' 青4階調(悲惨)
        Dim x As Integer = 0
        For i As Integer = 0 To 7
            For j As Integer = 0 To 7
                For k As Integer = 0 To 3
                    pal.Entries(x) = Color.FromArgb(0, wr(i), wg(j), wb(k))
                    x = x + 1
                Next
            Next
        Next
        bitmap.Palette = pal

        ' 保存したい対象を24bitに展開
        Dim BmpCopy As Bitmap = New Bitmap(Width, Height, PixelFormat.Format24bppRgb)
        Dim g As Graphics = Graphics.FromImage(BmpCopy)
        g.PageUnit = GraphicsUnit.Pixel
        g.DrawImage(image, 0, 0, Width, Height)
        g.Dispose()

        Dim rect As Rectangle = New Rectangle(0, 0, Width, Height)

        ' 24bitの画像を総スキャン(RGB値が3バイトで1組のバイト列になる)
        Dim bitmapData24 As BitmapData = BmpCopy.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb)
        Dim bufsize As Integer = bitmapData24.Width * bitmapData24.Stride
        Dim buf(bufsize - 1) As Byte
        System.Runtime.InteropServices.Marshal.Copy(bitmapData24.Scan0, buf, 0, bufsize)

        ' 8bitピクセルデータ配列を作成する(ここにパレットインデックスを書き込む)
        Dim bitmapData8 As BitmapData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed)
        Dim stride As UInteger = Math.Abs(bitmapData8.Stride)
        Dim pixels8 As IntPtr = bitmapData8.Scan0

        ' 24bitから8bitへの減色を行う
        Dim nOffset8 As Integer = 0
        For nOffset24 As Integer = 0 To bufsize - 1 Step 3
            Dim wrx As Integer = Fix((buf(nOffset24 + 0)) / 32) ' 赤8階調に減色
            Dim wgx As Integer = Fix((buf(nOffset24 + 1)) / 32) ' 緑8階調に減色
            Dim wbx As Integer = Fix((buf(nOffset24 + 2)) / 64) ' 青4階調に減色

            ' パレットインデックスを算出
            Dim src(0) As Byte
            src(0) = CByte(wrx * 32 + wgx * 4 + wbx)

            ' 8bitピクセルアドレスへパレットインデックスを書き込む
            System.Runtime.InteropServices.Marshal.Copy(src, 0, pixels8.ToInt32 + nOffset8, 1)
            nOffset8 = nOffset8 + 1
        Next
        BmpCopy.UnlockBits(bitmapData24)
        bitmap.UnlockBits(bitmapData8)

        bitmap.Save(filename, ImageFormat.Bmp)
        BmpCopy.Dispose()
        bitmap.Dispose()

    End Sub

End Class
id:takel

ありがとうございます。

時間がなくてまだ試せていませんが、やっぱり自力でパレット化しなくては駄目そうなんですね。

それだけわかっただけでも十分な情報でした。

もともと使う必要があるのが VB2005 用なので VB のコードで大丈夫です。

ありがとうございました。

2007/05/22 05:17:03
  • id:takel
    ごめんなさい!うっかり自動終了の期間が過ぎてしまいました!!
    昔ははてなって『投げ銭』っていうシステムがあったと思ったのですが…。
    丁寧な返答にたった60ポイントしかお支払いできず、いるか賞も差し上げられませんでした!
    mj99 さんにはせめてあと200ポイント程度はお支払いしたいのですが、どうしたらいいでしょう?
  • id:mj99
    いえいえ、そのお気持ちだけで。
    このままで問題ありません。

    しっかし、このコードにバグあること気付いた。
    時間があるときに訂正コードをコメントに載せておきます。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません