-1

I've tried several examples from here but have not found anything that works.

I need to be able to convert a color image to black and white so that I can take that data and send it to a thermal printer.

Getting the image from color to black and white seems to be the trouble as I've found no methods in the C# libraries.

The image I'm testing with is a PixelFormat.Format32bppArgb and I believe I want to covert that down to PixelFormat.Format1bppIndexed

EDIT: Not sure how I can make it any clearer. I don't want greyscale I want black and white which is what "PixelFormst.Format1bppIndexed"

Chris Ward
  • 771
  • 1
  • 9
  • 23
  • possible duplicate of [convert image to Black-White or Sepia in c#](http://stackoverflow.com/questions/4624998/convert-image-to-black-white-or-sepia-in-c-sharp) – scrappedcola Jun 30 '15 at 21:19
  • Converting an image to 1bpp invariably produces *very* disappointing results. You can see what it looks like with [this code](http://stackoverflow.com/a/273686/17034). Use a decent painting program to do this, you'll get an algorithm like Floyd-Steinberg for free. – Hans Passant Jun 30 '15 at 21:19
  • Look into a colorMatrix and dithering – TaW Jun 30 '15 at 21:19
  • In very crude terms, take the average of the R, G and B values for each pixel and set the R, G, and B values to that average. That will give you a greyscale image. It won't be ideal, as the eye is less sensitive to blue than red and green, but that simple approach will likely work for a thermal printer. – David Arno Jun 30 '15 at 21:19
  • 1
    Where did I say I wanted Grey Scale? I said BLACK & WHITE. Also I'm not doing Photographs mostly logos so yes I know greyscale would be nice but thermal printers don't print greyscale. – Chris Ward Jul 01 '15 at 00:54
  • Hans Passant I actually already tried that code and it didn't work it just made some image that was unrecongnizable. – Chris Ward Jul 01 '15 at 01:04
  • MSPaint seems to do an excellent job on images I've tried. Paint Shop Pro does an ok job I liked the results from MS Paint better, just need to figure out how to do the same in C# – Chris Ward Jul 01 '15 at 01:11
  • Not sure why this is getting so many downvotes... indexed images are still an interesting and useful tool. @ChrisWard did my answer help you? – Nyerguds Jan 26 '18 at 00:26

6 Answers6

2

You can do it with ImageMagick which is installed on most Linux distros and is available for free for OSX (ideally via homebrew) and also for Windows from here.

If you start with this smooth greyscale ramp:

enter image description here

At the command line, you can use this for Floyd-Steinberg dithering:

convert grey.png -dither FloydSteinberg -monochrome fs.bmp

enter image description here

or, this for Riemersma dithering:

convert grey.png -dither Riemersma -monochrome  riem.bmp

enter image description here

The Ordered Dither that Glenn was referring to is available like this with differing tile options:

convert grey.png -ordered-dither o8x8  -monochrome  od8.bmp

enter image description here

convert grey.png -ordered-dither o2x2  -monochrome  od2.bmp

enter image description here

A check of the format shows it is 1bpp with a 2-colour palette:

identify -verbose riem.bmp

Image: riem.bmp
  Format: BMP (Microsoft Windows bitmap image)
  Class: PseudoClass
  Geometry: 262x86+0+0
  Units: PixelsPerCentimeter
  Type: Bilevel
  Base type: Bilevel                     <--- 1 bpp
  Endianess: Undefined
  Colorspace: Gray
  Depth: 1-bit                           <--- 1 bpp
  Channel depth:
    gray: 1-bit
  Channel statistics:
    Pixels: 22532
    Gray:
      min: 0 (0)
      max: 1 (1)
      mean: 0.470486 (0.470486)
      standard deviation: 0.499128 (0.499128)
      kurtosis: -1.98601
      skewness: 0.118261
      entropy: 0.997485
  Colors: 2
  Histogram:
     11931: (  0,  0,  0) #000000 gray(0)
     10601: (255,255,255) #FFFFFF gray(255)
  Colormap entries: 2
  Colormap:
         0: (  0,  0,  0) #000000 gray(0)        <--- colourmap has only black...
         1: (255,255,255) #FFFFFF gray(255)      <--- ... and white

If you start with a colour image like this:

enter image description here

and process like this:

convert colour.png -ordered-dither o8x8  -monochrome  od8.bmp

you will get this

enter image description here

As Glenn says, there are C# bindings for ImageMagick - or you can just use the above commands in a batch file, or use the C# equivalent of the system() call to execute the above ImageMagick commands.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
1

Imagemagick can convert images to black-and-white by various methods including dithering (-dither) and ordered-dithering (-ordered-dither). I generally use the command-line interface, but there is a C# binding called magick.net that you might try. See magick.codeplex.com.

For some examples, see this Q&A at codegolf.stackexchange.com

Community
  • 1
  • 1
Glenn Randers-Pehrson
  • 11,940
  • 3
  • 37
  • 61
1

enter image description here

You can use this codes for converting image to black and white.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Converting_Image_to__Black_and_White
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    public static int siyahbeyazsinirnoktasi=0;
    public static string DosyaYolu = "";

    #region resim üzerinde işlemler yapma (siyah beyaz)

    Bitmap BlackandWhite(Bitmap Goruntu)

    {


        Bitmap yeniGoruntu = new Bitmap(Goruntu.Width, 
     Goruntu.Height);//Bitmap sınıfımızı oluşturduk.



        double toplampikselsayisi = Goruntu.Width * Goruntu.Height;
        int GriTonlama;



        for (int i = 0; i < Goruntu.Width; i++)//resmi yatay olarak 
taramak için

        {

            for (int j = 0; j < Goruntu.Height; j++)//resmi dikey olarak 
taramak için

            {

                Color Pixel = Goruntu.GetPixel(i, j);//color sınıfını ile 
pixel rengini alıyoruz.

                GriTonlama = (Pixel.R + Pixel.G + Pixel.B) / 3;//almış 
olduğumuz renk değerini gri tona çevirmek için kullanmamız gereken 
formül.

                if (GriTonlama < siyahbeyazsinirnoktasi)
                {

                    yeniGoruntu.SetPixel(i, j, Color.FromArgb(0, 0, 
0));//yeni görüntümüze gri tonlamadaki pixel değerini veriyoruz.
                }

                if (GriTonlama >= siyahbeyazsinirnoktasi)
                {

                    yeniGoruntu.SetPixel(i, j, Color.FromArgb(255, 255, 
255));//yeni görüntümüze gri tonlamadaki pixel değerini veriyoruz.
                }


            }


        }


        return yeniGoruntu;
    }
    #endregion


    private void btnLoadImage_Click(object sender, EventArgs e)
    {



        FolderBrowserDialog Klasor = new FolderBrowserDialog();
        openFileDialog1.Title = "Resimdosyası seçiniz.";
        openFileDialog1.Filter = "Image files (*.jpg)|*.jpg|Tüm 
dosyalar(*.*)|*.*";
        if (openFileDialog1.ShowDialog() == DialogResult.OK)
        {
             DosyaYolu = openFileDialog1.FileName;
            var dosyaboyutu = new FileInfo(DosyaYolu).Length;


            if (dosyaboyutu <= 500000)
            {
                pictureBox1.Image = new 
Bitmap(openFileDialog1.OpenFile());
                btnConvertBlackandWhite.Enabled = true;
                label1.Visible = true;
                label2.Visible= true;
                label3.Visible = true;
                label4.Visible = true;
                label5.Visible = true;
                label6.Visible = true;
                trackBar1.Visible = true;

            }

            else
            {
                MessageBox.Show("Seçtiğiniz resim boyutu 500 KB'nın 
altında olmalıdır.");
            }

        }

    }

    private void btnConvertBlackandWhite_Click(object sender, EventArgs 
 e)
    {
        pictureBox1.Image = BlackandWhite(new Bitmap(DosyaYolu));
        btnSave.Enabled = true;

    }

    private void trackBar1_Scroll(object sender, EventArgs e)
    {
        siyahbeyazsinirnoktasi = trackBar1.Value ;
        label3.Text = Convert.ToString(siyahbeyazsinirnoktasi);
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        label3.Text = "130";
        siyahbeyazsinirnoktasi = 130;
    }

    private void btnSave_Click(object sender, EventArgs e)
    {
        {

            Image pngoptikform = new Bitmap(pictureBox1.Image);
            SaveFileDialog sf = new SaveFileDialog();//yeni bir kaydetme 
diyaloğu oluşturuyoruz.

            sf.Filter = "Image file (Jpg dosyası (*.jpg)|*.jpg  ";//.bmp 
veya .jpg olarak kayıt imkanı sağlıyoruz.

            sf.Title = "Kayıt";//diğaloğumuzun başlığını belirliyoruz.

            sf.CheckPathExists = true;
            sf.DefaultExt = "jpg";
            sf.FilterIndex = 1;


            DialogResult sonuc = sf.ShowDialog();

            if (sonuc == DialogResult.OK)
            {

                if (sf.FilterIndex == 1)
                {
                    pngoptikform.Save(sf.FileName);
                    System.Diagnostics.Process.Start(sf.FileName);
                }


            }

        }
    }

}
}
0

I believe you can get fairly good results with a decent dithering algorithm. There's a similar post here, please see if it helps you:

Converting a bitmap to monochrome

Community
  • 1
  • 1
0

I'd personally do this in two steps:

  • Convert the image to an 8-bit image with a palette of two colours.
  • Convert the 8-bit image to a 1-bit image.

The first step I already explained before, in this answer. (You may want to use more detailed dithering methods as described in the other answers here, but even then the answer is needed for its methods to convert and manipulate images as byte arrays.)

The basic method detailed there is:

  • Paint the image on a new 32bpp ARGB image so you have a predictable four-byte-per-pixel data structure.
  • Extract the image's bytes.
  • Make a colour out of every block of 4 bytes, match that to the closest match on a given palette, and store the result of that matching in a byte array (with exactly width * height bytes).
  • Make a new image using the 8-bit data array and the used palette.

That'll give you your picture converted to black and white 8-bit image. Now, all we need to do to get a 1-bit image is put a new step before the last, where we compact the 8-bit data to 1-bit data, and then do the final call to the BuildImage function with PixelFormat.Format1bppIndexed instead.

Here is the function to reduce the image to a lower bits length. It requires the original image data and stride, and will return the converted image data and the new stride.

Note, I'm not sure what the bits order inside the data bytes is for normal dotNet 1-bit images, since I only used this function to convert custom game file formats, so you'll just need to test it out to see what you need to give in the bigEndian parameter. If you give the wrong value, each column of 8 pixels will be left-right mirrored, so it should be obvious in the result.

/// <summary>
/// Converts given raw image data for a paletted 8-bit image to lower amount of bits per pixel.
/// </summary>
/// <param name="data8bit">The eight bit per pixel image data</param>
/// <param name="width">The width of the image</param>
/// <param name="height">The height of the image</param>
/// <param name="bitsLength">The new amount of bits per pixel</param>
/// <param name="bigEndian">True if the bits in the new image data are to be stored as big-endian.</param>
/// <param name="stride">Stride used in the original image data. Will be adjusted to the new stride value.</param>
/// <returns>The image data converted to the requested amount of bits per pixel.</returns>
public static Byte[] ConvertFrom8Bit(Byte[] data8bit, Int32 width, Int32 height, Int32 bitsLength, Boolean bigEndian, ref Int32 stride)
{
    Int32 parts = 8 / bitsLength;
    // Amount of bytes to write per width. This rounds the bits up to the nearest byte.
    Int32 newStride = ((bitsLength * width) + 7) / 8;
    // Bit mask for reducing original data to actual bits maximum.
    // Should not be needed if data is correct, but eh.
    Int32 bitmask = (1 << bitsLength) - 1;
    Byte[] dataXbit = new Byte[newStride * height];
    // Actual conversion porcess.
    for (Int32 y = 0; y < height; y++)
    {
        for (Int32 x = 0; x < width; x++)
        {
            // This will hit the same byte multiple times
            Int32 indexXbit = y * newStride + x / parts;
            // This will always get a new index
            Int32 index8bit = y * stride + x;
            // Amount of bits to shift the data to get to the current pixel data
            Int32 shift = (x % parts) * bitsLength;
            // Reversed for big-endian
            if (bigEndian)
                shift = 8 - shift - bitsLength;
            // Get data, reduce to bit rate, shift it and store it.
            dataXbit[indexXbit] |= (Byte)((data8bit[index8bit] & bitmask) << shift);
        }
    }
    stride = newStride;
    return dataXbit;
}
Nyerguds
  • 5,360
  • 1
  • 31
  • 63
-1

The term for showing colors is called Gray Scale:

A quick search for: color to gray scale converter c# yielded the following web site: http://www.codeproject.com/Questions/315939/How-To-Convert-Grayscale-Image-to-Color-Image-in-c

Steve
  • 623
  • 4
  • 11