2017-07-06 2 views
0

J'ai fait mon propre exportateur ply et même si cela fonctionne, il est assez lent. Je suppose que le problème vient de boucler spécifiquement sur les éléments et, basé sur this comment, d'utiliser struct.pack.Accélère l'écriture de données numériques dans un fichier binaire. générateur de plis

Le défi avec l'exportation Nodes est qu'il s'agit d'un vecteur 1D de tuples. Le défi avec l'exportation Faces est que chaque ligne de int s est précédée d'un nombre "3" au format uchar.

Je mets le code entier ici puisqu'il sert de MWE et aussi de référence si quelqu'un veut utiliser le code pour exporter des maillages au format ply.

code:

from __future__ import division 
import numpy as np 
import struct 
import timeit 

def Timeme(funct,var,NN=10,NNN=10): 
    for i in xrange(NN): 
     start =timeit.default_timer() 
     for t in xrange(NNN): 
      funct(*var) 
     end =timeit.default_timer() 
     print str(i)+': '+str((end - start)/NNN*1000) 

# This function is fictitious. In reality the 
# Nodes array is imported from another module 
def MakeNodes(Nr,Nc): 
    Nodes=np.zeros(Nr*Nc,dtype=[('x', np.float32), ('y', np.float32), ('z', np.float32)]) 
    x = np.linspace(0, (Nc-1), Nc, dtype=np.float32) 
    y = np.linspace((Nr-1),0 , Nr, dtype=np.float32) 
    xv, yv = np.meshgrid(x, y, sparse=False, indexing='xy') 
    Nodes['x']=xv.flatten() 
    Nodes['y']=yv.flatten() 
    Nodes['z']=(1/2-((Nodes['x']/Nc-1/2)**2+ (Nodes['y']/Nr-1/2)**2))*Nr/2 
    return Nodes 

# Function below explained in https://stackoverflow.com/questions/44934631 
def MakeFaces(Nr,Nc): 
    out = np.empty((Nr-1,Nc-1,2,3),dtype=int) 
    r = np.arange(Nr*Nc).reshape(Nr,Nc) 
    l1=r[:-1,:-1] 
    l2=r[:-1,1:] 
    l3=r[1:,:-1] 
    l4=r[1:,1:] 
    out[:,:, 0,0] = l2 
    out[:,:, 0,1] = l1 
    out[:,:, 0,2] = l3 
    out[:,:, 1,0] = l4 
    out[:,:, 1,1] = l2 
    out[:,:, 1,2] = l3 
    out.shape =(-1,3) 
    return out 

def ExportPlyBinary(Nodes,Faces,file): 
    LN=len(Nodes) 
    LF=len(Faces) 

    header= \ 
    "ply\n" \ 
    "format binary_little_endian 1.0\n" \ 
    "element vertex "+str(LN)+"\n" \ 
    "property float x\n" \ 
    "property float y\n" \ 
    "property float z\n" \ 
    "element face "+str(LF)+"\n" \ 
    "property list uchar int vertex_indices\n" \ 
    "end_header\n" 

    with open(file, 'wb') as fp: 
     fp.write(header) 
     s = struct.Struct('<fff') 
     for nd in Nodes: 
      fp.write(s.pack(nd['x'],nd['y'],nd['z'])) 

     s = struct.Struct('<Blll') 
     for fc in Faces: 
      fp.write(s.pack(3,fc[0],fc[1],fc[2])) 

Nr=200 
Nc=200 
Nodes=MakeNodes(Nr,Nc) 
Faces=MakeFaces(Nr,Nc) 
Timeme(ExportPlyBinary,(Nodes,Faces,"Test.ply")) 
Timeme(np.savez,("Test_np.ply", Nodes,Faces)) 

Résultat:

0: 366.3528
1: 386.216017627 
2: 383.307741944 
3: 359.598214393 
4: 363.434228045 
5: 397.255473919 
6: 433.967095136 
7: 407.806616677 
8: 393.701390596 
9: 379.542319143 
0: 15.5258007875 
1: 13.2543344563 
2: 12.8754439597 
3: 24.2303215372 
4: 15.9684973291 
5: 14.2023306048 
6: 13.7465456437 
7: 13.6964054484 
8: 21.27484093 
9: 13.2139143373 

Répondre

0

J'ai vérifié Trimesh comme suggéré par @ max9111 et était capable de créer une fonction plus efficace. Les idées clés (à mon sens) sont (1) la conversion au type et à l'ordre corrects de données en créant un nouveau tableau et (2) l'utilisation de la fonction .tostring. J'ai d'abord évité cette direction car elle semblait perdre de la mémoire mais à ce stade les avantages sont clairs. Notez que mon tableau Nodes est prêt à avoir .tostring appliqué, mais je pense que je garderais la solution plus générale.

def NewExportPlyBinary(Nodes,Faces,file): 
    LN=len(Nodes) 
    LF=len(Faces) 

    header= \ 
    "ply\n" \ 
    "format binary_little_endian 1.0\n" \ 
    "element vertex "+str(LN)+"\n" \ 
    "property float x\n" \ 
    "property float y\n" \ 
    "property float z\n" \ 
    "element face "+str(LF)+"\n" \ 
    "property list uchar int vertex_indices\n" \ 
    "end_header\n" 

    dtype_vertex = [('vertex', '<f4', (3))] 
    vertex = np.empty(LN, dtype=dtype_vertex) 
    vertex['vertex']=np.stack((Nodes['x'],Nodes['y'],Nodes['z']),axis=-1) 

    # vertex=Nodes 

    dtype_face = [('count', '<u1'),('index', '<i4', (3))] 
    faces = np.empty(LF, dtype=dtype_face) 
    faces['count'] = 3 
    faces['index'] = Faces 

    with open(file, 'wb') as fp: 
     fp.write(header) 
     fp.write(vertex.tostring()) 
     fp.write(faces.tostring()) 

Je reçois maintenant les heures suivantes pour le 200x200 exemple:

0: 373.361611377 # original ExportPlyBinary 
0: 20.5686725792 # numpy's savez 
0: 4.85469689001 # NewExportPlyBinary 

Note: la différence entre savez et NewExportPlyBinary disparaît essentiellement lorsque la taille du problème augmente.

1

Pourquoi ne pas utiliser ce qui est disponible?

Par exemple, les trimestres? https://pypi.python.org/pypi/trimesh

Même lorsque vous ne voulez qu'une petite partie de l'emballage, vous pouvez copier et ajuster cette pièce à partir de son code source. (avec mention des auteurs bien sûr)

+0

Nous vous remercions de votre suggestion. J'ai été capable d'obtenir une solution rapide en explorant les trimestres. – Miguel