curtacircuitos / pcb-tools

Tools to work with PCB data (Gerber, Excellon, NC files) using Python.
Apache License 2.0
279 stars 135 forks source link

png bottom image flipped #119

Open aster94 opened 4 years ago

aster94 commented 4 years ago

hello,

using the test files attached I got a perfect image from the up, but a wrong image in the bottom:

cairo_example cairo_bottom

As you can see the silkscreen and the drill files should be inversed. Uploading the same files on OSHPark brings to correct images

Gerber-test.zip

This is my test file:

import os
from gerber import load_layer
from gerber.render import RenderSettings, theme
from gerber.render.cairo_backend import GerberCairoContext

GERBER_FOLDER = "C:\\Users\\vincenzo\\Desktop\\Gerber-test\\"
output = "C:\\Users\\vincenzo\\Desktop\\"

# The default style can be overridden by passing a RenderSettings instance to
# render_layer().
# First, create a settings object:
our_settings = RenderSettings(color=theme.COLORS['white'], alpha=0.85)

# Open the gerber files
drill = load_layer(os.path.join(GERBER_FOLDER, 'ATMEGA328.drl'))

copper = load_layer(os.path.join(GERBER_FOLDER, 'ATMEGA328-F_Cu.gbr'))
mask = load_layer(os.path.join(GERBER_FOLDER, 'ATMEGA328-F_Mask.gbr'))
silk = load_layer(os.path.join(GERBER_FOLDER, 'ATMEGA328-F_SilkS.gbr'))

# Create a new drawing context
ctx = GerberCairoContext()

# Draw the copper layer. render_layer() uses the default color scheme for the
# layer, based on the layer type. Copper layers are rendered as
ctx.render_layer(copper)
ctx.render_layer(mask)
ctx.render_layer(silk, settings=our_settings)
ctx.render_layer(drill)

# Write output to png file
ctx.dump(os.path.join(os.path.dirname(__file__), 'cairo_example.png'))

# Clear the drawing
ctx.clear()

# Load the bottom layers
copper = load_layer(os.path.join(GERBER_FOLDER, 'ATMEGA328-B_Cu.gbr'))
mask = load_layer(os.path.join(GERBER_FOLDER, 'ATMEGA328-B_Mask.gbr'))
silk = load_layer(os.path.join(GERBER_FOLDER, 'ATMEGA328-B_SilkS.gbr'))

# Render bottom layers
ctx.render_layer(copper)
ctx.render_layer(mask)
ctx.render_layer(silk, settings=our_settings)
ctx.render_layer(drill)

# Write png file
ctx.dump(os.path.join(os.path.dirname(__file__), 'cairo_bottom.png'))
MarinMikael commented 4 years ago

Hello, It seems that your filenames are not matching the pattern detected by the library, as such, pcb-tools is unaware that your bottom files are to be flipped. I do not know this file pattern so I cannot tell if pcb-tools is right or wrong. I'll let the maintainers be the judge of that.

Meanwhile, a quick fix to your problem would be to change the names of the files to the following: ATMEGA328-B.Cu.gbr ATMEGA328-B.Mask.gbr ATMEGA328-B.SilkS.gbr

Alternatively, you can bypass load_layer and directly use: PCBLayer(fileame, layer_class, common.read(filename))

Where layer_class is any of the following: top, bottom, internal, topsilk, bottomsilk, topmask, bottommask, toppaste, bottompaste

aster94 commented 4 years ago

Tomorrow I will try the second approach

My files are generated by the latest stable release of kicad 5, I guess them are right, but my knowledge about gerbers file is limited

aster94 commented 4 years ago

ok that's a little better: only the drill file is inverted

front bottom

Do you know how to reduce the size of the images? both are about 1.6Mb I don't need such a resolution EDIT: found ctx = GerberCairoContext(scale=100)

import os
from gerber.layers import *
from gerber.render.cairo_backend import GerberCairoContext
from PIL import Image

def render_pcb(input_path, output_file):
    img_front_path = os.path.join(input_path, 'front.png')
    img_bottom_path = os.path.join(input_path, 'bottom.png')

    # Open the gerber files
    for r, d, f in os.walk(input_path):
        for file in f:
            if '.drl' in file:
                real_path = os.path.join(r, file)
                drill = DrillLayer(real_path, common.read(real_path))

            # Front
            elif '-F_Cu.gbr' in file:
                real_path = os.path.join(r, file)
                copper_front = PCBLayer(real_path, 'top', common.read(real_path))
            elif '-F_Mask.gbr' in file:
                real_path = os.path.join(r, file)
                mask_front = PCBLayer(real_path, 'topmask', common.read(real_path))
                pass
            elif '-F_SilkS.gbr' in file:
                real_path = os.path.join(r, file)
                silk_front = PCBLayer(real_path, 'topsilk', common.read(real_path))
                pass
            # Bottom

            elif '-B_Cu.gbr' in file:
                real_path = os.path.join(r, file)
                copper_bottom = PCBLayer(real_path, 'bottom', common.read(real_path))
            elif '-B_Mask.gbr' in file:
                real_path = os.path.join(r, file)
                mask_bottom = PCBLayer(real_path, 'bottommask', common.read(real_path))
            elif '-B_SilkS.gbr' in file:
                real_path = os.path.join(r, file)
                silk_bottom = PCBLayer(real_path, 'bottomsilk', common.read(real_path))
            else:
                pass

    # Create a new drawing context
    ctx = GerberCairoContext()

    ctx.render_layer(copper_front)
    ctx.render_layer(mask_front)
    ctx.render_layer(silk_front)
    ctx.render_layer(drill)

    # Write png file
    ctx.dump(img_front_path)

    # Clear the drawing
    ctx.clear()

    # Render bottom layers
    ctx.render_layer(copper_bottom)
    ctx.render_layer(mask_bottom)
    ctx.render_layer(silk_bottom)
    ctx.render_layer(drill) #not working, it is inverted

    # Write png file
    ctx.dump(img_bottom_path)

    ctx.clear()
'''
    # Concatenate
    front = Image.open(img_front_path)
    bottom = Image.open(img_bottom_path)
    render = Image.new('RGB', (front.width, front.height * 2))
    render.paste(front, (0, 0))
    render.paste(bottom, (0, front.height))
    render.save(output_file)
'''

project_path = os.path.dirname(os.path.realpath(__file__))
gerber_path = os.path.join(project_path, 'Gerber-test')
project_name = "test-pcb"
output_path = os.path.join(project_path, project_name + ".png")

print('Project:{}\nGerber:\t{}\nPNG:\t{}'.format(project_path, gerber_path, output_path))
render_pcb(gerber_path, output_path)

Is it correct that I am passing the filename twice? copper_front = PCBLayer(real_path, 'top', common.read(real_path))

MarinMikael commented 4 years ago

You can also mirror the layer using RenderSettings:

from gerber.render import RenderSettings
ctx.render_layer(drill, settings=RenderSettings(mirror=True))

If you want more control about the appearance of your pcb, RenderSettings is the way to go.

I recommend you to use file.endswith(pattern) to check for patterns instead of for ".." in file.

aster94 commented 4 years ago

It is simply perfect, thank you very much @MarinMikael I really appreciated your help 😀

I will leave the issue open in case the mantainers would like to add more support out of the box for the kicad gerbers files

test-pcb

here there is the final code: still i do not understand why i am passing two times the path of the gerber file DrillLayer(real_path, common.read(real_path))

import os
from gerber import common 
from gerber.layers import PCBLayer, DrillLayer
from gerber.render import RenderSettings
from gerber.render.cairo_backend import GerberCairoContext
from PIL import Image

SCALE = 25
OFFSET = 20

def render_pcb(input_path, output_file):
    img_front_path = os.path.join(input_path, 'front.png')
    img_bottom_path = os.path.join(input_path, 'bottom.png')

    for file in os.listdir(input_path):
        if not os.path.isfile(os.path.join(input_path, file)):
            continue

        real_path = os.path.join(input_path, file)

        # Drill
        if file.endswith('.drl'):
            drill = DrillLayer(real_path, common.read(real_path))

        # Front
        elif file.endswith('-F_Cu.gbr'):
            copper_front = PCBLayer(real_path, 'top', common.read(real_path))
        elif file.endswith('-F_Mask.gbr'):
            mask_front = PCBLayer(real_path, 'topmask', common.read(real_path))
        elif file.endswith('-F_SilkS.gbr'):
            silk_front = PCBLayer(real_path, 'topsilk', common.read(real_path))

        # Bottom
        elif file.endswith('-B_Cu.gbr'):
            copper_bottom = PCBLayer(real_path, 'bottom', common.read(real_path))
        elif file.endswith('-B_Mask.gbr'):
            mask_bottom = PCBLayer(real_path, 'bottommask', common.read(real_path))
        elif file.endswith('-B_SilkS.gbr'):
            silk_bottom = PCBLayer(real_path, 'bottomsilk', common.read(real_path))
        else:
            continue

    # Create a new drawing context
    ctx = GerberCairoContext(scale=SCALE)

    ctx.render_layer(copper_front)
    ctx.render_layer(mask_front)
    ctx.render_layer(silk_front)
    ctx.render_layer(drill)

    # Write png file
    ctx.dump(img_front_path)

    # Clear the drawing
    ctx.clear()

    # Render bottom layers
    ctx.render_layer(copper_bottom)
    ctx.render_layer(mask_bottom)
    ctx.render_layer(silk_bottom)
    ctx.render_layer(drill, settings=RenderSettings(mirror=True))

    # Write png file
    ctx.dump(img_bottom_path)

    ctx.clear()

    # Concatenate
    front = Image.open(img_front_path)
    bottom = Image.open(img_bottom_path)
    render = Image.new('RGB', (front.width, front.height * 2 + OFFSET))
    render.paste(front, (0, 0))
    render.paste(bottom, (0, front.height + OFFSET))
    render.save(output_file)
    render.show()

project_path = os.path.dirname(os.path.realpath(__file__))
gerber_path = os.path.join(project_path, 'Gerber-test')
project_name = "test-pcb"
output_path = os.path.join(project_path, project_name + ".png")

print('Project:{}\nGerber:\t{}\nPNG:\t{}'.format(project_path, gerber_path, output_path))
render_pcb(gerber_path, output_path)
MarinMikael commented 4 years ago

:)

still i do not understand why i am passing two times the path of the gerber file DrillLayer(real_path, common.read(real_path))

DrillLayer needs the path and the content of the file. common.read needs the path to get the content of the file.

xiaoqiangnobug commented 4 years ago

Hello, In the process of using it, I successfully render Gerber files on windows, but when porting to the Ubuntu system, all top layer inversion will appear when combined rendering, and there is no problem with single

file gto2 微信图片_20200721170545