mozman / ezdxf

Python interface to DXF
https://ezdxf.mozman.at
MIT License
927 stars 190 forks source link

ezdxf.addons.drawing dxf2png cpu 100% #699

Closed vicky8989 closed 2 years ago

vicky8989 commented 2 years ago

i want to export an png with dxf。This dxf is very complicated,it may have 50000+ entites, when i export dxf to png, use the code below:

Frontend(ctx, out).draw_layout(msp, finalize=True)

the cpu of my computer 8G is going 100%。I put this to a unix server, 16G ,cpu is also going to 100%。

did anyone meet this problem? is there any other way to invoid this question. it kills my application... Appriciate your help.

The test dxf file below:

F397.dxf.zip

mbway commented 2 years ago

It sounds like the problem you are having is that you do not have enough RAM to complete the drawing of the entire file. You could try drawing each layer individually with a transparent background and compositing the result afterwards? Also the matplotlib backend converts the DXF entities to plotting entities and only rasterises at the end. Drawing pixels onto a canvas immediately would probably require a lot less ram so you could implement a backend using openCV or something that can draw pixels.

chibai commented 2 years ago

Maybe you can upload the dxf file?

mozman commented 2 years ago

This example script shows how to render the model space in multiple tiles, it's the result of this stackoverflow question. The script always loads the whole DXF file, but renders only a certain area of the model space, this may need less memory than a full rendering. You can stitch the tiles afterwards with an image library like Pillow automatically (I guess).

vicky8989 commented 2 years ago

Maybe you can upload the dxf file?

uploaded!

vicky8989 commented 2 years ago

This example script shows how to render the model space in multiple tiles, it's the result of this stackoverflow question. The script always loads the whole DXF file, but renders only a certain area of the model space, this may need less memory than a full rendering. You can stitch the tiles afterwards with an image library like Pillow automatically (I guess).

Appricatiate your suggestion, i will try. Added on my question, i've uploaded my dxf file. i guess this problem is caused by the insert entity. i've debug the source code, the code will invoke explode.js , i think this may get an endlessloop. i tried to explode this file in autocad software,it will get 100 0000 + virtual entites , it has so many blocks. i think your answer may work, i will find how to use this solution. thanks so much!

vicky8989 commented 2 years ago

This example script shows how to render the model space in multiple tiles, it's the result of this stackoverflow question. The script always loads the whole DXF file, but renders only a certain area of the model space, this may need less memory than a full rendering. You can stitch the tiles afterwards with an image library like Pillow automatically (I guess).

i tried this way to export pngs。i think this way can improve the render performance in frontend,e.g. Web except exporting a png。 Maybe i have to find another better way to analyse INSERT entity。o(╥﹏╥)o

mozman commented 2 years ago

The good news are, ezdxf can now render this file (unexpected matplotlib errors are handled now), the bad news are, it requires ~24.1 GB (overall) on a Windows 11 machine. Rendering the model space in tiles reduces the memory usage, but unfortunately does this method not work for that complex drawings, there are no exact cutting edges between the tiles:

image

vicky8989 commented 2 years ago

The good news are, ezdxf can now render this file (unexpected matplotlib errors are handled now), the bad news are, it requires ~24.1 GB (overall) on a Windows 11 machine. Rendering the model space in tiles reduces the memory usage, but unfortunately does this method not work for that complex drawings, there are no exact cutting edges between the tiles:

image

Thanks a lot! I wonder if I can change the INSERT analyser method instead explode it. Can I read blocks first, use INSERT attributes to rewrite blocks to entities.

`msp = doc.modelspace()

for insert in msp.query('INSERT'): block = doc.blocks[insert.dxf.name] for e in block: msp.add_entity(e) msp.delete_entity(insert)`

I have tried the above way. Maybe something is wrong, it lost so many INSERT.
This problem is so hard. it bothers me for several days!

F397

mozman commented 2 years ago

Insert.explode(): https://ezdxf.mozman.at/docs/blocks/insert.html#ezdxf.entities.Insert.explode

Collect INSERT entities before exploding them, because exploding the entities alters the layout content:

inserts = msp.query("INSERT")
for insert in inserts:
    insert.explode()
mozman commented 2 years ago

Added a proof of concept for a Pillow backend:

F397

This image took only ~100 sec with not much memory usage, the backend draws direct to the canvas.

Backend: https://github.com/mozman/ezdxf/blob/master/src/ezdxf/addons/drawing/pillow.py Example: https://github.com/mozman/ezdxf/blob/master/examples/addons/drawing/pillow.py

Not ready for usage, see limitations:

vicky8989 commented 2 years ago

what an excellent news! it fits my requirements totally except text. it will be better includes text .Thanks so much!

vicky8989 commented 2 years ago

i use the commond:

python testPillow.py f397.dxf -o f397.png -i 1920,1080 -r 2 --dpi 300

I got a 11kb png file without any graphic...

is it a lib version problem?

mozman commented 2 years ago

The current state creates this:

f397

You have to keep your cloned repository updated!

vicky8989 commented 2 years ago

The current state creates this:

f397

You have to keep your cloned repository updated!

yes,i got the image after updating all codes! the cpu will also go 100% for seconds,the good news is it will not kill my computer, after 279s ,i got the image. there are some other things i need to research. such as text layer, style,faster ... 。 Thanks so much for helping!

image image

F397

mozman commented 2 years ago

The process in which the python interpreter is running will always reach 100%, the important part is not to run out of memory.

mozman commented 2 years ago

I tried to add text support but wasn't successful. Instead I added an option to draw a rectangle as text placeholder. The CADKitSample "Wood Details.dxf" looks like this:

Without placeholder: wood

With text placeholder: wood-placeholder

Your DXF with text placeholder: f397-text-placeholder

mbway commented 2 years ago

what doesn't work about your attempt at pillow text drawing? It looks like pillow supports drawing with ttf fonts so was it something like the sizing?

mozman commented 2 years ago

Pillow has only support for horizontal text. So you have to create the horizontal text in a separated image and then transform and paste the image which was very slow. And that's just the beginning... coordinate transformation (inverted y-axis), text sizing (you can't render a small text size and scale it by a large factor) and maybe more issues I don't want to bother with.

mozman commented 2 years ago

Added a (dead) branch "pillow-backend" to preserve the current state: it's slow and rotation doesn't work properly

This is "Wood Details.dxf" with 4x oversampling, which takes >105 seconds to render with text and <1 second without text:

wood

A test file with rotated text: text

But should look like this: image

mozman commented 2 years ago

Added PillowDelayedDraw() as 2nd backend.

This backend does not need to know the layout extents in advance. The layout extents are calculated on the fly while drawing commands are queued. The pending drawing commands are executed during image export.

This needs more memory but avoids the bounding box detection, measurements for F397.dxf (i7-12700K):

Backend Runtime Memory Usage
PillowBackend() incl. bbox detection ~85 seconds ~16 MiB
PillowDelayedDraw() ~70 seconds ~1088 MiB

If you know the layout region to export (e.g. (0, 0) to (1000, 1000)) and don't need the bounding box detection, the PillowBackend() is faster and uses less memory than the PillowDelayedDraw() backend.

mozman commented 2 years ago

Added text support by reusing the TextRenderer() from the matplotlib backend (branch: pillow-backend):

 python3 pillow.py '.\WOOD DETAILS.dxf' -o wood.png -r 6 -i 3840x2160

wood

This implementation renders the text as outline paths and slows the execution speed drastically if there is much text. The WOOD DETAILS.dxf sample file needs 9.4 seconds for rendering with text and 1.0 seconds for rendering with placeholders.

I will merge this changes into the main-branch and add a new command quickdraw to the ezdxf launcher.

ezdxf quickdraw '.\WOOD DETAILS.dxf' -o wood.png -r 6 -i 3840x2160

This command could be useful to create overviews or thumbnails of the modelspace. The default text mode will be text-placeholder to justify the term "quickdraw".

The remaining limitations are:

mozman commented 2 years ago

UPDATE for v0.18.1

ezdxf  pillow '.\WOOD DETAILS.dxf' -r3 -o wood.png

wood