[Render_3D] Added option to auto-crop the resulting PNG

This commit is contained in:
Salvador E. Tropea 2022-10-30 14:35:18 -03:00
parent 5b19227625
commit 81ce2004d5
8 changed files with 100 additions and 20 deletions

View File

@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Option to control the *SVG precision* (units scale) - Option to control the *SVG precision* (units scale)
- Render_3D: - Render_3D:
- Option to render only some components (like in PcbDraw) - Option to render only some components (like in PcbDraw)
- Option to auto-crop the resulting PNG
- SVG: - SVG:
- Option to control the *SVG precision* (units scale) - Option to control the *SVG precision* (units scale)

View File

@ -171,6 +171,13 @@ Notes:
- Find commit hash and/or date for `sch_replace` - Find commit hash and/or date for `sch_replace`
- Find commit hash and/or date for `set_text_variables` - Find commit hash and/or date for `set_text_variables`
[**ImageMagick**](https://imagemagick.org/) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://imagemagick.org/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/imagemagick) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png)
- Optional to:
- Create outputs preview for `navigate_results`
- Create monochrome prints and scaled PNG files for `pcb_print`
- Create JPG and BMP images for `pcbdraw`
- Automatically crop images for `render_3d`
[**RSVG tools**](https://gitlab.gnome.org/GNOME/librsvg) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://gitlab.gnome.org/GNOME/librsvg) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/librsvg2-bin) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) [**RSVG tools**](https://gitlab.gnome.org/GNOME/librsvg) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://gitlab.gnome.org/GNOME/librsvg) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/librsvg2-bin) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png)
- Optional to: - Optional to:
- Create outputs preview for `navigate_results` - Create outputs preview for `navigate_results`
@ -178,12 +185,6 @@ Notes:
- Create PDF, PNG, PS and EPS formats for `pcb_print` - Create PDF, PNG, PS and EPS formats for `pcb_print`
- Create PNG, JPG and BMP images for `pcbdraw` - Create PNG, JPG and BMP images for `pcbdraw`
[**ImageMagick**](https://imagemagick.org/) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://imagemagick.org/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/imagemagick) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png)
- Optional to:
- Create outputs preview for `navigate_results`
- Create monochrome prints and scaled PNG files for `pcb_print`
- Create JPG and BMP images for `pcbdraw`
[**Ghostscript**](https://www.ghostscript.com/) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://www.ghostscript.com/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/ghostscript) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) [**Ghostscript**](https://www.ghostscript.com/) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://www.ghostscript.com/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/ghostscript) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png)
- Optional to: - Optional to:
- Create outputs preview for `navigate_results` - Create outputs preview for `navigate_results`
@ -3092,6 +3093,10 @@ Notes:
- **`view`**: [string='top'] [top,bottom,front,rear,right,left,z,Z,y,Y,x,X] Point of view. - **`view`**: [string='top'] [top,bottom,front,rear,right,left,z,Z,y,Y,x,X] Point of view.
- **`zoom`**: [number=0] Zoom steps. Use positive to enlarge, get closer, and negative to reduce. - **`zoom`**: [number=0] Zoom steps. Use positive to enlarge, get closer, and negative to reduce.
Same result as using the mouse wheel in the 3D viewer. Same result as using the mouse wheel in the 3D viewer.
- `auto_crop`: [boolean=false] When enabled the image will be post-processed to make the background transparent and then remove the
empty space around the image. In this mode the `background1` and `background2` colors are ignored.
- `auto_crop_color`: [string='#ff70b7'] Color used for the chroma key. Adjust it if some regions of the board becomes transparent.
- `auto_crop_fuzz`: [number=15] [0,100] Chroma key tolerance (percent). Bigger values will remove more pixels.
- `background1`: [string='#66667F'] First color for the background gradient. - `background1`: [string='#66667F'] First color for the background gradient.
- `background2`: [string='#CCCCE5'] Second color for the background gradient. - `background2`: [string='#CCCCE5'] Second color for the background gradient.
- `board`: [string='#332B16'] Color for the board without copper or solder mask. - `board`: [string='#332B16'] Color for the board without copper or solder mask.
@ -4519,4 +4524,5 @@ relative paths. So you can move the new PCB file to any place, as long as the `3
- **Chip in assembly_simple.svg**: [oNline Web Fonts](https://www.onlinewebfonts.com/) - **Chip in assembly_simple.svg**: [oNline Web Fonts](https://www.onlinewebfonts.com/)
- **Wrench**: [Freepik - Flaticon](https://www.flaticon.es/iconos-gratis/llave-inglesa) - **Wrench**: [Freepik - Flaticon](https://www.flaticon.es/iconos-gratis/llave-inglesa)
- **Most icons for the navigate_results output**: The KiCad project - **Most icons for the navigate_results output**: The KiCad project
- **PTV09A 3D Model**: Dmitry Levin (https://grabcad.com/dmitry.levin-6) - **PTV09A 3D Model**: Dmitry Levin ([GrabCad](https://grabcad.com/dmitry.levin-6))
- **PcbDraw PCB example**: [Arduino Learning Kit Starter](https://github.com/RoboticsBrno/ArduinoLearningKitStarter)

View File

@ -1923,4 +1923,5 @@ relative paths. So you can move the new PCB file to any place, as long as the `3
- **Chip in assembly_simple.svg**: [oNline Web Fonts](https://www.onlinewebfonts.com/) - **Chip in assembly_simple.svg**: [oNline Web Fonts](https://www.onlinewebfonts.com/)
- **Wrench**: [Freepik - Flaticon](https://www.flaticon.es/iconos-gratis/llave-inglesa) - **Wrench**: [Freepik - Flaticon](https://www.flaticon.es/iconos-gratis/llave-inglesa)
- **Most icons for the navigate_results output**: The KiCad project - **Most icons for the navigate_results output**: The KiCad project
- **PTV09A 3D Model**: Dmitry Levin (https://grabcad.com/dmitry.levin-6) - **PTV09A 3D Model**: Dmitry Levin ([GrabCad](https://grabcad.com/dmitry.levin-6))
- **PcbDraw PCB example**: [Arduino Learning Kit Starter](https://github.com/RoboticsBrno/ArduinoLearningKitStarter)

View File

@ -1829,6 +1829,13 @@ outputs:
type: 'render_3d' type: 'render_3d'
dir: 'Example/render_3d_dir' dir: 'Example/render_3d_dir'
options: options:
# [boolean=false] When enabled the image will be post-processed to make the background transparent and then remove the
# empty space around the image. In this mode the `background1` and `background2` colors are ignored
auto_crop: false
# [string='#ff70b7'] Color used for the chroma key. Adjust it if some regions of the board becomes transparent
auto_crop_color: '#ff70b7'
# [number=15] [0,100] Chroma key tolerance (percent). Bigger values will remove more pixels
auto_crop_fuzz: 15
# [string='#66667F'] First color for the background gradient # [string='#66667F'] First color for the background gradient
background1: '#66667F' background1: '#66667F'
# [string='#CCCCE5'] Second color for the background gradient # [string='#CCCCE5'] Second color for the background gradient

View File

@ -14,6 +14,7 @@ from .misc import W_UNKOPS
from . import log from . import log
logger = log.get_logger() logger = log.get_logger()
HEX_DIGIT = '[A-Fa-f0-9]{2}'
def do_filter(v): def do_filter(v):
@ -31,8 +32,10 @@ class Optionable(object):
_str_values_re = compile(r"string=.*\] \[([^\]]+)\]") _str_values_re = compile(r"string=.*\] \[([^\]]+)\]")
_num_range_re = compile(r"number=.*\] \[(-?\d+),(-?\d+)\]") _num_range_re = compile(r"number=.*\] \[(-?\d+),(-?\d+)\]")
_default = None _default = None
_color_re = re.compile(r"#[A-Fa-f0-9]{6}$")
_color_re_a = re.compile(r"#[A-Fa-f0-9]{8}$") _color_re = re.compile(r"#("+HEX_DIGIT+"){3}$")
_color_re_a = re.compile(r"#("+HEX_DIGIT+"){4}$")
_color_re_component = re.compile(HEX_DIGIT)
def __init__(self): def __init__(self):
self._unkown_is_error = False self._unkown_is_error = False
@ -372,15 +375,36 @@ class Optionable(object):
def get_default(cls): def get_default(cls):
return cls._default return cls._default
def validate_color_str(self, color):
return self._color_re.match(color) or self._color_re_a.match(color)
def validate_color(self, name): def validate_color(self, name):
color = getattr(self, name) if not self.validate_color_str(getattr(self, name)):
if not self._color_re.match(color) and not self._color_re_a.match(color):
raise KiPlotConfigurationError('Invalid color for `{}` use `#rrggbb` or `#rrggbbaa` with hex digits'.format(name)) raise KiPlotConfigurationError('Invalid color for `{}` use `#rrggbb` or `#rrggbbaa` with hex digits'.format(name))
def validate_colors(self, names): def validate_colors(self, names):
for color in names: for color in names:
self.validate_color(color) self.validate_color(color)
def parse_one_color(self, color):
res = self._color_re_component.findall(color)
alpha = 1.0
if len(res) > 3:
alpha = int(res[3], 16)/255.0
return (int(res[0], 16)/255.0, int(res[1], 16)/255.0, int(res[2], 16)/255.0, alpha)
def color_to_rgb(self, color):
index = 4 if len(color) > 4 else 0
alpha = color[index+3]
if alpha == 1.0:
return "rgb({}, {}, {})".format(round(color[index]*255.0), round(color[index+1]*255.0),
round(color[index+2]*255.0))
return "rgba({}, {}, {}, {})".format(round(color[index]*255.0), round(color[index+1]*255.0),
round(color[index+2]*255.0), alpha)
def color_str_to_rgb(self, color):
return self.color_to_rgb(self.parse_one_color(color))
class BaseOptions(Optionable): class BaseOptions(Optionable):
""" A class to validate and hold output options. """ A class to validate and hold output options.

View File

@ -9,9 +9,13 @@ Dependencies:
- from: KiAuto - from: KiAuto
role: mandatory role: mandatory
version: 2.0.4 version: 2.0.4
- from: ImageMagick
role: Automatically crop images
""" """
import os import os
import shlex
from shutil import rmtree from shutil import rmtree
import subprocess
from .misc import (RENDER_3D_ERR, PCB_MAT_COLORS, PCB_FINISH_COLORS, SOLDER_COLORS, SILK_COLORS, from .misc import (RENDER_3D_ERR, PCB_MAT_COLORS, PCB_FINISH_COLORS, SOLDER_COLORS, SILK_COLORS,
KICAD_VERSION_6_0_2, MISSING_TOOL) KICAD_VERSION_6_0_2, MISSING_TOOL)
from .gs import GS from .gs import GS
@ -24,6 +28,19 @@ from . import log
logger = log.get_logger() logger = log.get_logger()
def _run_command(cmd):
logger.debug('- Executing: '+shlex.join(cmd))
try:
cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
logger.error('Failed to run %s, error %d', cmd[0], e.returncode)
if e.output:
logger.debug('Output from command: '+e.output.decode())
exit(RENDER_3D_ERR)
if cmd_output.strip():
logger.debug('- Output from command:\n'+cmd_output.decode())
class Render3DOptions(Base3DOptions): class Render3DOptions(Base3DOptions):
_colors = {'background1': 'bg_color_1', _colors = {'background1': 'bg_color_1',
'background2': 'bg_color_2', 'background2': 'bg_color_2',
@ -107,6 +124,13 @@ class Render3DOptions(Base3DOptions):
self.show_components = Optionable self.show_components = Optionable
""" *[list(string)|string=all] [none,all] List of components to draw, can be also a string for `none` or `all`. """ *[list(string)|string=all] [none,all] List of components to draw, can be also a string for `none` or `all`.
Unlike the `pcbdraw` output, the default is `all` """ Unlike the `pcbdraw` output, the default is `all` """
self.auto_crop = False
""" When enabled the image will be post-processed to make the background transparent and then remove the
empty space around the image. In this mode the `background1` and `background2` colors are ignored """
self.auto_crop_color = "#ff70b7"
""" Color used for the chroma key. Adjust it if some regions of the board becomes transparent """
self.auto_crop_fuzz = 15
""" [0,100] Chroma key tolerance (percent). Bigger values will remove more pixels """
super().__init__() super().__init__()
self._expand_ext = 'png' self._expand_ext = 'png'
@ -147,7 +171,7 @@ class Render3DOptions(Base3DOptions):
self.copper = "#"+color self.copper = "#"+color
break break
super().config(parent) super().config(parent)
self.validate_colors(self._colors.keys()) self.validate_colors(list(self._colors.keys())+['auto_crop_color'])
view = self._views.get(self.view, None) view = self._views.get(self.view, None)
if view is not None: if view is not None:
self.view = view self.view = view
@ -232,6 +256,9 @@ class Render3DOptions(Base3DOptions):
"Please upgrade KiCad to 6.0.2 or newer") "Please upgrade KiCad to 6.0.2 or newer")
exit(MISSING_TOOL) exit(MISSING_TOOL)
command = self.ensure_tool('KiAuto') command = self.ensure_tool('KiAuto')
if self.auto_crop:
self.background1 = self.background2 = self.auto_crop_color
convert_command = self.ensure_tool('ImageMagick')
# Base command with overwrite # Base command with overwrite
cmd = [command, '--rec_w', str(self.width+2), '--rec_h', str(self.height+85), cmd = [command, '--rec_w', str(self.width+2), '--rec_h', str(self.height+85),
'3d_view', '--output_name', output] '3d_view', '--output_name', output]
@ -255,6 +282,12 @@ class Render3DOptions(Base3DOptions):
video_name = os.path.join(self.expand_filename_pcb(GS.out_dir), 'pcbnew_3d_view_screencast.ogv') video_name = os.path.join(self.expand_filename_pcb(GS.out_dir), 'pcbnew_3d_view_screencast.ogv')
if os.path.isfile(video_name): if os.path.isfile(video_name):
os.remove(video_name) os.remove(video_name)
if self.auto_crop:
_run_command([convert_command, output, '-fuzz', str(self.auto_crop_fuzz)+'%', '-transparent',
self.color_str_to_rgb(self.auto_crop_color), output])
# Don't ask me why I need 2 passes
_run_command([convert_command, output, '-trim', '+repage', output])
_run_command([convert_command, output, '-trim', '+repage', output])
@output_class @output_class

View File

@ -159,7 +159,7 @@ deps = '{\
],\ ],\
"extra_deb": null,\ "extra_deb": null,\
"help_option": "--version",\ "help_option": "--version",\
"importance": 3,\ "importance": 4,\
"in_debian": true,\ "in_debian": true,\
"is_kicad_plugin": false,\ "is_kicad_plugin": false,\
"is_python": false,\ "is_python": false,\
@ -190,6 +190,13 @@ deps = '{\
"max_version": null,\ "max_version": null,\
"output": "pcbdraw",\ "output": "pcbdraw",\
"version": null\ "version": null\
},\
{\
"desc": "Automatically crop images",\
"mandatory": false,\
"max_version": null,\
"output": "render_3d",\
"version": null\
}\ }\
],\ ],\
"tests": [],\ "tests": [],\

View File

@ -2,18 +2,19 @@
kibot: kibot:
version: 1 version: 1
global:
solder_mask_color: blue
pcb_finish: ENIG
outputs: outputs:
- name: render_list - name: render_list
comment: "Render with only some components" comment: "Render with only some components"
type: render_3d type: render_3d
options: options:
width: 800 width: 1280
height: 600 height: 960
orthographic: true orthographic: true
zoom: 4 zoom: 4
show_components: ["RV1", "RV2", "U1", "U2", "U3"] show_components: ["RV1", "RV2", "U1", "U2", "U3"]
ray_tracing: true ray_tracing: true
background1: "#ff70b7" auto_crop: true
background2: "#ff70b7"
# background1: "#ffffff"
# background2: "#ffffff"