├── elegooMarsUtility ├── __init__.py ├── main.py ├── emUtility.py └── xyCompensateGui.py ├── .github └── FUNDING.yml ├── LICENCE ├── setup.py ├── .gitignore └── README.md /elegooMarsUtility/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: yaqwsx 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jan Mrázek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import setuptools 4 | 5 | 6 | with open("README.md", "r") as fh: 7 | long_description = fh.read() 8 | 9 | setuptools.setup( 10 | name="ElegooMarsUtility", 11 | version="0.2.2", 12 | author="Jan Mrázek", 13 | author_email="email@honzamrazek.cz", 14 | description="Utility to post process sliced models for Elegoo Mars", 15 | long_description=long_description, 16 | long_description_content_type="text/markdown", 17 | url="https://github.com/yaqwsx/ElegooMarsUtility", 18 | packages=setuptools.find_packages(), 19 | classifiers=[ 20 | "Programming Language :: Python :: 3", 21 | "License :: OSI Approved :: MIT License", 22 | "Operating System :: OS Independent", 23 | ], 24 | install_requires=[ 25 | "click", 26 | "numpy", 27 | "pyphotonfile>=0.2", 28 | "scikit-image", 29 | "Pillow==6.2.0" 30 | ], 31 | zip_safe=False, 32 | include_package_data=True, 33 | entry_points = { 34 | "console_scripts": [ 35 | "elegooMarsUtility=elegooMarsUtility.main:cli" 36 | ], 37 | } 38 | ) -------------------------------------------------------------------------------- /elegooMarsUtility/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import click 4 | from elegooMarsUtility import emUtility 5 | import sys 6 | import shutil 7 | import pkg_resources 8 | 9 | if pkg_resources.get_distribution("pyphotonfile").version != "0.2.0": 10 | print("Only version 0.2.0 of pyphoton file is supported for now") 11 | sys.exit(1) 12 | 13 | @click.group() 14 | def cli(): 15 | pass 16 | 17 | @click.command() 18 | @click.argument("input", type=click.File('rb')) 19 | @click.option("--compensation", "-c", type=int, default=0) 20 | @click.option('--firstCompensation', '-f', type=int, default=0) 21 | @click.option('--output', '-o', type=click.File('wb')) 22 | @click.option('--debugShow', type=bool, default=False) 23 | def xyCompensate(input, compensation, firstcompensation, output, debugshow): 24 | emUtility.xyCompensate(input.name, compensation, firstcompensation, output.name, 25 | lambda x, y: print("Processing layer: {}/{}".format(x, y)), debugshow) 26 | 27 | @click.command() 28 | @click.argument("input") 29 | def dump(input): 30 | photon = Photon(input) 31 | photon.export_images('tempdir') 32 | 33 | cli.add_command(xyCompensate) 34 | cli.add_command(dump) 35 | 36 | if __name__ == '__main__': 37 | cli() -------------------------------------------------------------------------------- /elegooMarsUtility/emUtility.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import skimage 3 | from skimage.morphology import square, disk 4 | from matplotlib import pyplot as plt 5 | from pyphotonfile import Photon 6 | from pyphotonfile.photonfile import rle_to_imgarray, imgarr_to_rle 7 | 8 | def xyCompensate(infilename, compensation, firstcompensation, outfilename, report, debugshow=False): 9 | infile = Photon(infilename) 10 | for i in range(len(infile.layers)): 11 | report(i + 1, len(infile.layers)) 12 | for sublayer in infile.layers[i].sublayers: 13 | if i < infile.bottom_layers: 14 | comp = firstcompensation 15 | else: 16 | comp = compensation 17 | if not comp or comp == 0: 18 | continue 19 | layer = rle_to_imgarray(sublayer._data) 20 | if comp < 0: 21 | newLayer = skimage.morphology.binary_dilation( 22 | layer, square(-comp)) 23 | else: 24 | newLayer = skimage.morphology.binary_erosion( 25 | layer, square(comp)) 26 | sublayer._data = imgarr_to_rle(skimage.img_as_ubyte(newLayer)) 27 | if debugshow: 28 | f, a = plt.subplots(1, 2) 29 | a[0].imshow(layer, cmap=plt.cm.gray) 30 | a[1].imshow(newLayer, cmap=plt.cm.gray) 31 | plt.show() 32 | infile.write(outfilename) 33 | infile.write(outfilename) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | 91 | # VSCode settings 92 | .vscode 93 | 94 | # Test files 95 | *.cbddlp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elegoo Mars Utility 2 | 3 | Simple tool to post-process CBDDLP files from ChiTuBox. 4 | 5 | [![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/E1E2181LU) 6 | 7 | ## Usage 8 | 9 | If you are a Windows user, there pre-built GUI available at the [release 10 | page](https://github.com/yaqwsx/ElegooMarsUtility/releases). Just download it 11 | and run it. There might be a false positive alarm by your anti-virus software. 12 | Please add an exception or follow the installation procedure below (it does not 13 | trigger false positives, however, it is slightly complicated). 14 | 15 | If you use Linux, please follow the installation procedure using PIP below. 16 | 17 | ## Installation 18 | 19 | First, you have to install bleeding edge version of the pyphotonfile library as 20 | it has not been published yet in the repository: 21 | 22 | ``` 23 | pip install git+https://github.com/fookatchu/pyphotonfile@master 24 | ``` 25 | 26 | Then you can install the utility by pip 27 | 28 | ``` 29 | pip install ElegooMarsUtility 30 | ``` 31 | 32 | ## Usage 33 | 34 | The tool has several commands to transform your sliced images. The list of the 35 | commands follows: 36 | 37 | ## xycompensate 38 | 39 | This command can compensate for exposure bleeding and the elephant foot of the 40 | first layers by slightly shrinking the footprint of the layers. The bleeding 41 | leads to slightly larger components (and smaller holes). 42 | 43 | Arguments: 44 | - `-c`, `--compensation` - the amount of compensation for regular layers. The 45 | unit is a single pixel. You have to find the correct amount empirically by 46 | printing a test object and measuring it. It the compensation is negative the 47 | footprint is enlarged. 48 | - `-f`, `--firstCompensation` - the amount of compensation for the first layers. 49 | Argument is the same as in the previous case. 50 | - `-o`, `--output` - filename for the resulting file 51 | - `--debugShow` - shows side by side each compensated layer -------------------------------------------------------------------------------- /elegooMarsUtility/xyCompensateGui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import PySimpleGUI as sg 4 | import sys 5 | import time 6 | from elegooMarsUtility import emUtility 7 | 8 | layout = [ 9 | [sg.Text("Please enter your calibration data, input and output file")], 10 | [sg.Text("First layer compensation:", size=(23, 1)), sg.InputText("6", size=(32, 1))], 11 | [sg.Text("Normal layer compensation:", size=(23, 1)), sg.InputText("0", size=(32, 1))], 12 | [sg.Text("Input file:", size=(10, 1)), sg.InputText(size=(30, 1)), sg.FileBrowse(size=(10, 1), file_types=(("CBDDLP Files", "*.cbddlp"),))], 13 | [sg.Text("Output file:", size=(10, 1)), sg.InputText(size=(30, 1)), sg.FileSaveAs(size=(10, 1), file_types=(("CBDDLP Files", "*.cbddlp"),))], 14 | [sg.Submit(button_text="Run", size=(25, 1)), sg.Cancel(button_text="Exit", size=(25, 1))] 15 | ] 16 | window = sg.Window("Elegoo Mars utility").Layout(layout) 17 | 18 | while True: 19 | button, values = window.Read() 20 | 21 | if button == "Exit": 22 | sys.exit(0) 23 | 24 | try: 25 | firstCompensation = int(values[0]) 26 | except ValueError: 27 | sg.Popup("Error: first layer compensation is not a number") 28 | continue 29 | 30 | try: 31 | normalCompensation = int(values[1]) 32 | except ValueError: 33 | sg.Popup("Error: first layer compensation is not a number") 34 | continue 35 | 36 | if len(values[2]) == 0: 37 | sg.Popup("Error: you have to select input file") 38 | continue 39 | inputFilename = values[2] 40 | 41 | if len(values[3]) == 0: 42 | sg.Popup("Error: you have to select output file") 43 | continue 44 | outputFilename = values[3] 45 | if not outputFilename.endswith(".cbddlp"): 46 | outputFilename += ".cbddlp" 47 | 48 | break 49 | window.Close() 50 | 51 | layout = [ [sg.Text("Applying the compensation")], 52 | [sg.ProgressBar(1, orientation="h", size=(20, 20), key="progress")], 53 | ] 54 | 55 | window = sg.Window("Elegoo Mars utility", layout).Finalize() 56 | progress_bar = window.FindElement("progress") 57 | emUtility.xyCompensate(inputFilename, normalCompensation, firstCompensation, outputFilename, 58 | lambda x, y: progress_bar.UpdateBar(x, y)) 59 | window.Close() 60 | 61 | sg.Popup("Done. Outputfile: {}".format(outputFilename)) --------------------------------------------------------------------------------