Image to GDS Conversion

The input image (input_image.png) has two polygons (width = 50nm, space = 50nm, height = 550nm).

I want to convert this image file into GDS layout with same dimensions and the converted GDS file should be used for other layout operations like boolean operations etc.

I found one related discussion topic:

This image can be converted to GDS file using the details given in this link however the converted layout polygons has small small boxes of size = resolution and these boxes are not needed.

The converted GDS file should look like the layout file attached (remove .txt from input_layout.gds.txt) with only outer boundary layer (layer 1/0) and no boxes inside polygons.

One more requirement is to have this functionality in KLayout Python module (import klayout.db as db) so that I can integrate this piece of code with my other code written on KLayout Python modules.


  • edited April 2023

    Hi Pardeep,

    many thanks for taking that topic to the forum.

    I have created a small script that may help you with the conversion. It produces a layout called "converted.gds" from "image.png". It merges the resulting shapes and applies a smoothing to remove the edges somewhat. You can skip this step - it's for demonstration only. Two layers are generated: 1/0 for the direct conversion (merged) and 2/0 for the smoothed polygons:

    # use klayout.lay and klayout.db for standalone module
    import pya as klay
    import pya as kdb
    ly = kdb.Layout()
    ly.dbu = 0.001
    top_cell = ly.create_cell("TOP")
    image = klay.Image("image.png")
    # threshold
    thr = 128
    # pixel dimension in DBU (EDIT: comment was misleading)
    pixel_size = int(0.5 + 0.1 / ly.dbu)
    image_geo = kdb.Region()
    for y in range(0, image.height()):
      on = False
      xstart = 0
      for x in range(0, image.width()):
        # take green (component 1) value for "intensity"
        value = image.get_pixel(x, y, 1) >= thr
        if value != on:
          on = value
          if on: 
            xstart = x
            image_geo.insert(kdb.Box(xstart, y, x, y + 1) * pixel_size)
      # EDIT: added these two lines
      if on: 
        image_geo.insert(kdb.Box(xstart, y, image.width(), y + 1) * pixel_size)
    image_geo = image_geo.merged()
    layer = ly.layer(1, 0)
    image_geo = image_geo.smoothed(pixel_size * 0.99)
    layer = ly.layer(2, 0)

    Kind regards,


  • Hi Matthias,
    Your code solved one important issue of merging pixel boxes and creating outer boundary however the dimensions are not correct in the converted layout file. For the given input_image.png image file the dimensions in converted layout should be width = 50nm, space = 50nm, height = 550nm, but your code is giving following output:

    The converted layout should look like below image:

    Can we fix this scaling issue during the conversion of image to gds file?


  • Have you verified that your layout DBU(s) and the script's pixel
    scaling are in sync? That the "converted" GDS file and your working
    GDS have identical DBU settings?

  • Layout DBU in my KLayout GUI is set to 0.001 microns which is matching with script's pixel value.

    We can perform a simple experiment with three steps:

    Step 1:
    Create a layout with two polygons (width = 50nm, space = 50nm, height = 550nm). It is also attached in my first problem statement (input_layout.gds.txt)

    Step 2:
    From Step 1 generated layout file, generate a screenshot (similar to input_image.png) using KLayout Python code.

    Step 3:
    Use the screenshot generated from Step 2 and convert it back to GDS file using KLayout Python code.

    If the GDS file generated from Step 3 matches with GDS file created by Step 1 then we are done!

    This seems super easy but currently we don't have KLayout Python based tested scripts which we can use to close this loop of image to GDS and GDS to image conversion without any scaling issue.

  • @pardeep The script was intended as an example, so you should be able to modify it. I wasn't saying script does exactly what you need.

    However, your image is inverted which triggers a small bug: when a scan line ended with a bright pixel, the line is not completed. I have edited the script above, please see the comment

    With this fix, the result is that:

    The reason, the image is inverted is that the script takes "bright" pixels as "on". If you want to change this, modify this line:

        value = image.get_pixel(x, y, 1) >= thr


        value = image.get_pixel(x, y, 1) < thr

    The script uses a pixel dimension of 0.1 here:

    pixel_size = int(0.5 + 0.1 / ly.dbu)

    The 0.5 is not the dimension, it is for rounding and added before the "int" function which truncates the value to the nearest integer.

    If you want a pixel dimension of 1nm, use:

    pixel_size = int(0.5 + 0.001 / ly.dbu)

    Or, when the DBU is already 1nm, simply use:

    pixel_size = 1


  • Thank you Matthias for your updates. I have to test this code on different images. I want to run this example ( .lym file) in batch mode for hundreds of files using Python terminal. Can we run **pymacros **in batch mode?

  • Yes, sure. If you want to run the script with different parameters (e.g. input file name), use global variables and specify them like

    klayout -b -r script.lym -rd input_file=x.png

    assuming your script is called "script.lym" and your input file is read from a variable called "input_file" like this:

    image = klay.Image(input_file)


  • edited September 2023

    Hi Matthias, I used your code for my case in which ly.dbu is 5*1e-5 um and a pixel dimension is 1nm. However, when I tested saving a gds to an image and then used the code to reverse the image back to a gds, the two gds are not the same. The two gds differ by 1nm for most of the edges and by 1.5nm for some. Is it possible to solve this?

    Thank you!

  • No, painting is not made for being precise down to single pixels. It is for visualization, not data translation.

    Apparently some people use the screenshot images for mask making. This is not a supported use model. No warranties about missing pixels or optimization artefacts. If you want precision rendering to bitmaps, other (and slower) algorithms are required.


Sign In or Register to comment.