I am working on a python script which converts DXF files from AutoCAD into high resolution bi-level TIFF files using the MagickWand bindings library. The script works exactly as expected, but I am looking for ways to improve the performance.
Here is the script:
import dxfgrabber
import cv2
import numpy as np
import sys
from wand.image import Image
RESOLUTION_DPI = 1000
def drawPolyLines(pl):
if(pl.is_closed):
points = []
for pt in pl.points:
points += [[xToPix(pt[0]),yToPix(pt[1])]]
points = np.array(points,np.int32)
cv2.fillPoly(canvas,[points],0,cv2.LINE_8)
dxf = dxfgrabber.readfile("812b.dxf")
shapes = dxf.entities.get_entities()
in_limMin = dxf.header['$LIMMIN']
in_limMax = dxf.header['$LIMMAX']
pix_limMin = tuple([int(z * RESOLUTION_DPI) for z in in_limMin])
pix_limMax = tuple([int(z * RESOLUTION_DPI) for z in in_limMax])
#Translate x,y values to pixels
def xToPix(coord):
return int((coord*RESOLUTION_DPI))
def yToPix(coord, ymax=pix_limMax[1]):
return int(ymax - (coord*RESOLUTION_DPI))
canvas = np.zeros((pix_limMax[1],pix_limMax[0]), np.uint8)
canvas.fill(1)
for shape in shapes:
if shape.dxftype == 'POLYLINE':
drawPolyLines(shape)
with Image.from_array(canvas) as img:
img.resolution = RESOLUTION_DPI
img.compression='rle'
img.type='bilevel'
img.depth = 1
img.save(filename='result.tif')
It takes about 30 seconds to convert one file and I am looking to improve this. A cProfile sorted by cumulative time shows the following:
$ python -m cProfile -s 'cumtime' test.py
324966 function calls (322147 primitive calls) in 37.550 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
488/1 0.001 0.000 37.557 37.557 {built-in method builtins.exec}
1 0.000 0.000 37.557 37.557 test.py:1(<module>)
3 0.000 0.000 31.180 10.393 image.py:1013(wrapped)
1 30.500 30.500 30.500 30.500 image.py:2451(type)
1 4.329 4.329 4.329 4.329 image.py:9251(save)
1 1.485 1.485 1.487 1.487 image.py:8754(from_array)
1 0.681 0.681 0.681 0.681 image.py:1629(depth)
16 0.001 0.000 0.446 0.028 __init__.py:1(<module>)
208/5 0.002 0.000 0.229 0.046 <frozen importlib._bootstrap>:986(_find_and_load)
207/5 0.001 0.000 0.228 0.046 <frozen importlib._bootstrap>:956(_find_and_load_unlocked)
197/6 0.001 0.000 0.225 0.038 <frozen importlib._bootstrap>:650(_load_unlocked)
160/6 0.000 0.000 0.225 0.037 <frozen importlib._bootstrap_external>:777(exec_module)
290/6 0.000 0.000 0.221 0.037 <frozen importlib._bootstrap>:211(_call_with_frames_removed)
330/3 0.001 0.000 0.179 0.060 {built-in method builtins.__import__}
198/23 0.000 0.000 0.173 0.008 <frozen importlib._bootstrap>:1017(_handle_fromlist)
197/58 0.000 0.000 0.161 0.003 <frozen importlib._bootstrap>:549(module_from_spec)
22/3 0.000 0.000 0.159 0.053 <frozen importlib._bootstrap_external>:1099(create_module)
22/3 0.029 0.001 0.159 0.053 {built-in method _imp.create_dynamic}
1 0.000 0.000 0.158 0.158 __init__.py:41(readfile)
1 0.000 0.000 0.158 0.158 __init__.py:52(readfile_as_asc)
1 0.000 0.000 0.116 0.116 __init__.py:61(_read_encoded_file)
1 0.110 0.110 0.110 0.110 {method 'fill' of 'numpy.ndarray' objects}
As you can see, almost the entire run time is spent in the resulting call of setting the image type to bilevel. I've dug into the MagickWand source a bit and found a dead-end when the SetImageType()
method is called from within MagickImage.MagickSetImageType()
. I'm wondering if there is a thresholding operation which is converting every pixel to a single bit value. This is probably time consuming, and pointless in this case since the values of the matrix are only 0 or 1. Unfortunately, the opencv draw methods require 8bit integer type and I do not know if there is another data type I can feed into MagickWand that will improve performance. Looking for any suggestions to speed up this operation. Thanks.