0

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.

  • If your data is already binary. Then remove `img.compression='rle' img.type='bilevel' img.depth = 1` and try that or try adding just `img.compression='fax'` or `img.compression='group4'` – fmw42 Nov 17 '20 at 00:59
  • Although the data is already binary, it is stored in 8-bit int type. If I just remove the bilevel image type and single bit depth, the resulting image still has 8-bit depth. The fax and group4 compressions produce the desired output and file size is much smaller! Thanks for this suggestion. However the processing time is still about 30 seconds in the save function now. – Matt Dodd Nov 17 '20 at 19:13

0 Answers0