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

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

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

透明な領域の色を保持するには

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

■93582 / inTopicNo.1)  透明な領域の色を保持するには
  
□投稿者/ ももも (1回)-(2019/12/23(Mon) 23:09:11)

分類:[.NET 全般] 


アルファチャンネルを含んだPNG画像を読み込んで
スケールを変換してから
アルファチャンネルを保持したまま、PNG画像として保存したいのですが、

以下のようにして、保存すると
アルファチャンネル=0だった領域が
全てR=0,G=0,B=0の黒色になってしまいます。


Dim img2 As New Bitmap(Reso, Reso, PixelFormat.Format32bppArgb)

Dim g As Graphics = Graphics.FromImage(img2)

g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic

g.DrawImage(img1, 0, 0, Reso, Reso)

g.Dispose()

img2.Save("d:\ccc.png", ImageFormat.Png)


透明だった場所の色を保持したままで
保存するにはどのようにしたら良いですか?



引用返信 編集キー/
■93583 / inTopicNo.2)  Re[1]: 透明な領域の色を保持するには
□投稿者/ 魔界の仮面弁士 (2536回)-(2019/12/23(Mon) 23:56:35)
No93582 (ももも さん) に返信
> アルファチャンネル=0だった領域が
> 全てR=0,G=0,B=0の黒色になってしまいます。

PC が手元に無いので未検証ですが、
g.DrawImage の前に、
g.CompositingMode を変更してみるのは
どうでしょうか。
引用返信 編集キー/
■93588 / inTopicNo.3)  Re[2]: 透明な領域の色を保持するには
□投稿者/ ももも (2回)-(2019/12/24(Tue) 11:33:43)
ありがとうございます。

少し勘違いしていました。


αチャンネルを含んだPNG画像を読み込む(img1)。
img1をPNGファイルとして保存する→α=0における色情報は保存されている
img1をMarshal.Copyを使って、配列化する→α=0における色情報は保存されている

img1をリスケールしてimg2にコピーする
img2をPNGファイルとして保存する→α=0における色情報は保存されている
img2をMarshal.Copyを使って、配列化する→なぜか、ここでα=0における色情報は保存されていない。



ということで、PNGファイルとして保存すると色情報はg.CompositingModeによらず保存されるのですが、
なぜかMarshal.Copyを使って、配列化すると、保存される場合とされない場合があるようです、

これは一体どういうことでしょうか?

以下の変換を行ったソースコードです。



Dim img2 As New Bitmap(Reso, Reso, PixelFormat.Format32bppArgb)



Dim g As Graphics = Graphics.FromImage(img2)

g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
g.CompositingMode = Drawing2D.CompositingMode.SourceCopy

g.DrawImage(img1, 0, 0, Reso, Reso)

g.Dispose()



Dim pixelFormat1 As PixelFormat = img1.PixelFormat


Dim bmpData1 As BitmapData = img1.LockBits(New Rectangle(0, 0, img1.Width, img1.Height),
ImageLockMode.ReadWrite, pixelFormat1)


Dim ptr1 As IntPtr = bmpData1.Scan0
Dim DataArray1(bmpData1.Stride * img1.Height - 1) As Byte
System.Runtime.InteropServices.Marshal.Copy(ptr1, DataArray1, 0, DataArray1.Length)

img1.UnlockBits(bmpData1)


img1.Save("D:\aaa1.png", ImageFormat.Png)




Dim pixelFormat0 As PixelFormat = img2.PixelFormat


Dim bmpData0 As BitmapData = img2.LockBits(New Rectangle(0, 0, img2.Width, img2.Height),
ImageLockMode.ReadWrite, pixelFormat0)


Dim ptr0 As IntPtr = bmpData0.Scan0
Dim DataArray(bmpData0.Stride * img2.Height - 1) As Byte
System.Runtime.InteropServices.Marshal.Copy(ptr0, DataArray, 0, DataArray.Length)

img2.UnlockBits(bmpData0)


img2.Save("D:\aaa2.png", ImageFormat.Png)

img2.Dispose()


引用返信 編集キー/
■93589 / inTopicNo.4)  Re[2]: 透明な領域の色を保持するには
□投稿者/ 魔界の仮面弁士 (2538回)-(2019/12/24(Tue) 11:39:38)
2019/12/24(Tue) 11:41:23 編集(投稿者)
No93583 (魔界の仮面弁士) に追記
> PC が手元に無いので未検証ですが、
> g.DrawImage の前に、
> g.CompositingMode を変更してみるのは
> どうでしょうか。

検証後のコード


Imports System.Drawing.Drawing2D
Imports System.Drawing.Imaging
Imports System.IO

Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim downloadList As New Dictionary(Of FileInfo, Uri)
        downloadList.Add(New FileInfo("C:\TEMP\透過0%.PNG"), New Uri("http://uno036.starfree.jp/PRGmanual/ImagePng/house2-24.png"))
        downloadList.Add(New FileInfo("C:\TEMP\透過50%.PNG"), New Uri("http://uno036.starfree.jp/PRGmanual/ImagePng/house2-24A128.png"))
        downloadList.Add(New FileInfo("C:\TEMP\透過100%.PNG"), New Uri("http://uno036.starfree.jp/PRGmanual/ImagePng/house2-24A064.png"))

        For Each kv In downloadList

            '画像のダウンロード
            Dim tmp As New FileInfo(Path.GetTempFileName)
            My.Computer.Network.DownloadFile(kv.Value, tmp.FullName, "", "", True, 200, True)

            '46x30 にリサイズ
            Using dst As New Bitmap(46, 30, PixelFormat.Format32bppArgb)
                Using g = Graphics.FromImage(dst), src As New Bitmap(tmp.FullName)
                    g.InterpolationMode = InterpolationMode.HighQualityBicubic

                    If ComboBox1.SelectedIndex = 0 Then
                        '案1:背景を無視した塗り潰しモード
                        g.CompositingMode = CompositingMode.SourceCopy
                    ElseIf ComboBox1.SelectedIndex = 1 Then
                        '案2:既定の重ね塗りモードのままだが、事前に背景をクリアしておく
                        g.Clear(Color.Transparent)
                        g.CompositingMode = CompositingMode.SourceOver
                    ElseIf ComboBox1.SelectedIndex = 2 Then
                        '背景が黒になるという事は、今はこの状態なのかな?
                        g.Clear(Color.Black)
                        g.CompositingMode = CompositingMode.SourceOver
                    Else
                        '何もしない場合
                    End If

                    g.DrawImage(src, 0, 0, 46, 30)
                End Using
                dst.Save(kv.Key.FullName, ImageFormat.Png)
            End Using
            tmp.Delete()
        Next
    End Sub
End Class

引用返信 編集キー/
■93597 / inTopicNo.5)  Re[3]: 透明な領域の色を保持するには
□投稿者/ ももも (3回)-(2019/12/24(Tue) 21:32:39)
やはり、うまくいきません。

αチャンネルを含んだPNG画像を読み込む(img1)。
img1をPNGファイルとして保存する→α=0における色情報は保存されている
img1をMarshal.Copyを使って、配列化する→α=0における色情報は保存されている

img1をリスケールしてimg2にコピーする
img2をPNGファイルとして保存する→α=0における色情報は保存されている
img2をMarshal.Copyを使って、配列化する→なぜか、ここでα=0における色情報は保存されていない。

→ img2を一旦PNGファイルとして保存した後に、再読み込みし、配列化する→やはりα=0における色情報は保存されていない。

PNGファイルとしては、色情報をもっているはずなのに
なぜか配列化すると色情報が失われてしまうのですが・・・
一体なぜでしょうか?


引用返信 編集キー/
■93599 / inTopicNo.6)  Re[4]: 透明な領域の色を保持するには
□投稿者/ 魔界の仮面弁士 (2539回)-(2019/12/25(Wed) 10:49:26)
No93597 (ももも さん) に返信
> やはり、うまくいきません。

当方では取得できているようなのですが、下記のコードだとどうなりますか?


Imports System.Drawing.Drawing2D
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ComboBox1.Items.Add("全ての画素")
        ComboBox1.Items.Add("Alpha = 0 の画素")
        ComboBox1.Items.Add("Alpha = 127 の画素")
        ComboBox1.Items.Add("Alpha = 255 の画素")
        ComboBox1.Items.Add("0 < Alpha < 255 の画素")
        ComboBox1.SelectedIndex = 4
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ' http://uno036.starfree.jp/PRGmanual/ImagePng/house2-24A128.png
        Dim srcFile As String = "C:\TEMP\透過50%.PNG"
        Using srcImg As New Bitmap(srcFile)
            Dim sz As New Size(46, 30)
            Using dstImg As New Bitmap(sz.Height, sz.Width, PixelFormat.Format32bppArgb), g = Graphics.FromImage(dstImg)
                g.InterpolationMode = InterpolationMode.HighQualityBicubic
                g.DrawImage(srcImg, 0, 0, sz.Height, sz.Width)

                Label1.Text = String.Format("{0}, {1}", srcImg.Size, srcImg.PixelFormat)
                Label2.Text = String.Format("{0}, {1}", dstImg.Size, srcImg.PixelFormat)

                Dim tbl1 = DumpBmp(srcImg)
                Dim tbl2 = DumpBmp(dstImg)
                Select Case ComboBox1.SelectedIndex
                    Case 1
                        DataGridView1.DataSource = tbl1.AsEnumerable().Where(Function(r) r.Field(Of Color)("Color").A = 0).AsDataView()
                        DataGridView2.DataSource = tbl2.AsEnumerable().Where(Function(r) r.Field(Of Color)("Color").A = 0).AsDataView()
                    Case 2
                        DataGridView1.DataSource = tbl1.AsEnumerable().Where(Function(r) r.Field(Of Color)("Color").A = 127).AsDataView()
                        DataGridView2.DataSource = tbl2.AsEnumerable().Where(Function(r) r.Field(Of Color)("Color").A = 127).AsDataView()
                    Case 3
                        DataGridView1.DataSource = tbl1.AsEnumerable().Where(Function(r) r.Field(Of Color)("Color").A = 255).AsDataView()
                        DataGridView2.DataSource = tbl2.AsEnumerable().Where(Function(r) r.Field(Of Color)("Color").A = 255).AsDataView()
                    Case 4
                        Dim ary() As Byte = {0, 255}
                        DataGridView1.DataSource = tbl1.AsEnumerable().Where(Function(r) Not ary.Contains(r.Field(Of Color)("Color").A)).AsDataView()
                        DataGridView2.DataSource = tbl2.AsEnumerable().Where(Function(r) Not ary.Contains(r.Field(Of Color)("Color").A)).AsDataView()
                    Case Else
                        DataGridView1.DataSource = tbl1
                        DataGridView2.DataSource = tbl2
                End Select
                DataGridView1.AutoResizeColumn(0)
                DataGridView1.AutoResizeColumn(1)
                DataGridView2.AutoResizeColumn(0)
                DataGridView2.AutoResizeColumn(1)
            End Using
        End Using
    End Sub

    'Private Function DumpBmp(bmp As Bitmap) As DataTable
    '    Dim dt As New DataTable()
    '    Dim colX = dt.Columns.Add("X", GetType(Integer))
    '    Dim colY = dt.Columns.Add("Y", GetType(Integer))
    '    dt.Columns.Add("Color", GetType(Color)).AllowDBNull = False
    '    dt.PrimaryKey = New DataColumn() {colX, colY}
    '    '画素ごとに GetPixel を読み取って解析
    '    For y = 0 To bmp.Height - 1
    '        For x = 0 To bmp.Width - 1
    '            dt.Rows.Add(x, y, bmp.GetPixel(x, y))
    '        Next
    '    Next
    '    Return dt
    'End Function

    Private Function DumpBmp(bmp As Bitmap) As DataTable
        Dim dt As New DataTable()
        Dim colX = dt.Columns.Add("X", GetType(Integer))
        Dim colY = dt.Columns.Add("Y", GetType(Integer))
        dt.Columns.Add("Color", GetType(Color)).AllowDBNull = False
        dt.PrimaryKey = New DataColumn() {colX, colY}
        Dim bmpData As BitmapData = bmp.LockBits(New Rectangle(Point.Empty, bmp.Size), ImageLockMode.ReadOnly, bmp.PixelFormat)
        'まとめて Byte() に読み取ってから解析
        Dim bin(bmpData.Stride * bmp.Height - 1) As Byte
        Marshal.Copy(bmpData.Scan0, bin, 0, bin.Length)
        For y = 0 To bmpData.Height - 1
            For x = 0 To bmpData.Width - 1
                Dim p As Integer = y * bmpData.Stride + x * 4
                dt.Rows.Add(x, y, Color.FromArgb(bin(p + 3), bin(p + 2), bin(p + 1), bin(p + 0)))
            Next
        Next
        bmp.UnlockBits(bmpData)
        Return dt
    End Function

    'Private Function DumpBmp(bmp As Bitmap) As DataTable
    '    Dim dt As New DataTable()
    '    Dim colX = dt.Columns.Add("X", GetType(Integer))
    '    Dim colY = dt.Columns.Add("Y", GetType(Integer))
    '    dt.Columns.Add("Color", GetType(Color)).AllowDBNull = False
    '    dt.PrimaryKey = New DataColumn() {colX, colY}
    '    Dim bmpData As BitmapData = bmp.LockBits(New Rectangle(Point.Empty, bmp.Size), ImageLockMode.ReadOnly, bmp.PixelFormat)
    '    '画素ごとに Scan0 のオフセットを読み取って解析
    '    For y = 0 To bmpData.Height - 1
    '        For x = 0 To bmpData.Width - 1
    '            Dim p As Integer = y * bmpData.Stride + x * 4
    '            dt.Rows.Add(x, y, Color.FromArgb(Marshal.ReadInt32(bmpData.Scan0, p)))
    '        Next
    '    Next
    '    bmp.UnlockBits(bmpData)
    '    Return dt
    'End Function
End Class

引用返信 編集キー/
■93600 / inTopicNo.7)  Re[4]: 透明な領域の色を保持するには
□投稿者/ Hongliang (944回)-(2019/12/25(Wed) 10:51:11)
2019/12/25(Wed) 10:53:06 編集(投稿者)

ARGB=(0,r,g,b)
のピクセルについて、リサイズしたりするとr,g,bの値の如何に関わらず
ARGB=(0,0,0,0)
になってしまう、という話ですね?

Graphicsを使う限りは仕様ということであきらめるしかないかと。
どうしても維持したいのならBitmapDataのピクセルデータをもとに自前で計算するか、あるいはほかの画像処理ライブラリを使うしかないでしょう。

一応System.Windows.Media.Imagingでも試してみましたが、
・TransformedBitmap
・ImageDrawingを使ってRenderTargetBitmap
いずれもARGB=(0,0,0,0)に丸められるみたいなので、標準ライブラリでは対応できなさそうです。
引用返信 編集キー/
■93604 / inTopicNo.8)  Re[5]: 透明な領域の色を保持するには
□投稿者/ ももも (4回)-(2019/12/25(Wed) 14:30:46)
No93599 (魔界の仮面弁士 さん) に返信

提示くださったコードを試してみましたが
やはりRGB=0になってしまいます。

アップしてくださった画像はDLできなかったため
自分で作成した以下の画像を使用しました。
RGB=255でアルファ=0の画像です

http://whitecats.dip.jp/up/download/1577251674/attach/1577251674.png
pass 1234

あと、


ComboBox1.SelectedIndex = 1

で試して、そちらではリサイズ後にRGB=255が再現できているのでしょうか?



引用返信 編集キー/

このトピックをツリーで一括表示


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

このトピックに書きこむ