Flutter / Dart : convert image to 1 bit black and white

2,569

Iterate over the 576 grey-scale bytes, comparing each to the threshold and pack those bits into bytes (or, more conveniently, ints).

Here's an example using the helper function from package:raw, but you could just inline that as it's relatively simple.

  Uint8List img24x24 = Uint8List(24 * 24); // input 24x24 greyscale bytes [0-255]
  Uint32List img24 = Uint32List(24); // output 24 packed int with 24 b/w bits each
  final threshold = 127; // set the greyscale -> b/w threshold here
  for (var i = 0; i < 24; i++) {
    for (var j = 0; j < 24; j++) {
      img24[i] = transformUint32Bool(
        img24[i],
        24 - j,
        img24x24[i * 24 + j] > threshold, // or < threshold to do the invert in one step
      );
    }
  }
Share:
2,569
Jean DuPont
Author by

Jean DuPont

Updated on December 13, 2022

Comments

  • Jean DuPont
    Jean DuPont over 1 year

    I'm writing a code to print an image using ESC * command (using an ESC POS thermal receipts printer).

    Basically, I'm trying to adapt Python Algorithm for Dart/Flutter. It sounds easy : open image -> grayscale -> invert colors -> convert to b&w 1 bit:

    im = Image.open(filename)
    im = im.convert("L")  # Invert: Only works on 'L' images
    im = ImageOps.invert(im)  # Bits are sent with 0 = white, 1 = black in ESC/POS
    
    print(len(im.tobytes())) # len = 576 (image size: 24*24)
    im = im.convert("1")  # Pure black and white
    print(len(im.tobytes())) # leng = 72 (image size: 24*24)
    ...
    

    I only have a problem with the last step (1 bit conversion).

    As you can see, Python code (Pillow library) will decrease the number of bytes after im.convert("1") command and it's exactly what I need to correctly generate a ESC/POS command. Each value is between 0 and 255.

    How to achieve it using Dart?

    Here is my code:

    import 'package:image/image.dart';
    
    const String filename = './test_24x24.png';
    final Image image = decodeImage(File(filename).readAsBytesSync());
    
    grayscale(image);
    invert(image);
    
    

    Source image: 24px * 24px

    At the end I have a grey/inverted image containing (24 * 24 * 3) bytes in RGB mode. All the r/g/b values are equal because of the greyscaling, so I can keep only one channel which gives me (24 * 24) bytes.

    How can I achieve the last step im.convert("1") and keep only 24 * 3 bytes?