Color and transparency for meshes

Questions about BrainVisa usage and installation

Moderators: denghien, riviere

Post Reply
Manik
Posts: 99
Joined: Fri Mar 05, 2004 1:00 pm
Location: INSERM U836, Grenoble

Color and transparency for meshes

Post by Manik »

Hello all,

is there a simple way (without textures or brainvisa scripts) to store the color and transparency properties of a mesh for Anatomist ?
Right now, I generate about 12 meshes, and each time I open them in Anatomist, I have to change the color of each one manually.
I thought maybe that kind of information can be stored in the associated .minf file of each .mesh...

Thanks in advance
Manik Bhattacharjee
INSERM U836
User avatar
Jean-Francois Mangin
Posts: 337
Joined: Mon Mar 01, 2004 10:24 am
Location: Neurospin, CEA, France
Contact:

Post by Jean-Francois Mangin »

Up to now, as far as I know, usual meshes do not have a
stored color. If the set of meshes you generate is the same for each
subject, the way to give them a color is to organize
them into a graph, and use their name to get automatically
a color from a model of your nomenclature described in a hierarchy.
This is what is done for the sulci for instance.
Anatomist can deal with new syntaxic types of graphs and
hierarchy without being recompiled. The new syntax of these graphs
are defined in ascii files in Anatomist "share" directories.
I know this solution may appear a bit complex, but I really
think this is the way to do it, because it will give you
all kind of new possibilities, for instance manipulating
several brains at the same time throughout simple selections
of some names in the hierarchy defining your nomenclature.
Manik
Posts: 99
Joined: Fri Mar 05, 2004 1:00 pm
Location: INSERM U836, Grenoble

Post by Manik »

I see there is some documentation about hierarchies and arg files (http://brainvisa.info/doc/html/anatomis ... graph.html), so I will try to understand how I can use it.
Thank you for the answer !
Manik Bhattacharjee
INSERM U836
User avatar
Jean-Francois Mangin
Posts: 337
Joined: Mon Mar 01, 2004 10:24 am
Location: Neurospin, CEA, France
Contact:

Post by Jean-Francois Mangin »

If you want an example simpler than the sulci,
there is a hierarchy somewhere for deep nuclei,
and a couple of brains with the nucleus-based graphs.
Maybe we even gave them to Eric one day...
You may visit us one of these days to see some demo
about the graph-based capacities of Anatomist.
In my opinion, this is the most powerful part.

PS: I am not sure the hierarchy system, which once again is
what I think should be done, can already deal with transparency.
One day it certainly will :-)
User avatar
riviere
Site Admin
Posts: 1361
Joined: Tue Jan 06, 2004 12:21 pm
Location: CEA NeuroSpin, Saint Aubin, France
Contact:

Post by riviere »

Hi,

If you want to make a structured set of objects with consistent colors on many subjects, the graph solution is certainly the best, althrough building the graph itself may cause you some headaches.
For episodic work or testing purpose, the idea of providing a default color in the .minf header is perhaps not so bad... We could discuss it in our internal "feature and design commitee" (hum that is, Jeff and me) to decide if it would be an interesting feature, or an inacceptable increase of entropy that would allow everyone to do anything in a general mess :wink:.

Denis
Manik
Posts: 99
Joined: Fri Mar 05, 2004 1:00 pm
Location: INSERM U836, Grenoble

Post by Manik »

Ok, I think I will try to use the graphs.
By the way, is there a documentation about the creation of a .hie and associated .arg files ?
If not, I will first try to understand the nuclei example.

About the .minf file, I just wanted to use it for my tests on one subject, so the entropy wouldn't have increased too much :)
Manik Bhattacharjee
INSERM U836
User avatar
riviere
Site Admin
Posts: 1361
Joined: Tue Jan 06, 2004 12:21 pm
Location: CEA NeuroSpin, Saint Aubin, France
Contact:

Post by riviere »

Sorry, this turns out to be a very technical developers topic...

The hierarchy format is rather simple, you will understand it by looking at one example.
The graph format is also simple in appearance, but as it can hold many different things under different shapes, it has become quite complex.
The two simplest ways of building a graph with meshes inside are:
a) "by hand", by writing a suitable .arg ASCII file. This is the "quick & dirty" way because if the graph format changes it will not work anymore (or it will write an obsolete format)
b) in a C++ program using the graph and aims APIs. This is the best way but requires knowledge of C++ and of our libraries.

a) The simplest option is to take an example or a "template" file and to duplicate node sections and change filenames in it. Here is an example:

Code: Select all

*BEGIN GRAPH RoiArg
RoiArg_VERSION  1.0
filename_base   .
roi.tri         roi Tmtktri_filename
type.tri        roi.tri
boundingbox_max 255 255 123
boundingbox_min 0 0 0
voxel_size      1.01562 1.01562 1.1

*BEGIN NODE roi 1
Tmtktri_filename	toto_Mesh_0.mesh
name something
*END

*END
b) C++ code:

Code: Select all

// WARNING: I didn't test this code...
#include <graph/graph/graph.h>
#include <aims/graph/graphmanip.h>
#include <aims/surface/surface.h>
// in Aims 2.14+, use <aims/mesh/surface.h> instead
#include <vector>

using namespace aims;
using namespace std;

Graph* buildgraph( const vector<AimsSurfaceTriangle> & meshes )
{
  Graph *g = new Graph( "RoiArg" );
  // global graph attributes
  vector<float>  vs(3);
  vectot<int>     bbmin(3), bbmax(3);
  vs[0] = 1;  // voxel size
  vs[1] = 1;
  vs[2] = 1;
  g->setAttribute( "voxel_size", vs );
  bbmin[0] = bbmin[1] = bbmin[2] = 0;
  g->setAttribute( "boundingbox_min", bbmin );
  bbmax[0] = 255; // in voxels
  bbmax[1] = 255;
  bbmax[2] = 123;
  g->setAttribute( "boundingbox_max", bbmax );

  // make nodes
  unsigned  i, n = meshes.size();
  for( i=0; i<n; ++i )
  {
    // create the node
    Vertex *v = g->addVertex( "roi" );
    // set its name attribute (you can change the value)
    v->setAttribute( "name", string( "unknown" ) );
    //  store the mesh in the vertex
    GraphManip::storeAims( *g, v, "aims_Tmtktri", meshes[i] );
  }
  return g;
}

int main()
{
  // make or read meshes
  vector<AimsSurfaceTriangle> meshes;
  // ....

  // build graph
  Graph *g = buildgraph( meshes );
  // save it
  Writer<Graph>  w( "toto.arg" );
  w.write( *g );
  // cleanup
  delete g;
}
For the colors in .minf, if we enable it for you, others will use it, of course (like myself to begin with) and after a little while there will be files with colors everywhere. Anyway the idea seems cool to me, we may adopt it...

Denis
Manik
Posts: 99
Joined: Fri Mar 05, 2004 1:00 pm
Location: INSERM U836, Grenoble

Post by Manik »

As I am writing a C++ program, the code you gave is really useful to me.
But I just have one problem right now, concerning the insertion of meshes in graphs.

The following line :

Code: Select all

GraphManip::storeAims( *g, v, "aims_Tmtktri", meshes[i] ); 
does not compile.
I noticed that this function is a template function, but I have absolutely no idea of what type I am supposed to give it, as its declaration is the following :

Code: Select all

    template<typename T>
    static void storeAims( Graph & graph, GraphObject* vertex,
                           const std::string & attribute,
                           carto::rc_ptr<T> obj );
In fact, only the carto::rc_ptr argument is the problem : everything else is easily casted in the needed types. I tried to put
storeAims<AimsSurfaceTriangle> (**same args**)
but that didn't work.

Then I tried to use a rc_ptr object, but I definitely don't get it : I finely compiled the program using this (awful) code

Code: Select all

    AimsSurfaceTriangle m = meshes[i];
    AimsSurfaceTriangle*ast_p = &m;
    rc_ptr<AimsSurfaceTriangle> rcp (ast_p);
    GraphManip::storeAims<AimsSurfaceTriangle>( *g, (GraphObject*)v, string("aims_Tmtktri"), rcp );
but I just got a segfault...

I suppose there is a much simpler way to get this working for an Anatomist guru :)
Manik Bhattacharjee
INSERM U836
User avatar
riviere
Site Admin
Posts: 1361
Joined: Tue Jan 06, 2004 12:21 pm
Location: CEA NeuroSpin, Saint Aubin, France
Contact:

Post by riviere »

Hi Manik,

You're right, my program was wrong. The rc_ptr<> is a reference counting pointer. It's normal you get a segfault in your example program, because there you build a rc_ptr on an object locally allocated. rc_ptr takes ownership of the object (here a mesh) and destroys it when no rc_ptr point to it anymore: here it will be deleted twice.
You can either modify my example prog so it takes as input, not a vector<AimsSurfaceTriangle> but a vector<rc_ptr<SimsSurfaceTriangle> >, or you can do as you did in your program with a slight modif:

Code: Select all

// make a full copy of the mesh
AimsSurfaceTriangle *m = new AimsSurfaceTriangle( meshes[i] );
// give it to a ref-counting pointer
rc_ptr<AimsSurfaceTriangle> rcp = rc_ptr<AimsSurfaceTriangle>( m );
// don't delete m now: rcp owns it.
GraphManip::storeAims( *g, v, string("aims_Tmtktri"), rcp );
I hope this code will work better (I didn't test it either...)

Anyway a command to do it in a general case would be useful, so I have to really make this small program as a "AimsMeshes2Graph" command or something similar.

Denis
User avatar
riviere
Site Admin
Posts: 1361
Joined: Tue Jan 06, 2004 12:21 pm
Location: CEA NeuroSpin, Saint Aubin, France
Contact:

Post by riviere »

Hi Manik,

As the work was almost done in the previous messages, I put the code in a new command (AimsMeshes2Graph as I said just before) that will be available in the next version of Aims. I know that you won't wait, so here is the source (this one has been tested and works):

Code: Select all

/*
 *  Copyright (C) 2004 CEA
 *
 *  This software and supporting documentation were developed by
 *  	CEA/DSV/SHFJ
 *  	4 place du General Leclerc
 *  	91401 Orsay cedex
 *  	France
 *
 */

#include <aims/getopt/getopt2.h>
#include <aims/io/reader.h>
#include <aims/io/writer.h>
#include <aims/def/path.h>
#include <aims/graph/graphmanip.h>
#include <aims/mesh/surface.h>
#include <graph/graph/graph.h>

using namespace carto;
using namespace aims;
using namespace std;


int main( int argc, const char **argv )
{
  vector<string>	filein;
  string		fileout;
  AimsApplication	app( argc, argv, "Makes a graph from a set of meshes" 
			     );

  app.addOption( fileout, "-o", "output data graph" );
  app.alias( "-output", "-o" );
  app.addOptionSeries( filein, "-i", "input meshes filenames" );
  app.alias( "-input", "-i" );

  try
    {
      app.initialize();

      Graph		g( "RoiArg" );

        // global graph attributes
      vector<float>	vs(3);
      vector<int>	bbmin(3), bbmax(3);
      bool		firstmesh = true;
      vs[0] = 1;  // voxel size
      vs[1] = 1;
      vs[2] = 1;
      g.setAttribute( "voxel_size", vs );
      g.setAttribute( "filename_base", string( "*" ) );

      // make nodes
      vector<string>::const_iterator	im, em = filein.end();
      for( im=filein.begin(); im!=em; ++im )
        {
          rc_ptr<AimsSurfaceTriangle>	mesh( new AimsSurfaceTriangle );
          Reader<AimsSurfaceTriangle>	mr( *im );
          mr.read( *mesh );
          // create the node
          Vertex *v = g.addVertex( "roi" );
          // set its name attribute (you can change the value)
          v->setAttribute( "name", string( "unknown" ) );
          //  store the mesh in the vertex
          GraphManip::storeAims( g, v, "aims_Tmtktri", mesh );

          // scan extrama
          mesh->setMini();
          mesh->setMaxi();
          Point3df	m = mesh->minimum();
          if( firstmesh || m[0] < bbmin[0] )
            bbmin[0] = (int) floor( m[0] );
          if( firstmesh || m[1] < bbmin[1] )
            bbmin[1] = (int) floor( m[1] );
          if( firstmesh || m[2] < bbmin[2] )
            bbmin[2] = (int) floor( m[2] );
          m = mesh->maximum();
          if( firstmesh || m[0] > bbmax[0] )
            bbmax[0] = (int) ceil( m[0] );
          if( firstmesh || m[1] > bbmax[1] )
            bbmax[1] = (int) ceil( m[1] );
          if( firstmesh || m[2] > bbmax[2] )
            bbmax[2] = (int) ceil( m[2] );
          firstmesh = false;
         }
      g.setAttribute( "boundingbox_min", bbmin );
      g.setAttribute( "boundingbox_max", bbmax );

      // write
      Writer<Graph>	gw( fileout );
      gw.write( g );

      return EXIT_SUCCESS;
    }
  catch( user_interruption &e )
    {
    }
  catch( exception & e )
    {
      cerr << e.what() << endl;
      return( 1 );
    }

  return EXIT_FAILURE;
}
Denis
Manik
Posts: 99
Joined: Fri Mar 05, 2004 1:00 pm
Location: INSERM U836, Grenoble

Post by Manik »

Ok, thanks a lot for the information.
I still don't understand everything about graphs, but I was able to create a hierarchy and make a program which imports my data (as a xml file) and generates a graph corresponding to the hierarchy.
One last question : is it possible to store the transparency in the hierarchy ? (Jean-François Mangin said he wasn't sure)
I tried to add a number to the color property (e.g. color 200 50 50 125), but it doesn't seem to work.
Is it a planned feature in Anatomist ?
Anyway, I now have enough clues to go on with my program ;)
Manik Bhattacharjee
INSERM U836
User avatar
riviere
Site Admin
Posts: 1361
Joined: Tue Jan 06, 2004 12:21 pm
Location: CEA NeuroSpin, Saint Aubin, France
Contact:

Post by riviere »

Hi Manik,
Up to now, colors in hierarchies are only RGB, not RGBA. This is just that we never needed transparency here before. I can change it for the next version. So, well, yes, now that you've talked about it, it is a planned feature.
Denis
User avatar
riviere
Site Admin
Posts: 1361
Joined: Tue Jan 06, 2004 12:21 pm
Location: CEA NeuroSpin, Saint Aubin, France
Contact:

Post by riviere »

Hi Manik,

Two little things:

1. It seems that providing RGBA colors in hierarchies just work fine, I just tried it and obtained some transparency in both stable and development versions. Doesn't it work for you ?

2. I've added in anatomist (development branch) the possibility to provide materials in the .minf header of objects (meshes...) so they are automatically colored at load time. The format is the following:

Code: Select all

attributes = {
  'material' : {
    'diffuse' : [ 0.8, 0.8, 0.3, 0.7 ],
    'ambient' : [ 0.5, 0, 0 ], 
    'emission' : [ 0.2, 0.2, 0.7 ], 
    'specular' : [ 0, 1, 0 ], 
    'shininess' : 128, 
  }
}
All attributes are optional (you can only specify 'diffuse' for instance) and colors can be RGB or RGBA (3 or 4 components). So it's quite identical to the processor command.

Denis
Manik
Posts: 99
Joined: Fri Mar 05, 2004 1:00 pm
Location: INSERM U836, Grenoble

Post by Manik »

Hi Denis,

thanks a lot,

I realize I did not try the alpha channel on the right node of the hierarchy, that is why I thought it was not working...
Thanks for the added functionnality in .minf !
Manik Bhattacharjee
INSERM U836
Post Reply