Jump to content

Need help with Python -> VEX


Ole

Recommended Posts

Hi there.

 

I need some help with Vex. I've set up a Python Sop with the code below but this is very slow. So my next challenge is to re-create it in vex (or fix the Python code if it can run much faster). Vex is unknown territory, and my vex code fail already with the attempt to create an array with unique cluster values ('clusters' in the Python code). When I foreach() on the array to look up the existing values within the while loop, VEX execution seems to go into an infinite loop if I have a printf() statement anywhere in the code. I've put the vex code in a Attribute Wrangle Sop with Run Over set to Detail (to be able to count over points).

 

My working Python code:

node = hou.pwd()
geo = node.geometry()

# Add code to modify contents of geo.
# Use drop down menu to select examples.

geo.addAttrib(hou.attribType.Point, "activate_by_cluster", 0)
#geo.addAttrib(hou.attribType.Point, "Cd", (0.0, 0.0, 0.0))

# Set threshold for number of points in a cluster before becoming active
min_points = 40

clusters = []
for p in geo.iterPoints():
    cluster = p.attribValue("cluster");
    if cluster not in clusters:
        clusters.append(cluster)

for cl in clusters:
    active_cluster = False
    threshold = 0
    for p in geo.iterPoints():
        if p.attribValue("cluster") == cl and p.attribValue("activate_box") == 1:
            threshold += 1
        if threshold >= min_points:
            active_cluster = True
            break

    if active_cluster == True:
        for p in geo.iterPoints():
            if p.attribValue("cluster") == cl:
                p.setAttribValue("activate_by_cluster", 1)

This is the vex code I got so far: 

//Global settings
int min_points = 400;

//Global variables
int clusters[] = {0};

//Get the existing clusters from the points
int pt_num = 0;
while(pt_num < @numpt){
    int _cluster = point(geoself(), "cluster", pt_num);
    foreach(int i; clusters){
        if(_cluster != i){
            push(clusters, _cluster);
            }
        }
    pt_num++;
    }

//printf("%d", clusters);

foreach(int i; clusters){
    int active_cluster = 0;
    int threshold = 0;
    while(threshold < @numpt){
        int _cluster = point(geoself(), "cluster", pt_num);
        int _activate_box = point(geoself(), "activate_box", pt_num);
        if(_cluster == i){
            if(_activate_box == 1){
                threshold++;
                }
            }
        }
        
        if(threshold >= min_points){
            active_cluster = 1;
            break;
            }
            
    if (active_cluster == 1){
        pt_num = 0;
        while(pt_num < @numpt){
            int _cluster = point(geoself(), "cluster", pt_num);
            if(_cluster == i){
                //setpointattrib(geoself(),"activate_by_chunk", pt_num, 1, "set");
                }
            }
        }       
    }

I've just tried to do the same in vex as I did in Python. Needless to say, it does not work as it is although I get no error messages.

 

In case the intention is unclear: I am looping over the current points looking for the cluster attribute (coming from Cluster Points sop). I then go through the points in each cluster looking for the "activate_box" attribute. If this is 1 and a minimum of points with these conditions is found, set all points in the cluster to have the "activate_by_chunk" attribute to 1. This is used downstream.

 

Please let me know if you have any ideas!

 

Ole

 

Link to comment
Share on other sites

I can't really help much with trying this in VEX, but there are definitely some things in your Python code that aren't going to help with performance:

 

Using hou.Geometry.iterPoints() is highly efficient for random access into the point list, however is generally slightly slower than just points() when you are straight up iterating over all the points in the range.  This will only be exasperated since you are doing loops within loops.

 

You are accessing and setting attribute values by name instead of using a reference to a hou.Attrib object.  Every time you set by name Houdini as to look up the attribute to get the reference internally. This can cause a bunch of slowdown the more entities you have.

 

All the constant iteration over large geometry sets will definitely cause a lot of slowdown.  Here is something I think should probably be faster.  It tries to avoid actually dealing with Houdini points and tries to just perform operations on lists of attribute values which can be accessed, manipulated and set back more efficiently.

 

 

Here's some test code that should probably be faster but is untested.

min_points = 40
 
attr = geo.addAttrib(hou.attribType.Point, "activate_by_cluster", 0)
 
# Get all the point cluster and activation values.
clusters = geo.pointIntAttribValues("cluster")
abb = geo.pointIntAttribValues("activate_by_box")
 
# A list of cluster value counts. This assumes your clusters are indexed 0-N and have continuous value ranges.
active = [0] * len(set(clusters))
 
# Build a simple list of the matching counts.
for cluster, val in zip(clusters, abb):
    if val:
        active[cluster] += 1
 
# Determine which clusters actually need to be activated.
mask = [int(val > min_points) for val in active]
 
# Build a list of the activate_by_cluster values.
to_set = []
for cluster in clusters:
    to_set.append(mask[cluster])
 
# Set the values to the points.
geo.setPointIntAttribValues("activate_by_cluster", to_set)
  • Like 1
Link to comment
Share on other sites

untested vex version...

putting this into an attribWrangle set to DETAIL should work. it´s most probably not much faster (if it is at all) than grahams python code since it doesn´t use multithreading.

int     num_cluster, num_points, num, i, p, min_points, count;

min_points = 40;

num_cluster = nuniqueval(@OpInput1, "point", "cluster");
for(i = 0; i < num_cluster; i++)
{
    num_points = findattribvalcount(@OpInput1, "point", "cluster", i);

    count = 0; p = 0;
    while(p < num_points && count < min_points + 1)
    {
        num = findattribval(@OpInput1, "point", "cluster", i, p++);
        if(point(@OpInput1, "activate_box", num) == 1)
            count += 1;
    }

    if(count > min_points)
    {
        p = 0;
        while(p < num_points)
        {
            num = findattribval(@OpInput1, "point", "cluster", i, p++);
            setpointattrib(geoself(), "activate_by_cluster", num, 1, "set");
        }
    }
}

edit: "activate_by_cluster" must be an existing attribute.

Edited by petz
  • Like 1
Link to comment
Share on other sites

two point wrangles:

 

wrangle 1:

v@clusterP = 0;
v@clusterP.x = i@cluster;
v@clusterP.y = i@activate_box;

wrangle 2:

int activate;
int handle = pcopen("op:`opinputpath('.',0)", "clusterP", v@clusterP, 0.001, 10000000);
 
if (pcnumfound(handle)>40)
    activate = 1;
 
addattribute("activate_by_cluster",activate);

the first wrangle sets up a point cloud search proxy using the "cluster" and "activate_box" int attributes.  the second uses that proxy to search for like points (ie, same cluster value and same activate_box value).

 

(this was dry coded, so there may be a syntax error or two)

Edited by fathom
Link to comment
Share on other sites

two point wrangles:

 

wrangle 1:

v@clusterP = 0;
v@clusterP.x = i@cluster;
v@clusterP.y = i@activate_box;

wrangle 2:

int activate;
int handle = pcopen("op:`opinputpath('.',0)", "clusterP", v@clusterP, 0.001, 10000000);
 
if (pcnumfound(handle)>40)
    activate = 1;
 
addattribute("activate_by_cluster",activate);

the first wrangle sets up a point cloud search proxy using the "cluster" and "activate_box" int attributes.  the second uses that proxy to search for like points (ie, same cluster value and same activate_box value).

 

(this was dry coded, so there may be a syntax error or two)

 

Thank you for the example, Miles!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...