Write images with HALCON

2018-01-08 by Andreas Heindl

Save a single image

Here comes the basic example of writing an image to your hard-disk. As an example, we generate a single-channel gray-ramp image of size 512x512 named ImageGrayRamp and write it to disk using the PNG format:

* Save a single image to C:\TEMP in format PNG
gen_image_gray_ramp (ImageGrayRamp, 1, 1, 128, 256, 256, 512, 512)
write_image (ImageGrayRamp, 'png', 0,'C:/TEMP/testimage.png')

File format

The file format is specified in the parameter Format of the operator write_image. Normally, we recommend saving images in a lossless image format. Ideally, the file format should be able to save all of HALCON’s image data. The file format should be readable for 3rd-party applications for quick overviews e.g. in Windows Explorer. Saving should be reasonable fast while compressing (without information loss!) as good as possible. Therefore we recommend saving images in PNG format, i.e. with Format set to 'png'. PNG can save 1-channel images and 3-channel images (color images) and can also save the image domain (ROI) as alpha channel. TIFF would be possible, too, but the format seems a bit uncommon nowadays and BMP is uncompressed and seems a bit Windows specific.

On the other hand, lossy image formats like JPG can save a lot of disk space, but at the cost of introducing artifacts into the image. In the following example you can see artifacts which were added when saving the image of a circle with the write_image option jpeg 20: Screenshot of typical JPEG artifacts In addition to the edge of the circle a few additional detected (unwanted!) edges appear in the right image (the image with the JPEG artifacts).

The image formats hobj and ima are HALCON-specific. The file format hobj is best to save images with all information available but it is not recognized by any 3rd-party application and therefore is a bit cumbersome to manage e.g. with Windows Explorer. I’d recommend using hobj if you have special images with more than three channels or with an image type other than byte (e.g. uint2). ima is the legacy format of HALCON to save images. It generates two files, one with the extension .ima and one with the extension .exp which contains additional metadata (for example, size and format of the image).

To select the right image file format for archiving images is very important. The following table tries to help in choosing the right format. The table rows are sorted with the (in our opinion) best image file formats for machine vision coming first, but this of course depends on your specific needs:

FormatLosslesschannelsdomaintypesmultiple objectsgenerally recognized1
  • 1) file format can be assumed to be readable by third-party image processing applications
  • 2) file extension for JPEG-XR is .jxr
  • 3) saves in separate files and requires multiple file names in FileName
  • 4) except complex and vector_field

Other parameters of write_image

The parameter FillColor can be left at the default value of 0. In almost 10 years of using HALCON I never had to change this default of operator write_image. The parameter FileName can contain the file name only to save the image in the current directory. If no extension is given, the extension corresponding to the Format is automatically added to the file name. If an absolute path is specified, you have to use forward slashes, even on Windows. A few examples are:

* Save PNG in current directory. The following two lines
* produce the same output file testimage.png
write_image (ImageGrayRamp, 'png', 0,'testimage.png')
write_image (ImageGrayRamp, 'png', 0,'testimage')

* Save image file 'testimage.tif' in TIFF format in directory C:\TEMP
write_image (ImageGrayRamp, 'tiff', 0,'C:/TEMP/testimage')

* PNG file format can also save the domain of a HALCON image:
gen_circle (Circle, 200, 200, 100.5)
reduce_domain (ImageGrayRamp, Circle, ImageReduced)
write_image (ImageReduced, 'png', 0, 'C:/TEMP/image_reduced.png')

Save multiple images

If you want to save multiple images, e.g. acquired from an acquisition device, you have to implement some code to give the image files names with continuous numbers:
* Example code to acquire images from the first DirectShow device (a Webcam?)
AcqName := 'DirectShow'
gen_empty_obj (ImagesCaptured)
for Index := 0 to 29 by 1
    grab_image (Image, AcqHandle)
    * append current Image to ImagesCaptured
    concat_obj (ImagesCaptured, Image, ImagesCaptured)

* Example code to write multiple images stored in ImagesCaptured
* to separate files 'image000', 'image001', ...
TargetDir := 'C:/TEMP'
count_obj (ImagesCaptured, NumberImages)
for Index0 := 0 to NumberImages-1 by 1
    select_obj (ImagesCaptured, CurrentImage, Index0+1)
    * The $'.3' makes sure that the numbers always consist of 3 digits.
    * Leading zeros are prepended as needed. This leads to better sorting of the
    * files:
    * good: sorted 'image000', 'image002', 'image003', 'image004', ...
    * bad: sorted 'image0', 'image1', 'image10', 'image2', 'image3', ...
    write_image (CurrentImage, 'png', 0, TargetDir + '/image_' + Index0$'.3')

As of HALCON 13, the operator write_image is not parallelized, which means that it can take a very long time if you want to write many images, e.g. a stream of images from an acquisition device. The following snippet parallelizes write_image manually. On my CPU (4 cores, 8 threads) and a SSD drive as write target this reduces the time needed to write 30 color images of size 1920x1080 from 35 seconds (sequential) to 6 seconds (parallel):

* Example code to write multiple images stored in ImagesCaptured
* to separate files 'image000', 'image001', ... Parallel version.
count_obj (ImagesCaptured, NumberImages)
get_system ('processor_num', ProcessorNum)
for IndexSingle0Chunk := 0 to NumberImages-1 by ProcessorNum
    ThreadIDs := {}
    for IndexSingle0 := IndexSingle0Chunk to min2(NumberImages-1,IndexSingle0Chunk + ProcessorNum - 1) by 1
        select_obj (ImagesCaptured, CurrentImage, IndexSingle0+1)
        par_start<ThreadIDs.at(IndexSingle0-IndexSingle0Chunk)> : write_image (CurrentImage, 'png', 0, TargetDir + '/image_' + IndexSingle0$'.3')
    convert_vector_to_tuple (ThreadIDs, ThreadIDsTuple)

Other possibilities

Instead of using write_image with Format 'hobj' the operator write_object can be used:
write_object (Images, TargetDir + '/myobjects')

The extension hobj is automatically added to the filename. Use this for example if you want to access the saved images with HALCON only.

Serialize / deserialize
Images can also be saved to disk in HALCON’s serialized data stream format. This can be useful if additional data should be saved in the same file, e.g. a shape model, which could also be serialized and appended to the stream data file:
* Save images to disk in HALCON's serialized data stream format
open_file (TargetDir + '/myserialized.dat', 'output_binary', FileHandle)
* serialize_image (Images, SerializedItemHandle)
serialize_object (Images, SerializedItemHandle)
fwrite_serialized_item (FileHandle,SerializedItemHandle)
close_file (FileHandle)

* Read images from disk in HALCON's serialized data stream format
open_file (TargetDir + '/myserialized.dat', 'input_binary', FileHandle)
fread_serialized_item (FileHandle, SerializedItemHandle)
* deserialize_image (ImagesRead, SerializedItemHandle)
deserialize_object (ImagesRead, SerializedItemHandle)
close_file (FileHandle)


Although the task to write images to disk and archive them for later usage sounds like a simple job at first, it can be a surprisingly complex topic. We hope to have clarified the various possibilities a bit. Please contact us if you have any questions or need help with your project.