5

I'm capturing fingerprints using a device called Secugen Pro 20, it has its own SDK for Linux, and i want to capture the fingerprint image and save it as any image format.

They have this typedef unsigned char BYTE;

I declared my imageBuffer

BYTE *CurrentImageBuffer;

Then i allocate memory to it using the devices specs

CurrentImageBuffer = malloc(device_info.ImageWidth*device_info.ImageHeight);

And at some point at my code i capture image and pass CurrentImageBuffer as argument to the capture function:

SGFPM_GetImageEx(m_hFPM, CurrentImageBuffer, GET_IMAGE_TIMEOUT, NULL, GET_IMAGE_DESIRED_QUALITY)

Thats what the variable looks right after this line of code ( i can confirm that it captured a finger):

currentImageBuffer representation

I just don't understand how to proceed creating an image from this buffer, as it doesn't look like a ByteArray

I don't even know if thats the right place to get my image from, but that looks like the right place because its a buffer, right?.

OBS: I'm new to C

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
Bruno Cerk
  • 355
  • 3
  • 16
  • What does the documentation say about `SGFPM_GetImageEx()`? Each of its arguments and its return value – SHG May 09 '19 at 05:17
  • 1
    SGFPM_GetImage() captures an image without checking for the presence of a finger or checking image quality. SGFPM_GetImageEx() captures fingerprint images continuously, checks the image quality against a specified quality value and ignores the image if it does not contain a fingerprint. If a quality image is captured within the given time (the second parameter), SGFPM_GetImageEx() ends its processing. If a window handle is provided by the application, the drivers will draw a fingerprint image in the provided window using the handle value. – Bruno Cerk May 09 '19 at 05:24
  • `SGFPM_DLL_DECL DWORD WINAPI SGFPM_GetImageEx(HSGFPM hFpm, BYTE* buffer, DWORD time, HWND dispWnd, DWORD quality);` – Bruno Cerk May 09 '19 at 05:25
  • So if I understand correctly, the 4th arg to this function (`dispWnd`) is where the fingerprint image is drawn. You provided NULL so you won't get an image. – SHG May 09 '19 at 05:28
  • But it should be a window handler(?), i don't want to display the image on a window, i want the image itself, saving it to a file – Bruno Cerk May 09 '19 at 05:32
  • 1
    According to [doc.](https://docplayer.net/62386993-Programming-manual-for-fdx-sdk-pro-for-windows-fdx-sdk-pro-for-windows-ce.html), it seems to be fine to pass `NULL` for the window handle. (It's a handle - not a handle**r**.) – Scheff's Cat May 09 '19 at 05:41
  • `BYTE *CurrentImageBuffer = malloc(device_info.ImageWidth*device_info.ImageHeight);` let me think, you get the pixel data of a gray level image (with probably 256 shades of gray in the range [0, 255] ( = [black, white])). Have you tried to display this as such? – Scheff's Cat May 09 '19 at 05:45
  • It seems i have found some path, here https://stackoverflow.com/questions/2654480/writing-bmp-image-in-pure-c-c-without-other-libraries – Bruno Cerk May 09 '19 at 05:50
  • I think this CurrentImageBuffer some kind of bmp data – Bruno Cerk May 09 '19 at 05:50
  • @Scheff what do you mean by "Display this as such" ? How could i do it? – Bruno Cerk May 09 '19 at 05:51
  • Please, don't take the `char` (in `unsigned char`) too literal. `unsigned char` is an unsigned integral type like `short int` or `int`. It is used because it provides 8-bit integers which seems to be needed for the storage of image. – Scheff's Cat May 09 '19 at 05:51
  • So you saying this could be a array of bytes? Sorry for not understanding well everything you saying, thats a bit away from my knowledges but i'm doing my best here – Bruno Cerk May 09 '19 at 05:53
  • According to the allocated size (found similar code with google - so, it seems to be correct), you just have the raw data of pixels. To display it, you have to use any available function which is able to display images from raw data. Alternatively, you could store it as an image file (with the resp. configuration of header info) and display it in an image viewer (if you just want to have a look onto the outcome). – Scheff's Cat May 09 '19 at 05:53
  • hmm, got it, do you have any exemple where i can try to take this raw data to an image file? I'm currently working on the one at the link i commented – Bruno Cerk May 09 '19 at 05:55
  • Storing the data as BMP could be an option. You have to fill the BMP header carefully. I couldn't open your linked link but [Wikipedia: BMP](https://en.wikipedia.org/wiki/BMP_file_format) has a description as well. – Scheff's Cat May 09 '19 at 05:58
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/193073/discussion-between-bruno-cerk-and-scheff). – Bruno Cerk May 09 '19 at 06:27
  • "*So you saying this could be a array of bytes?*": `typedef unsigned char BYTE;` and `BYTE* buffer` are used with that exact purpose. – alx - recommends codidact May 09 '19 at 07:58

3 Answers3

9

This is a small sample program to write an 8-bit graylevel image into a Windows BMP file:

#include <stdio.h>

typedef unsigned char Byte;

int writeBMPGray8(FILE *f, int w, int h, const Byte *data)
{
  unsigned bytesPerRow = (w + 3) & ~3; // align to 4 bytes (requirement)
  unsigned size
    = 14 // Bitmap file header size
    + 12 // DIB header size
    + 256 * 3; // palette size
  unsigned gap = size;
  size = (size + 3) & ~3; // align to 4 bytes (requirement)
  gap = size - gap; // get size of gap between end of headers and raw data
  unsigned offs = size; // store offset of raw data
  size += h * bytesPerRow; // bitmap data size in file
  /* write Bitmap file header (14 bytes) */      
  { const Byte buffer[14] = {
      'B', 'M', // magic code
      size & 0xff, size >> 8 & 0xff, size >> 16 & 0xff, size >> 24 & 0xff, // size of BMP file in bytes
      0, 0, // reserved
      0, 0, // reserved
      offs & 0xff, offs >> 8 & 0xff, offs >> 16 & 0xff, offs >> 24 & 0xff // starting offset of pixel data
    };
    if (fwrite(buffer, sizeof buffer, 1, f) != 1) return -1; // ERROR!
  }
  /* write DIB header (12 bytes) */
  { const Byte buffer[12] = {
      12, 0, 0, 0, // size of this header
      w & 0xff, w >> 8 & 0xff, // bitmap width in pixels
      h & 0xff, h >> 8 & 0xff, // bitmap height in pixels
      1, 0, // number of color planes, must be 1
      8, 0 // number of bits per pixel
    };
    if (fwrite(buffer, sizeof buffer, 1, f) != 1) return -1; // ERROR!
  }
  /* write color palette (3 * 256 bytes) */
  for (int i = 0; i < 256; ++i) { // make a gray level palette
    Byte buffer[3] = { i, i, i };
    if (fwrite(buffer, sizeof buffer, 1, f) != 1) return -1; // ERROR!   
  }
  /* write gap (to align start address of raw data with 4 */
  for (int i = 0; i < gap; ++i) {
    if (fputc(0, f) < 0) return -1; // ERROR!
  }
  /* write raw data */
  for (int y = 0; y < h; ++y) { // for all rows
    int x = 0;
    for (; x < w; ++x) { // for all columns
      if (fputc(*data++, f) < 0) return -1; // ERROR!
    }
    // write row padding
    for (; x < bytesPerRow; ++x) {
      if (fputc(0, f) < 0) return -1; // ERROR!
    }
  }
  /* done */
  return 0;
}

int main()
{
  /* a sample image 6 x 8, gray level */
  enum { w = 6, h = 8 };
  const Byte imgRaw[w * h] = {
    0x00, 0x30, 0x60, 0x90, 0xc0, 0xf0,
    0x02, 0x32, 0x62, 0x92, 0xc2, 0xf2,
    0x04, 0x34, 0x64, 0x94, 0xc4, 0xf4,
    0x06, 0x36, 0x66, 0x96, 0xc6, 0xf6,
    0x08, 0x38, 0x68, 0x98, 0xc8, 0xf8,
    0x0a, 0x3a, 0x6a, 0x9a, 0xca, 0xfa,
    0x0c, 0x3c, 0x6c, 0x9c, 0xcc, 0xfc,
    0x0e, 0x3e, 0x6e, 0x9e, 0xce, 0xfe
  };
  FILE *f = fopen("test.bmp", "wb");
  if (!f) return 1; // ERROR!
  if (writeBMPGray8(f, w, h, imgRaw)) return 1; // ERROR!
  if (fclose(f)) return 1; // ERROR!
  return 0; // success
}

The sample image provides some kind of gradients horizontally and vertically. I've chosen a width of 6 intentionally to check/show that row alignment is done properly.

The implementation is based on the description in Wikipedia BMP file format.

To keep it short, I encoded the simplest format – the ancient BITMAPCOREHEADER of Windows 2.0 and OS/2 1.x. (MS Paint can load this as well as the Windows 10 preview. I tested with GIMP which loaded as well without any complaints.)

This is how it looks in GIMP:

Snapshot of test.bmp in GIMP (magnified)

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • 1
    Maybe using a library (openCV for example, but probably there are lighter libraries to do it) would simplify that job. Upvoting though; it's always good to know how it is done manually – alx - recommends codidact May 09 '19 at 08:00
  • @CacahueteFrito There are surely a lot of open source libraries. E.g. Qt provides BMP support as well as ImageMagick (probably) (which has also a C++ API). Sometimes (especially for such MCVE SO answers) it's nice to have some minimal code with no other dependencies than C/C++ standard libs (which even can be tested/demonstrated on online compilers). – Scheff's Cat May 09 '19 at 08:07
  • This is not windows bitmap format. DIB header size should be 40, not 12 (`BITMAPINFOHEADER`). Palette size should be 256 * 4, not 256 * 3 – Barmak Shemirani May 21 '19 at 17:34
  • @BarmakShemirani Please, note that the linked Wikipedia article describes multiple versions of BMP. I chose one of the earliest with least memory footprint / impl. effort. Please, note that MS Paint / MS Explorer Preview as well as GIMP could load the stored image without any problems. – Scheff's Cat May 21 '19 at 18:07
  • Sorry, I was mistaken. I didn't read the second half of your post. – Barmak Shemirani May 21 '19 at 18:47
  • This is the best code for converting usigned char to buffer. Just one issue that bitmap Image is flipped vertical. Can @Scheff'sCat help us rotate the image verticaly. or Flip the Image vertically. or Rotate the Image – Abdurahman Popal Apr 01 '22 at 00:21
  • @Scheff'sCat Thank you so much for your response!!!, I am searching for a solution since 3 days and very super happy that you responded!! – Abdurahman Popal Apr 01 '22 at 08:38
  • @Scheff'sCat can you please help me rewrite the writeBMPGray8() function with a flipped vertically. IT WILL BE A SUPER BIG HELP FROM YOU WITH ME!!! – Abdurahman Popal Apr 01 '22 at 08:42
  • @Scheff'sCat I have changed *data++ to data[y * bytesPerRow + x] and made y-- in for loop... the issue is I am getting memory exception now – Abdurahman Popal Apr 01 '22 at 09:12
  • 1
    @Scheff'sCat The memory Exception Raises from this line: data[y * bytesPerRow + x] – Abdurahman Popal Apr 01 '22 at 09:27
  • @AbdurahmanPopal Sorry, my fault. :-( Of course, it must be `data[y * w + x]`. The row alignment has to be used for the output but the input is given without. (I feel a bit stupid now.) ;-) ([Check on coliru](http://coliru.stacked-crooked.com/a/82208850a73f7865)) – Scheff's Cat Apr 01 '22 at 11:09
  • @Scheff'sCat No Worries; I have looked on coliru link and copied the code and replaced with old function and I don't know what you changed but It worked ; THANK YOU SOOOOO MUCH; YOU SAVE ME. – Abdurahman Popal Apr 01 '22 at 14:36
  • @Scheff'sCat I am about to Create a ZKTeco Fingerprint reader package which will be use at Flutter and calling c++ code from dart language – Abdurahman Popal Apr 01 '22 at 14:37
  • @Scheff'sCat The new example at coliru is working great at Visual Studio with Windows Form App; but; when I move the code to cmake which make a .dll of c++ code. The example for loop is not stopping and the image size grow to more then a GB and the program crashes – Abdurahman Popal Apr 01 '22 at 20:57
  • @Scheff'sCat Looking to over your code. I think the issue is raising from for loop of `for (int y=h; y-- ; ){...}` can you please help me How can I stop the loop of writing the Image Infinitely. – Abdurahman Popal Apr 01 '22 at 21:00
  • @AbdurahmanPopal I don't believe there is an issue with `for (int y=h; y-- ; ){...}`: [Demo on coliru](http://coliru.stacked-crooked.com/a/253b8b086fd54b43) – Scheff's Cat Apr 02 '22 at 12:14
  • 1
    @Scheff'sCat it finally worked as your simple im demo on coliru. Thank you so much! – Abdurahman Popal Apr 12 '22 at 08:56
1

The easiest way to get an image is to make a NetPBM PGM image - see Wikipedia NetPBM page.

So, if your image is say 640 px wide by 480 px tall, you would get a buffer from your SDK with 307,200 bytes and you would write that to a file and check it has the correct length. Call that image.raw.

Now you just need a PGM header, and as your image is greyscale and binary, you need a P5 header.

So, in Terminal you can put a header on:

{ printf "P5\n640 480\n255\n" ; cat image.raw ; } > image.pgm

If you are unfamiliar with that syntax, you can get the same with:

printf "P5\n640 480\n255\n" >  image.pgm
cat image.raw               >> image.pgm

And you can view that image with feh, gimp, Photoshop etc.


If you want to make it into a BMP, or JPEG, or PNG, use ImageMagick which is installed on most Linux distros and is available for macOS and Windows:

magick image.pgm image.png

or

magick image.pgm image.jpg

If your version of ImageMagick is v6 or older, use convert in place of magick:

convert image.pgm image.png
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
1

If you have correctly captured the image in CurrentImageBuffer, you can write this as raw file using the code fragment below:

        fp = fopen(rawFileName,"wb");
        fwrite (CurrentImageBuffer, sizeof (BYTE) , device_info.ImageHeight*device_info.ImageWidth , fp);
        fclose(fp);

As I have used the same environment, I am sending the above fragment from my working codebase. Actually, raw file is later converted to template which is later used for matching / identification and not directly used for viewing etc. Variable rawFileName stores the name of the file as char array (string) where this buffer is stored.

R Tandon
  • 11
  • 1
  • Thanks for the answer, i have managed to save the image as bmp and also save the template and everything to file, i can now save the minutae and open it later for match proposes, everything is working now – Bruno Cerk May 26 '19 at 15:24