1
$\begingroup$

I'm aware there are numerous posts/questions already been raised on this, but upon reading the previous posts, I'm not quite able to find exactly what I'm looking for. So apologies if there's a duplicate post that already answered this. Basically, I have a CSV file with 3 columns = x coordinate, y coordinate and pixel value (based on 255 greyscale). All I want to do is import this CSV file in Blender and plot the points as vertices, using python script. That's it. So if there are 3 points in the CSV file, I want those 3 points created/plotted in their respective xy coordinates as vertices in Blender. I've seen examples of python scripts for adding mesh objects. But I simply want to plot the points given the xy coordinates in the CSV file, as vertices. Not add existing 3D objects/meshes.

This is the code I have so far:

import bpy
import csv

with open(r'file path\img_pixel_coor.csv') as csvfile:
    csvread = csv.reader(csvfile)
    for i, row in enumerate (csvread):
        print("i = "+str(i)+". row = "+str(row))

This prints:

i = 0. row = ['494', '216', '86']
i = 1. row = ['494', '217', '86']
i = 2. row = ['494', '218', '86']
i = 3. row = ['494', '219', '85']
i = 4. row = ['494', '220', '84']
i = 5. row = ['494', '221', '87']
i = 6. row = ['494', '222', '87']
i = 7. row = ['494', '223', '88']
i = 8. row = ['494', '224', '92']

The print is just to show what's in the file (logging purposes).

Also, would it be possible to put this script in a python file, and run it from a python file, without having to manually open Blender, i.e. can you control externally from a python file, to open Blender and run the script, without having to actually open Blender software manually yourself?

Any help would be greatly appreciated.

Update:

Here's a sample of how the CSV file looks like in the first few rows, to demonstrate how the x, y and pixel values are structured:

enter image description here

Finally, how do I smooth the vertices more. As you can see from the image below, the surface of the mesh is very spikey. I used the code below, but it's still very rough/spikey surface.

for p in ob.data.polygons: #Set smooth shading (if needed)
    p.use_smooth = True

enter image description here

Many thanks in advance for any help.

Illustration (my understanding):

of the code:

# Vertices store the color values as Z
vertices = [Vector( (x, y, int(z_max * random.random())) ) for x, y in product( range(x_size), range(y_size) )]

# Get base face indices
face_basis = ((x * y_size) + y for (x, y) in product( range(x_size-1), range(y_size-1) ))

# Each face is a square for the base index turning around counterclockwise
faces = [(v, v + 1, v + y_size + 1, v + y_size) for v in face_basis]

enter image description here

$\endgroup$
  • 1
    $\begingroup$ See here blender.stackexchange.com/questions/76535/… $\endgroup$ – lemon Sep 10 at 6:03
  • $\begingroup$ Thank you for your response and referring your answer. Really appreciated. I have a question. In the code, I think it treats the 3rd column of the csv file, i.e. float(r[2]) as the z coordinates. However, the 3rd column, in the csv file, is in fact the greyscale pixel value (I think it's also referred to as the brightness intensity value) 0-255. So is there a way to plot this in Blender as such, rather than as the z coordinate? $\endgroup$ – Hazzaldo Sep 10 at 18:33
  • $\begingroup$ Vertex colors in Blender are stored as face loop. That means you can not assign a vertex color if there is no face (nearly the same for materials). So here you'll need some rules to make it colored. Is for instance (x,y) map a regular grid? $\endgroup$ – lemon Sep 11 at 6:36
  • $\begingroup$ Apologies, I'm a bit junior in Blender. I'm not sure if I understood your question correctly. I'll try to answer based on how I interpreted it. I'll show you a sample of how the csv file starts in the first few rows, so you can see if x,y map to a regular grid. Basically when plotting the data from the csv it forms into a rectangular frame. But the content on the inside of that rectangular frame I base it on the third column pixel value. Actually, looking at it, I'm happy to go along with plotting the third column pixel value as the z coor, as it provides the depth. However ... $\endgroup$ – Hazzaldo Sep 11 at 20:11
  • $\begingroup$ I'm still wondering how to use the same third column (representing 0-255) to use its values as the material colours? I've update my question above to show how the first rows of the CSV look like in terms of x, y and pixel values. Hope this helps in answering your question. $\endgroup$ – Hazzaldo Sep 11 at 20:14
2
$\begingroup$

I suppose the input data is a 2D grid with known max values.

Here, we start with a random input but seeing your question you know how to load data from a file.

The script below does the following:

  • Create random inputs (you'll use file data instead)
  • Create vertices and store the color value as Z coordinate
  • Create the face vertex indices
  • Make a mesh object from that
  • Scale the object along Z
  • Create a vertex colors layer
  • Assign the Z value of the vertices as vertex color
  • Link a prepared material to the object

That gives the following result:

enter image description here

import bpy
import bmesh
from mathutils import Vector, Matrix
from itertools import product
import random

# Generate a random grid of the given file
x_size = 20
y_size = 30
z_max = 256

# Vertices store the color values as Z
vertices = [Vector( (x, y, int(z_max * random.random())) ) for x, y in product( range(x_size), range(y_size) )]

# Get base face indices
face_basis = ((x * y_size) + y for (x, y) in product( range(x_size-1), range(y_size-1) ))

# Each face is a square for the base index turning around counterclockwise
faces = [(v, v + 1, v + y_size + 1, v + y_size) for v in face_basis]

# Create resulting object
name = "grid"
mesh = bpy.data.meshes.new( name ) 
obj = bpy.data.objects.new( name, mesh )

obj.data.from_pydata( vertices, [], faces ) 

# Scale it along Z
scale = 1.0 / z_max # Or set 0.0 here
obj.matrix_world = Matrix.Scale( scale, 4, (0, 0, 1) ) @ obj.matrix_world

# Smooth faces
for face in obj.data.polygons: 
    face.use_smooth = True

# Get or create vertex colors data of the given name
vcol_name = "VCol"
vcol = obj.data.vertex_colors.get( vcol_name )
if not vcol:
    vcol = obj.data.vertex_colors.new( name = vcol_name )

# Assigns z values as vertex colors
for face in obj.data.polygons:
    for vertex_index, loop_index in zip( face.vertices, face.loop_indices ):
        intensity = obj.data.vertices[vertex_index].co.z / z_max
        vcol.data[loop_index].color = (intensity, intensity, intensity, 1)

# Get the prepared material
material_name = "Material"
material = bpy.data.materials.get( material_name )

# If found assign it to the object
if material:
    obj.data.materials.append( material )

# Link the object to the scene
bpy.context.scene.collection.objects.link( obj )

The material is the following:

It uses the created vertex color as input for a color ramp and use this color for shading:

enter image description here

As your question was about plotting vertices, you may want to rework the mesh manually or modify the script to have this kind of results:

enter image description here

Note:

An illustration for

# Vertices store the color values as Z
vertices = [Vector( (x, y, int(z_max * random.random())) ) for x, y in product( range(x_size), range(y_size) )]

# Get base face indices
face_basis = ((x * y_size) + y for (x, y) in product( range(x_size-1), range(y_size-1) ))

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.