using System;
using System.IO;
using System.Text;
/*
BASED ON python script here
http://binarymillenium.googlecode.com/svn/trunk/processing/psynth/bin_to_csv.py
via blog posts
http://binarymillenium.com/2008/08/exporting-point-clouds-from-photosynth.html
http://binarymillenium.com/2008/08/photosynth-export-process-tutorial.html
and
http://getsatisfaction.com/livelabs/topics/pointcloud_exporter
An output file is written with a .PLY extension. It can be opened in Meshlab;
visit http://meshlab.sourceforge.net/ to download
Any errors are mine (http://conceptdev.blogspot.com). The colors don't seem right yet.
*/
namespace PointExtractor
{
//TODO: struct minmax { float x; float y; float z; float x1; float y1; float z1;}
class Program
{
static void Main(string[] args)
{
var output = new StringBuilder();
int rowCount = 0;
var baseFilename = args[0].Replace ("_0_0", "_0_{0}");
for (int f = 0; f < 50; f++)
{ // loop through up to 50 bin files
var filename = string.Format(baseFilename, f.ToString());
if (File.Exists(filename))
ReadPoints(filename, ref rowCount, ref output);
else
break;
}
// thanks to http://knele.myopenid.com/
string header = @"ply
format ascii 1.0
comment made by Knele
element vertex "+ rowCount +@"
property float x
property float y
property float z
property uchar red
property uchar green
property uchar blue
element face 0
property list uchar int vertex_index
end_header
";
output.Append("0 0 0 0"); // end of ply
File.WriteAllText(args[0]+".ply", header + output.ToString());
}
/// <summary>
/// def checkfloats(fbin):
/// for i in range(3):
/// if not((abs(fbin[i]) > 1e-10) and (abs(fbin[i]) < 1e6)): return False
/// return True;
/// </summary>
static bool checkFloats(byte[] fbin)
{
for (int i = 0; i < 3; i++)
{
float t = System.BitConverter.ToSingle(fbin, i*4);
if (
! ( (Math.Abs(t) > 1e-10)
&& (Math.Abs(t) < 1e6) )
)
return false;
}
return true;
}
/// <summary>
/// fbin = array.array('f')
/// fbin.fromfile(fraw, 3)
/// </summary>
static float f(byte[] b, int i)
{
var f1=new byte[4]{b[i],b[i+1],b[i+2],b[i+3]};
return BitConverter.ToSingle(f1, 0);
}
/// <summary>
/// bin = array.array('H')
/// bin.fromfile(fraw, 1)
/// </summary>
static short b(byte[] b)
{
var f1=new byte[2] {b[0],b[1]};
return BitConverter.ToInt16(f1, 0);
}
/// <summary>
/// 14 bytes long. The first 3 sets of 4 bytes are the xyz position in floating point values.
/// in python I had to do a byteswap on those bytes (presumably from network order) to get them to be read in right with the readfile command.
/// The last 2 bytes is the color of the point. It's only 4-bits per color channel,
/// </summary>
static void ReadPoints(string filename, ref int count, ref StringBuilder output)
{
using (BinaryReader fraw = new BinaryReader(File.Open(filename, FileMode.Open)))
{
int filesize = (int)fraw.BaseStream.Length;
var bsize = 14;
//HACK: i'm trimming and extra two off this calculation than the Python script does (- 2)
var offset = filesize - (Convert.ToInt32(filesize / bsize) - 1) * bsize - 2;
var bin = new byte[offset];
bin = fraw.ReadBytes(offset); // "use up" the initial bytes
int pos = sizeof(byte) * offset;
// get first chunk to check
bin = fraw.ReadBytes(14);
pos += sizeof(byte) * 14;
while (pos < filesize)
{
bin = byteswap(bin);
// Comment from Python
// # in multipart files the header is very huge, I'm not sure how to parse it
// # but I do know that it generates bad floats, so just screen for bad floats:
if (checkFloats(bin))
{
float t1 = f(bin, 0);
float t2 = f(bin, 4);
float t3 = f(bin, 8);
output.Append(String.Format("{0} {1} {2} ",t1,t2,t3)); // output xyz
short color = b(bin); // already did byteswap above
var red = (color >> 11) & 0x1f;
var green = (color >> 5) & 0x3f;
var blue = (color >> 0) & 0x1f;
red = red * 255 / 31;
green = green * 255 / 63;
blue = blue * 255 / 31;
output.Append(String.Format(" {0} {1} {2}\r\n", red, green, blue)); // output rgb
//output.Append(String.Format(" {0} {1} {2}\r\n"
// , red.ToString("X")
// , green.ToString("X")
// , blue.ToString("X")
// ));
count++;
//TODO: collect min/max
for (int j = 0; j < 3; j++)
{
//float m1 = System.BitConverter.ToSingle(fbin, j);
}
}
// get another 14 bytes
bin = fraw.ReadBytes(14);
pos += sizeof(byte) * 14;
}
// TODO: output min/max
}
}
/// <summary>
/// fbin.byteswap()
/// </summary>
private static byte[] byteswap(byte[] bin)
{
var b = new byte[bin.Length]; // obviously expects even number of bytes
for (int i = 0; i < bin.Length; i = i + 2)
{
b[i] = bin[i+1];
b[i+1] = bin[i];
}
return b;
}
}
}