How to improve performance of bi-level image type in MagickWand Python?

96 Views Asked by At

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.

0

There are 0 best solutions below