I'm just starting to understand the Python syntax and I created a module that does what I wanted, but really slow.
Here are the stats of cProfile, top 10 ordered by internal time
:
ncalls tottime percall cumtime percall filename:lineno(function) 1291716 45.576 0.000 171.672 0.000 geometry.py:10(barycentric_coords) 6460649 31.617 0.000 31.617 0.000 {numpy.core.multiarray.array} 2583432 15.979 0.000 15.979 0.000 {method 'reduce' of 'numpy.ufunc' objects} 2031 12.032 0.006 193.333 0.095 geometry.py:26(containing_tet) 1291716 10.944 0.000 58.323 0.000 linalg.py:244(solve) 1291716 7.075 0.000 7.075 0.000 {numpy.linalg.lapack_lite.dgesv} 1291716 5.750 0.000 9.865 0.000 linalg.py:99(_commonType) 2583432 5.659 0.000 5.659 0.000 {numpy.core.multiarray._fastCopyAn dTranspose} 1291716 5.526 0.000 7.299 0.000 twodim_base.py:169(eye) 1291716 5.492 0.000 12.791 0.000 numeric.py:1884(identity)
To process data and create a 24 bitmap of 300*300 pixels it would take about 1 1/2 hour on my laptop with the latest intel i5, a SSD disk and 12gb RAM!
I am not sure if it's a good idea to post all the code, but if not how can you get the entire picture?
The first thing colors_PPM.py
does is to access a function called geometry
.
geometry.py
itself calls tetgen
, tetgen.py
calls tetgen.exe
.
The module colors_PPM.py
reads a list in targets.csv
and a list in XYZcolorlist_D65.csv
, it excludes some elements of XYZcolorlist_D65.csv
and then reads one by one the rows of targets.csv
, performs a delaunay triangulation via tetgen
and returns 4 names[]
and 4 bcoords[]
.
Then random
is used to choose one name
by a series of if
elif
else
tests.
Finally the result is exported in a bitmap file epson_gamut.pbm
.
Do you see any way this could run faster? I know it should seem quite a mess.
.csv
files structures examples:
'XYZcolorlist_D65.csv'
255 63 127,35.5344302104,21.380721966,20.3661095969 255 95 127,40.2074945517,26.5282949405,22.7094284437 255 127 127,43.6647438365,32.3482625492,23.6181801523 255 159 127,47.1225628354,39.1780944388,22.9366615044 255 223 159,61.7379149646,62.8387601708,32.3936200864 255 255 159,70.7428790853,78.6134546144,29.5579371353 255 0 127,32.0951763469,18.3503537537,19.0863164396 255 31 127,32.281389139,18.5592317077,18.6802029444 255 191 127,52.6108977261,48.5621713952,21.7645428218 255 223 127,59.7600830083,60.9770436618,20.9338174593 ...
'targets.csv'
30,5,3 30.34,5,3 30.68,5,3 31.02,5,3 31.36,5,3 31.7,5,3 32.04,5,3 32.38,5,3 32.72,5,3 33.06,5,3 33.4,5,3 33.74,5,3 ...
This is geometry.py
:
#geometry.py import numpy as np import numpy.linalg as la import tetgen def barycentric_coords(vertices, point): T = (np.array(vertices[:-1])-vertices[-1]).T v = np.dot(la.inv(T), np.array(point)-vertices[-1]) v.resize(len(vertices)) v[-1] = 1-v.sum() return v def tetgen_of_hull(points): tg_all = tetgen.TetGen(points) hull_i = set().union(*tg_all.hull) hull_points = [points[i] for i in hull_i] tg_hull = tetgen.TetGen(hull_points) return tg_hull, hull_i def containing_tet(tg, point): for i, tet in enumerate(tg.tets): verts = [tg.points[j] for j in tet] bcoords = barycentric_coords(verts, point) if (bcoords >= 0).all(): return i, bcoords return None, None
This is tetgen.py
:
#tetgen import tempfile, subprocess, os class TetGen: def __init__(self, points): self.points = points node_f = tempfile.NamedTemporaryFile(suffix=".node", delete=False); node_f.write("%i 3 0 0\n" % len(points)) for i, point in enumerate(points): node_f.write("%i %f %f %f\n" % (i, point[0], point[1], point[2])) node_f.close() subprocess.call(["tetgen", node_f.name], stdout=open(os.devnull, 'wb')) #subprocess.call(["C:\Users\gary\Documents\eclipse\Light Transformer Setup\tetgen", node_f.name], stdout=open(os.devnull, 'wb')) ele_f_name = node_f.name[:-5] + ".1.ele" face_f_name = node_f.name[:-5] + ".1.face" ele_f_lines = [line.split() for line in open(ele_f_name)][1:-1] face_f_lines = [line.split() for line in open(face_f_name)][1:-1] self.tets = [map(int, line[1:]) for line in ele_f_lines] self.hull = [map(int, line[1:]) for line in face_f_lines] if __name__ == '__main__': from pprint import pprint points = [(0,0,0),(0,0,1),(0,1,0),(0,1,1),(1,0,0),(1,0,1),(1,1,0),(1,1,1)] tg = TetGen(points) pprint(tg.tets) pprint(tg.hull)
And this is the module colors_PPM.py
:
import geometry import csv import numpy as np import random import cv2 S = 0 img = cv2.imread("MAP.tif", -1) height, width = img.shape ppm = file("epson gamut.ppm", 'w') ppm.write("P3" + "\n" + str(width) + " " + str(height) + "\n" + "255" + "\n") # PPM file header all_colors = [(name, float(X), float(Y), float(Z)) for name, X, Y, Z in csv.reader(open('XYZcolorlist_D65.csv'))] tg, hull_i = geometry.tetgen_of_hull([(X,Y,Z) for name, X, Y, Z in all_colors]) colors = [all_colors[i] for i in hull_i] print ("thrown out: " + ", ".join(set(zip(*all_colors)[0]).difference(zip(*colors)[0]))) targets = ((float(X), float(Y), float(Z)) for X, Y, Z in csv.reader(open('targets.csv'))) for target in targets: X, Y, Z = target target_point = (np.array([X,Y,Z])) tet_i, bcoords = geometry.containing_tet(tg, target_point) if tet_i == None: ppm.write(str("255 255 255") + "\n") continue # not in gamut else: A = bcoords[0] B = bcoords[1] C = bcoords[2] D = bcoords[3] R = random.uniform(0,1) names = [colors[i][0] for i in tg.tets[tet_i]] if R <= A: S = names[0] elif R <= A+B: S = names[1] elif R <= A+B+C: S = names[2] else: S = names[3] ppm.write(str(S) + "\n") print "done" ppm.close()
tetgen_of_hull
? Or is it the later loop over thetargets
? As Janne says, it would be worth starting by learning to use the profiler.\$\endgroup\$geometry.py
\$\endgroup\$