如何把彩色图片转换为单色图片-GDI+

 

http://bobpowell.net/onebit.htm

Converting an RGB image to 1 bit-per-pixel monochrome.

 

One bit per pixel images are essentially an indexed format image having two entries in the colour palette. Unlike a GIF or other 256 colour indexed format which uses a single byte to access one of up-to 256 colours in the palette, the one bit per pixel format uses only a single bit to select colour A or colour B from the palette.

Often used in Fax formats, one bit per pixel images are very compact with one byte of information representing eight pixels.

Very often, a more complex RGB image can be used for the basic raw-image that is used to define the 1 bpp array but this operation in GDI+ and .NET systems is a little more complex than one would hope because you cannot simply get the Graphics object for an indexed image and draw on it. In order to do this conversion, you must manipulate the individual bits of the image by locking it's byte array and selecting the correct bit for the desired pixel.

The LockBits method can be used to obtain a BitmapData object which contains the address of the bitmap in memory, the dimensions and particularly, the stride of the image. All images are stored in an array that fills memory to a four-byte boundary so the stride is used to calculate where each successive line begins in the array. A specific line may be addressed by the formula LineAddress=stride*linenumber and NOT LineAddress = pixeldepth*imagewidth*linenumber as many people assume.

For our single bit per pixel image, the pixel indexing is further complicated by the need to select a specific bit in a byte that contains up to 8 pixels (it may have less than 8 pixels because it may be on the end of a scan-line). To perform this selection, a bit mask is used and rotated into position so that the correct bit may be set or reset as desired.

The simple application shown in listing 1 enables you to load any image into a PictureBox control situated on the left of the form. Once loaded, the image is scanned pixel by pixel to check the brightness of the colours contained in the image. If a pixel is darker than 50% brightness, a black pixel is set in the corresponding pixel of a black and white, single bit per pixel image stored in the rightmost PictureBox on the form. Between the two PictureBox controls, a Splitter enables you to see more or less of the original image for comparison with the converted one.

Note in particular how the single bit per pixel image is created in the pictureBox1_Click handler and how the SetIndexedPixel method is used to access the individual pixel bits in the monochrome image.

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Drawing.Imaging;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using System.Runtime.InteropServices;

namespace _1bitmap

{

  ///<summary>

  /// Summary description for Form1.

  ///</summary>

  public class Form1 : System.Windows.Forms.Form

  {

    ///<summary>

    /// Required designer variable.

    ///</summary>

    private System.ComponentModel.Container components =null;

    public Form1()

    {

      //

      // Required for Windows Form Designer support

      //

      InitializeComponent();

      //

      // TODO: Add any constructor code after InitializeComponent call

      //

      SetStyle(ControlStyles.ResizeRedraw,true);

    }

    ///<summary>

    /// Clean up any resources being used.

    ///</summary>

    protected overridevoid Dispose( bool disposing )

    {

      if( disposing )

      {

        if (components != null)

        {

          components.Dispose();

        }

      }

      base.Dispose( disposing );

    }

    #region Windows Form Designer generated code

    ///<summary>

    /// Required method for Designer support - do not modify

    /// the contents of this method with the code editor.

    ///</summary>

    private void InitializeComponent()

    {

      this.pictureBox1 = new System.Windows.Forms.PictureBox();

      this.splitter1 = new System.Windows.Forms.Splitter();

      this.pictureBox2 = new System.Windows.Forms.PictureBox();

      this.SuspendLayout();

      //

      // pictureBox1

      //

      this.pictureBox1.BackColor = System.Drawing.Color.White;

      this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Left;

      this.pictureBox1.Name = "pictureBox1";

      this.pictureBox1.Size = new System.Drawing.Size(100, 397);

      this.pictureBox1.TabIndex = 0;

      this.pictureBox1.TabStop = false;

      this.pictureBox1.Click += new System.EventHandler(this.pictureBox1_Click);

      //

      // splitter1

      //

      this.splitter1.Location = new System.Drawing.Point(100, 0);

      this.splitter1.Name = "splitter1";

      this.splitter1.Size = new System.Drawing.Size(3, 397);

      this.splitter1.TabIndex = 1;

      this.splitter1.TabStop = false;

      //

      // pictureBox2

      //

      this.pictureBox2.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(128)), ((System.Byte)(255)), ((System.Byte)(255)));

      this.pictureBox2.Dock = System.Windows.Forms.DockStyle.Fill;

      this.pictureBox2.Location = new System.Drawing.Point(103, 0);

      this.pictureBox2.Name = "pictureBox2";

      this.pictureBox2.Size = new System.Drawing.Size(473, 397);

      this.pictureBox2.TabIndex = 2;

      this.pictureBox2.TabStop = false;

      //

      // Form1

      //

      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

      this.ClientSize = new System.Drawing.Size(576, 397);

      this.Controls.AddRange(new System.Windows.Forms.Control[] {

                                      this.pictureBox2,

                                      this.splitter1,

                                      this.pictureBox1});

      this.Name = "Form1";

      this.Text = "Form1";

      this.Load += new System.EventHandler(this.Form1_Load);

      this.ResumeLayout(false);

    }

    #endregion

    ///<summary>

    /// The main entry point for the application.

    ///</summary>

    [STAThread]

    static void Main()

    {

      Application.Run(new Form1());

    }

    protected void SetIndexedPixel(int x,int y,BitmapData bmd,bool pixel)

    {

      int index=y*bmd.Stride+(x>>3);

      byte p=Marshal.ReadByte(bmd.Scan0,index);

      byte mask=(byte)(0x80>>(x&0x7));

      if(pixel)

        p |=mask;

      else

        p &=(byte)(mask^0xff);

      Marshal.WriteByte(bmd.Scan0,index,p);

    }

    private System.Windows.Forms.PictureBox pictureBox1;

    private System.Windows.Forms.Splitter splitter1;

    private System.Windows.Forms.PictureBox pictureBox2;

    Bitmap bm;

   

    private void Form1_Load(object sender, System.EventArgs e)

    {

    }

    private void pictureBox1_Click(object sender, System.EventArgs e)

    {

      //Load a file

      OpenFileDialog dlg = new OpenFileDialog();

      dlg.Filter="Image files|*.bmp;*.gif;*.jpg";

      if(dlg.ShowDialog()==DialogResult.OK)

      {

        Bitmap img = (Bitmap)Image.FromFile(dlg.FileName);

        //Ensure that it's a 32 bit per pixel file

        if(img.PixelFormat!=PixelFormat.Format32bppPArgb)

        {

          Bitmap temp=new Bitmap(img.Width,img.Height,PixelFormat.Format32bppPArgb);

          Graphics g=Graphics.FromImage(temp);

          g.DrawImage(img, new Rectangle(0,0,img.Width,img.Height),0,0,img.Width,img.Height,GraphicsUnit.Pixel);

          img.Dispose();

          g.Dispose();

          img=temp;

        }

        this.pictureBox1.Image=img;

        //lock the bits of the original bitmap

        BitmapData bmdo=img.LockBits(new Rectangle(0,0,img.Width,img.Height),ImageLockMode.ReadOnly,img.PixelFormat);

        //and the new 1bpp bitmap

        bm=new Bitmap(this.pictureBox1.Image.Width,this.pictureBox1.Image.Height,PixelFormat.Format1bppIndexed);

        BitmapData bmdn=bm.LockBits(new Rectangle(0,0,bm.Width,bm.Height),ImageLockMode.ReadWrite,PixelFormat.Format1bppIndexed);

        //for diagnostics

        DateTime dt=DateTime.Now;

       

        //scan through the pixels Y by X

        int x,y;

        for(y=0;y<img.Height;y++)

        {

          for(x=0;x<img.Width;x++)

          {

            //generate the address of the colour pixel

            int index=y*bmdo.Stride+(x*4);

            //check its brightness

            if(Color.FromArgb(Marshal.ReadByte(bmdo.Scan0,index+2),

                            Marshal.ReadByte(bmdo.Scan0,index+1),

                            Marshal.ReadByte(bmdo.Scan0,index)).GetBrightness()>0.5f)

              this.SetIndexedPixel(x,y,bmdn,true);//set it if its bright.

          }

        }

        //tidy up

        bm.UnlockBits(bmdn);

        img.UnlockBits(bmdo);

        //show the time taken to do the conversion

        TimeSpan ts=dt-DateTime.Now;

        System.Diagnostics.Trace.WriteLine("Conversion time was:"+ts.ToString());

        //display the 1bpp image.

        this.pictureBox2.Image=bm;

      }

    }

  }

}

Imports System

Imports System.Drawing

Imports System.Drawing.Drawing2D

Imports System.Drawing.Imaging

Imports System.Collections

Imports System.ComponentModel

Imports System.Windows.Forms

Imports System.Data

Imports System.Runtime.InteropServices

Namespace _1bitmap

   '/ <summary>

   '/ Summary description for Form1.

   '/ </summary>

  

   Public Class Form1

      Inherits System.Windows.Forms.Form

      '/ <summary>

      '/ Required designer variable.

      '/ </summary>

      Private components As System.ComponentModel.Container = Nothing

     

     

      Public SubNew()

         '

         ' Required for Windows Form Designer support

         '

         InitializeComponent()

        

         '

         ' TODO: Add any constructor code after InitializeComponent call

         '

         SetStyle(ControlStyles.ResizeRedraw, True)

      End Sub 'New

     

     

      '/ <summary>

      '/ Clean up any resources being used.

      '/ </summary>

        Protected OverloadsOverrides Sub Dispose(ByVal disposingAs Boolean)

            If disposing Then

                If Not (componentsIs Nothing) Then

                    components.Dispose()

                End If

            End If

            MyBase.Dispose(disposing)

        End Sub'Dispose

#Region "Windows Form Designer generated code"

        '/ <summary>

        '/ Required method for Designer support - do not modify

        '/ the contents of this method with the code editor.

        '/ </summary>

        Private Sub InitializeComponent()

            Me.pictureBox1 = New System.Windows.Forms.PictureBox

            Me.splitter1 = New System.Windows.Forms.Splitter

            Me.pictureBox2 = New System.Windows.Forms.PictureBox

            Me.SuspendLayout()

            '

            ' pictureBox1

            '

            Me.pictureBox1.BackColor = System.Drawing.Color.White

            Me.pictureBox1.Dock = System.Windows.Forms.DockStyle.Left

            Me.pictureBox1.Name = "pictureBox1"

            Me.pictureBox1.Size = New System.Drawing.Size(100, 397)

            Me.pictureBox1.TabIndex = 0

            Me.pictureBox1.TabStop = False

            '

            ' splitter1

            '

            Me.splitter1.Location = New System.Drawing.Point(100, 0)

            Me.splitter1.Name = "splitter1"

            Me.splitter1.Size = New System.Drawing.Size(3, 397)

            Me.splitter1.TabIndex = 1

            Me.splitter1.TabStop = False

            '

            ' pictureBox2

            '

            Me.pictureBox2.BackColor = System.Drawing.Color.FromArgb(CType(128, System.Byte),CType(255, System.Byte), CType(255, System.Byte))

            Me.pictureBox2.Dock = System.Windows.Forms.DockStyle.Fill

            Me.pictureBox2.Location = New System.Drawing.Point(103, 0)

            Me.pictureBox2.Name = "pictureBox2"

            Me.pictureBox2.Size = New System.Drawing.Size(473, 397)

            Me.pictureBox2.TabIndex = 2

            Me.pictureBox2.TabStop = False

            '

            ' Form1

            '

            Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)

            Me.ClientSize = New System.Drawing.Size(576, 397)

            Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.pictureBox2,Me.splitter1, Me.pictureBox1})

            Me.Name = "Form1"

            Me.Text = "Form1"

            Me.ResumeLayout(False)

        End Sub'InitializeComponent

#End Region

        '/ <summary>

        '/ The main entry point for the application.

        '/ </summary>

        <STAThread()> _

        Shared Sub Main()

            Application.Run(New Form1)

        End Sub'Main

        Protected Sub SetIndexedPixel(ByVal xAs Integer, ByVal y As Integer,ByVal bmd As BitmapData,ByVal pixel As Boolean)

            Dim index As Integer = y * bmd.Stride + (x >> 3)

            Dim p AsByte = Marshal.ReadByte(bmd.Scan0, index)

            Dim mask As Byte = &H80 >> (x And &H7)

            If pixel Then

                p = p Or mask

            Else

                p = p And CByte(mask ^ &HFF)

            End If

            Marshal.WriteByte(bmd.Scan0, index, p)

        End Sub'SetIndexedPixel

        Private WithEvents pictureBox1As System.Windows.Forms.PictureBox

        Private splitter1 As System.Windows.Forms.Splitter

        Private pictureBox2 As System.Windows.Forms.PictureBox

        Private bm As Bitmap

        Private Sub Form1_Load(ByVal senderAs Object, ByVal e As System.EventArgs) Handles MyBase.Load

        End Sub'Form1_Load

        Private Sub pictureBox1_Click(ByVal senderAs Object, ByVal e As System.EventArgs) Handles pictureBox1.Click

            'Load a file

            Dim dlg AsNew OpenFileDialog

            dlg.Filter = "Image files|*.bmp;*.gif;*.jpg"

            If dlg.ShowDialog() = DialogResult.OKThen

                Dim img As Bitmap = CType(Image.FromFile(dlg.FileName), Bitmap)

                'Ensure that it's a 32 bit per pixel file

                If img.PixelFormat <> PixelFormat.Format32bppPArgbThen

                    Dim temp As New Bitmap(img.Width, img.Height, PixelFormat.Format32bppPArgb)

                    Dim g As Graphics = Graphics.FromImage(temp)

                    g.DrawImage(img, New Rectangle(0, 0, img.Width, img.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel)

                    img.Dispose()

                    g.Dispose()

                    img = temp

                End If

                Me.pictureBox1.Image = img

                'lock the bits of the original bitmap

                Dim bmdo As BitmapData = img.LockBits(New Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, img.PixelFormat)

                'and the new 1bpp bitmap

                bm = New Bitmap(Me.pictureBox1.Image.Width,Me.pictureBox1.Image.Height, PixelFormat.Format1bppIndexed)

                Dim bmdn As BitmapData = bm.LockBits(New Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed)

                'for diagnostics

                Dim dt As DateTime = DateTime.Now

                'scan through the pixels Y by X

                Dim y As Integer

                For y = 0 To img.Height - 1

                    Dim x As Integer

                    For x = 0 To img.Width - 1

                        'generate the address of the colour pixel

                        Dim index As Integer = y * bmdo.Stride + x * 4

                        'check its brightness

                        If Color.FromArgb(Marshal.ReadByte(bmdo.Scan0, index + 2), Marshal.ReadByte(bmdo.Scan0, index + 1), Marshal.ReadByte(bmdo.Scan0, index)).GetBrightness() > 0.5FThen

                            Me.SetIndexedPixel(x, y, bmdn,True) 'set it if its bright.

                        End If

                    Next x

                Next y

                'tidy up

                bm.UnlockBits(bmdn)

                img.UnlockBits(bmdo)

                'show the time taken to do the conversion

                Dim ts As TimeSpan = dt.Subtract(DateTime.Now)

                System.Diagnostics.Trace.WriteLine(("Conversion time was:" + ts.ToString()))

                'display the 1bpp image.

                Me.pictureBox2.Image = bm

            End If

        End Sub'pictureBox1_Click

    End Class 'Form1

End Namespace '_1bitmap

Figure 1 shows the application in action.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值