3
$\begingroup$

Here is my target application. The rendered image with rendered meshes is used in an interactive application. A user may click or point the cursor to an object on the image. Information related to that object appears. So I need to know either

1) the coordinates of the polygon of the mesh silhouette on the rendered image

OR

2) pixels on the rendered image surrounded by the silhouette.

I noticed the mesh silhouette is drawn by default in the object mode. But how to find its coordinates from python for the rendered image?

For the 1) my idea was to use bpy_extras.object_utils.world_to_camera_view(..) to find image coordinates of each vertex of the mesh. Then somehow compute the silhouette. For the sake of simplicity, let's neglect transparency and consider that the mesh doesn't have holes. If you can propose a solution for a general case for a mesh with holes (e.g. a torus), that would be great!

Any hint is appreciated

$\endgroup$
11
  • $\begingroup$ see this blender.stackexchange.com/questions/882/… $\endgroup$
    – Chebhou
    Commented May 7, 2015 at 0:22
  • $\begingroup$ What is the purpose of this ? ( there maybe some easier ways to do it ) $\endgroup$
    – Chebhou
    Commented May 7, 2015 at 0:24
  • $\begingroup$ Here is my target application. The rendered image is used in an interactive application. A user may click or point the cursor to an object on the image. Information related to that object appears. So I need to know the coordinates of the polygon of the mesh outline on the rendered image. $\endgroup$
    – vvoovv
    Commented May 7, 2015 at 8:59
  • 1
    $\begingroup$ if you can just save this pass as one image and read the pixel value which would be the object index directly it would be easier $\endgroup$
    – Chebhou
    Commented May 7, 2015 at 13:00
  • 1
    $\begingroup$ it does not have to be bmp i just thought this format is easier , what do think ? you can create 2D array same size as the image and store obj ind in it for fast look-up ( for each frame ) $\endgroup$
    – Chebhou
    Commented May 7, 2015 at 13:09

4 Answers 4

3
$\begingroup$

You can use object index to identify to which object does the pixel belongs to, for this you have to :

  • assign unique object index to each object
  • enable the object index pass in the render layer

after that you can output this read the corresponding object for each point from this pass


in the following script we assign unique object index for each object and store this index and name in a dictionary :

import bpy

index = 0
obj_index = dict()

for obj in bpy.context.scene.objects :
    if obj.type == 'MESH' :
        index += 1
        obj.pass_index = index
        obj_index[obj.name] = index

print(obj_index)

this part is for extracting the object index pass from the node viewer image :

# division by 4 since we have RGBA
# object index is contained in any of R, G, B values
l = len(bpy.data.images['Viewer Node'].pixels)//4
table = [0 for x in range(l)] 
pixels = bpy.data.images['Viewer Node'].pixels
for i in range(l):
    table[i] = int(pixels[i*4])

print(table[:])

to make this work you have to setup the composition nodes as this :

enter image description here

$\endgroup$
7
  • $\begingroup$ How can I access the object index from the related image (IndexOB pass) via python? $\endgroup$
    – vvoovv
    Commented May 7, 2015 at 13:37
  • 1
    $\begingroup$ see this blender.stackexchange.com/questions/2170/… $\endgroup$
    – Chebhou
    Commented May 7, 2015 at 13:40
  • $\begingroup$ Why did you use the "Normalize" node? Working with raw values without any normalization is more convinient. $\endgroup$
    – vvoovv
    Commented May 7, 2015 at 19:49
  • $\begingroup$ i think the image will clip values more than one $\endgroup$
    – Chebhou
    Commented May 7, 2015 at 22:53
  • 1
    $\begingroup$ I assigned index 2000 (2 thousand) to an object. It was preserved in any of RGB values. $\endgroup$
    – vvoovv
    Commented May 8, 2015 at 0:06
2
$\begingroup$

I guess a custom Freestyle Python script could be a solution:

from freestyle.chainingiterators import *
from freestyle.predicates import *
from freestyle.shaders import *
from freestyle.types import *


class StrokePrinter(StrokeShader):
    def shade(self, stroke):
        for svert in stroke:
            print(svert.point)


upred = AndUP1D(QuantitativeInvisibilityUP1D(0), pyNatureUP1D(Nature.SILHOUETTE))
Operators.select(upred)
Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(upred))
shaders_list = [
    SamplingShader(5),
    ConstantThicknessShader(4),
    ConstantColorShader(0, 0, 0),
    StrokePrinter()
    ]
Operators.create(TrueUP1D(), shaders_list)

Copy and paste the script into the Text Editor in Blender. Change the Freestyle control mode from the Parameter Editor mode (default) to the Python Scripting mode. Add a style module and select the script from the pull-down menu. Render a still image, then in the console window you will see coordinates of silhouette lines (at every 5 pixels along the silhouette lines; change the parameter of the SamplingShader in the code to tune the interval).

Update: To retrieve the object name of a stroke:

class StrokePrinter(StrokeShader):
    def shade(self, stroke):
        sv1 = stroke[0].first_svertex
        sv2 = stroke[0].second_svertex
        fedge = sv1.get_fedge(sv2)
        print(fedge.viewedge.viewshape.name)
        for svert in stroke:
            print(svert.point)
$\endgroup$
5
  • $\begingroup$ Thank you for your script! Let's consider a complex example Freestyle example The torus on the image is partially hidden by the cube. How can one find the silhouettes of the cube and the torus? In the latter case the silhouette consists of two polygons. $\endgroup$
    – vvoovv
    Commented May 19, 2015 at 13:26
  • 1
    $\begingroup$ In that case you have 3 contour lines: One for the cube, one for the torus silhouette on the left, and another for the torus silhouette on the right. Using the Freestyle Python API, you can retrieve the name of the object to which each contour line belongs to. <pre><code> class StrokePrinter(StrokeShader): def shade(self, stroke): sv1 = stroke[0].first_svertex sv2 = stroke[0].second_svertex fedge = sv1.get_fedge(sv2) print(fedge.viewedge.viewshape.name) for svert in stroke: print(svert.point) </code></pre> $\endgroup$
    – T.K.
    Commented May 19, 2015 at 17:16
  • $\begingroup$ Hmm mini-Markdown does not seem to allow me to insert a formatted code block... Updated the answer to show the revised code. $\endgroup$
    – T.K.
    Commented May 19, 2015 at 17:27
  • $\begingroup$ Then I need to compose polygons out of stroke coordinates. Am I correct that there is no guarantee that a silhouette for each object is a closed polygon (without breaks)? $\endgroup$
    – vvoovv
    Commented May 19, 2015 at 18:38
  • 1
    $\begingroup$ No there is no guarantee that object silhouettes are a closed polygon. Check out the Freestyle SVG exporter add-on. It comes with an option to generate fills (closed polygons) out of selected feature lines. You could isolate silhouette lines (possibly together with object names) as illustrated above, and combine them with the SVG fills afterwards. wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.73/Freestyle $\endgroup$
    – T.K.
    Commented May 20, 2015 at 4:13
1
$\begingroup$

Here are the algorithms I gathered for the solution 1 (mesh silhouette as a polygon).

Keywords for search: mesh silhouette detection

Mesh silhouette is always displayed in the object mode. It's also calculated by Frestyle. But how to get the silhouette coordinates in the image space?

$\endgroup$
3
  • 1
    $\begingroup$ is this an edit to the question ? $\endgroup$
    – Chebhou
    Commented May 7, 2015 at 22:57
  • $\begingroup$ My task can have 2 solutions: 1) mesh silhouette as a polygon, 2) pixels surrounded by the silhouette. You helped me to elaborate solution 2). Still, I'd like to have something for solution 1) as well. $\endgroup$
    – vvoovv
    Commented May 8, 2015 at 0:10
  • $\begingroup$ Which edits would you suggest to make? I can't write the answer since I didn't study the algorithms in the links thoroughly. $\endgroup$
    – vvoovv
    Commented May 8, 2015 at 13:05
0
$\begingroup$

I might be misunderstanding, but you might be able to do a few simple manipulations to get the outline.

  1. Render with transparent film

enter image description here

  1. Use this node setup to get the edges of the meshes (the output of the 'greater than' thresholding node are your edges):

enter image description here

  1. Add the edges to the render:

enter image description here

$\endgroup$
2
  • $\begingroup$ I need a silhouette in the vector form (polygons) for each object in the scene. Your solution provides bitmap silhouette for the scene as a whole. But I tried your setup and learned something new. Thank you anyway! $\endgroup$
    – vvoovv
    Commented May 19, 2015 at 18:29
  • 1
    $\begingroup$ @vvoovv ahhh I knew I was missing something. FYI you might try checking out skimage.measure.find_contours and running it over your object index pass. $\endgroup$
    – ajwood
    Commented May 19, 2015 at 18:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .