#include <Mil.h>
#include "BestPlaneFitter.h"
#include <math.h>
static const MIL_DOUBLE SUB_FACTOR = 0.5;
static const MIL_INT SMOOTH_BORDER_SIZE = (MIL_INT)(10 * SUB_FACTOR);
static const MIL_INT DEPTH_SMOOTHNESS = 80;
static const MIL_INT BASIN_ERODE_ITER = 2;
static const MIL_INT MIN_VARIATION = 20;
static const MIL_DOUBLE FIT_OUTLIER_DISTANCE = 10;
static const MIL_INT FLOOR_MESH_SPACING = 100;
CBestPlaneFitter::CBestPlaneFitter(MIL_ID MilSystem, MIL_INT SrcImageSizeX, MIL_INT SrcImageSizeY)
: m_MilSystem(MilSystem),
m_MilBlobResult(M_NULL),
m_MilBlobFeatureList(M_NULL),
m_MilSubDepthMapImage(M_NULL),
m_MilBorderDepthMap(M_NULL),
m_MilSmoothDepthMap(M_NULL),
m_MilValidMask(M_NULL),
m_MilValidMask8(M_NULL),
m_MilAngleImage(M_NULL),
m_MilAngleEdgeImage(M_NULL),
m_MilLaplacianImage(M_NULL),
m_MilBasinsImage(M_NULL),
m_MilPlaneGeometry(M_NULL),
m_MilRotationMatrix(M_NULL),
m_Status(false),
m_NbChains(0),
m_MaxNbChains(0)
{
MblobAllocFeatureList(MilSystem, &m_MilBlobFeatureList);
MblobAllocResult(MilSystem, &m_MilBlobResult);
MblobSelectFeature(m_MilBlobFeatureList, M_AREA + M_SORT1_DOWN);
MblobSelectFeature(m_MilBlobFeatureList, M_NUMBER_OF_CHAINED_PIXELS);
MbufCreate2d(MilSystem, 3, 3, 32 + M_FLOAT, M_ARRAY, M_DEFAULT,
M_DEFAULT, (void*)m_RotationMatrix, &m_MilRotationMatrix);
M3dmapAlloc(MilSystem, M_GEOMETRY, M_DEFAULT, &m_MilPlaneGeometry);
MIL_INT SizeX = (MIL_INT)(SrcImageSizeX * SUB_FACTOR);
MIL_INT SizeY = (MIL_INT)(SrcImageSizeY * SUB_FACTOR);
MbufAlloc2d(MilSystem, SizeX, SizeY, 16 + M_UNSIGNED, M_IMAGE + M_PROC, &m_MilSubDepthMapImage);
MbufAlloc2d(MilSystem, SizeX, SizeY, 16 + M_UNSIGNED, M_IMAGE + M_PROC, &m_MilBorderDepthMap);
MbufAlloc2d(MilSystem, SizeX, SizeY, 32 + M_FLOAT , M_IMAGE + M_PROC, &m_MilSmoothDepthMap);
MbufAlloc2d(MilSystem, SizeX, SizeY, 16 + M_UNSIGNED, M_IMAGE + M_PROC, &m_MilValidMask);
MbufAlloc2d(MilSystem, SizeX, SizeY, 8 + M_UNSIGNED, M_IMAGE + M_PROC, &m_MilValidMask8);
MbufAlloc2d(MilSystem, SizeX, SizeY, 8 + M_UNSIGNED, M_IMAGE + M_PROC, &m_MilAngleImage);
MbufAlloc2d(MilSystem, SizeX, SizeY, 8 + M_UNSIGNED, M_IMAGE + M_PROC, &m_MilAngleEdgeImage);
MbufAlloc2d(MilSystem, SizeX, SizeY, 8 + M_UNSIGNED, M_IMAGE + M_PROC, &m_MilLaplacianImage);
MbufAlloc2d(MilSystem, SizeX, SizeY, 16 + M_UNSIGNED, M_IMAGE + M_PROC, &m_MilBasinsImage);
}
CBestPlaneFitter::~CBestPlaneFitter()
{
FreeWorldChains();
FreeFitImages();
M3dmapFree(m_MilPlaneGeometry);
MbufFree(m_MilRotationMatrix);
MblobFree(m_MilBlobResult);
MblobFree(m_MilBlobFeatureList);
}
void CBestPlaneFitter::FreeFitImages()
{
if(m_MilSubDepthMapImage)
{
MbufFree(m_MilSubDepthMapImage);
MbufFree(m_MilBorderDepthMap);
MbufFree(m_MilSmoothDepthMap);
MbufFree(m_MilValidMask);
MbufFree(m_MilValidMask8);
MbufFree(m_MilAngleImage);
MbufFree(m_MilAngleEdgeImage);
MbufFree(m_MilLaplacianImage);
MbufFree(m_MilBasinsImage);
m_MilSubDepthMapImage = M_NULL;
}
}
void CBestPlaneFitter::FreeWorldChains()
{
if(m_MaxNbChains)
{
delete [] m_pWorldChainZ;
delete [] m_pWorldChainY;
delete [] m_pWorldChainX;
m_MaxNbChains = 0;
}
}
bool CBestPlaneFitter::CalculateBestPlane(MIL_ID MilDepthMapImage)
{
m_Status = false;
MIL_DOUBLE OrgWorldPosX;
MIL_DOUBLE OrgWorldPosY;
MIL_DOUBLE OrgWorldPosZ;
MIL_DOUBLE OrgPixelSizeX;
MIL_DOUBLE OrgPixelSizeY;
MIL_DOUBLE OrgGrayLevelSizeZ;
McalInquire(MilDepthMapImage, M_WORLD_POS_X, &OrgWorldPosX);
McalInquire(MilDepthMapImage, M_WORLD_POS_Y, &OrgWorldPosY);
McalInquire(MilDepthMapImage, M_WORLD_POS_Z, &OrgWorldPosZ);
McalInquire(MilDepthMapImage, M_PIXEL_SIZE_X, &OrgPixelSizeX);
McalInquire(MilDepthMapImage, M_PIXEL_SIZE_Y, &OrgPixelSizeY);
McalInquire(MilDepthMapImage, M_GRAY_LEVEL_SIZE_Z, &OrgGrayLevelSizeZ);
MimResize(MilDepthMapImage, m_MilSubDepthMapImage, SUB_FACTOR, SUB_FACTOR, M_NEAREST_NEIGHBOR);
MIL_DOUBLE PixelSizeX = OrgPixelSizeX / SUB_FACTOR;
MIL_DOUBLE PixelSizeY = OrgPixelSizeY / SUB_FACTOR;
MIL_DOUBLE WorldPosX = OrgWorldPosX - (0.5 * OrgPixelSizeX);
MIL_DOUBLE WorldPosY = OrgWorldPosY - (0.5 * OrgPixelSizeY);
WorldPosX += (0.5 * PixelSizeX);
WorldPosY += (0.5 * PixelSizeY);
McalUniform(m_MilSubDepthMapImage, WorldPosX, WorldPosY, PixelSizeX, PixelSizeY, 0, M_DEFAULT);
McalControl(m_MilSubDepthMapImage, M_GRAY_LEVEL_SIZE_Z, OrgGrayLevelSizeZ);
McalControl(m_MilSubDepthMapImage, M_WORLD_POS_Z, OrgWorldPosZ);
MimBinarize(m_MilSubDepthMapImage, m_MilValidMask, M_FIXED + M_NOT_EQUAL, MIL_UINT16_MAX, M_NULL);
MimBinarize(m_MilSubDepthMapImage, m_MilValidMask8, M_FIXED + M_NOT_EQUAL, MIL_UINT16_MAX, M_NULL);
MimErode(m_MilValidMask, m_MilBorderDepthMap, 1, M_BINARY);
MimArith(m_MilValidMask, m_MilBorderDepthMap, m_MilBorderDepthMap, M_SUB);
MimArith(m_MilBorderDepthMap, m_MilSubDepthMapImage, m_MilBorderDepthMap, M_AND);
MimDilate(m_MilBorderDepthMap, m_MilBorderDepthMap, SMOOTH_BORDER_SIZE, M_GRAYSCALE);
MbufCopyCond(m_MilSubDepthMapImage, m_MilBorderDepthMap, m_MilSubDepthMapImage,
M_NOT_EQUAL, MIL_UINT16_MAX);
MimConvolve(m_MilBorderDepthMap, m_MilSmoothDepthMap, M_DERICHE_FILTER(M_SMOOTH, DEPTH_SMOOTHNESS));
MimEdgeDetect(m_MilSmoothDepthMap, M_NULL, m_MilAngleImage, M_SOBEL, M_REGULAR_EDGE_DETECT, M_NULL);
MimConvolve(m_MilSmoothDepthMap, m_MilLaplacianImage, M_LAPLACIAN_8);
MimShift(m_MilLaplacianImage, m_MilLaplacianImage, -1);
MimArith(m_MilAngleImage, m_MilValidMask8, m_MilAngleImage, M_AND);
MimEdgeDetect(m_MilAngleImage, m_MilAngleEdgeImage, M_NULL, M_SOBEL, M_DEFAULT, M_NULL);
MimArith(m_MilAngleEdgeImage, m_MilLaplacianImage, m_MilAngleEdgeImage, M_ADD+M_SATURATION);
MimWatershed(m_MilAngleEdgeImage, M_NULL, m_MilBasinsImage, MIN_VARIATION, M_BASIN);
MimArith(m_MilBasinsImage, m_MilValidMask, m_MilBasinsImage, M_AND);
MblobControl(m_MilBlobResult, M_BLOB_IDENTIFICATION, M_LABELED_TOUCHING);
MblobCalculate(m_MilBasinsImage, M_NULL, m_MilBlobFeatureList, m_MilBlobResult);
MIL_INT NbBlobs;
if(MblobGetNumber(m_MilBlobResult, &NbBlobs))
{
MIL_INT* Labels = new MIL_INT[NbBlobs];
MblobGetResult(m_MilBlobResult, M_LABEL_VALUE + M_TYPE_MIL_INT, &Labels[0]);
MblobSelect(m_MilBlobResult, M_INCLUDE_ONLY, M_LABEL_VALUE, M_EQUAL, (MIL_DOUBLE)Labels[0], M_NULL);
MblobFill(m_MilBlobResult, m_MilValidMask8, M_EXCLUDED_BLOBS, 0);
MimErode(m_MilValidMask8, m_MilValidMask8, BASIN_ERODE_ITER, M_BINARY);
M3dmapSetGeometry(m_MilPlaneGeometry, M_PLANE, M_FIT, (MIL_DOUBLE)m_MilSubDepthMapImage,
(MIL_DOUBLE)m_MilValidMask8, FIT_OUTLIER_DISTANCE, M_DEFAULT, M_DEFAULT);
MblobControl(m_MilBlobResult, M_BLOB_IDENTIFICATION, M_INDIVIDUAL);
MblobCalculate(m_MilValidMask8, M_NULL, m_MilBlobFeatureList, m_MilBlobResult);
MIL_INT SingleNbBlobs;
if(MblobGetNumber(m_MilBlobResult, &SingleNbBlobs))
{
if(SingleNbBlobs > NbBlobs)
{
delete [] Labels;
Labels = new MIL_INT[SingleNbBlobs];
}
MblobGetResult(m_MilBlobResult, M_LABEL_VALUE + M_TYPE_MIL_INT, &Labels[0]);
if(M3dmapInquire(m_MilPlaneGeometry, M_DEFAULT, M_STATUS, M_NULL) == M_SUCCESS)
{
M3dmapInquire(m_MilPlaneGeometry, M_DEFAULT, M_FIT_PARAM_AX, &m_Ax);
M3dmapInquire(m_MilPlaneGeometry, M_DEFAULT, M_FIT_PARAM_AY, &m_Ay);
M3dmapInquire(m_MilPlaneGeometry, M_DEFAULT, M_FIT_PARAM_Z0, &m_Z0);
CalculatePlaneRotationMatrix();
m_Status = GetDepthWorldChains(Labels[0]);
}
}
delete [] Labels;
}
return m_Status;
}
void CBestPlaneFitter::MoveWorldChains(MIL_ID MilCalibration,
MIL_INT SrcCoordinateSystem,
MIL_INT DstCoordinateSystem)
{
McalTransformCoordinate3dList(MilCalibration,
SrcCoordinateSystem,
DstCoordinateSystem,
m_NbChains,
m_pWorldChainX,
m_pWorldChainY,
m_pWorldChainZ,
m_pWorldChainX,
m_pWorldChainY,
m_pWorldChainZ,
M_DEFAULT);
}
void CBestPlaneFitter::MoveCoordinateSystemOnPlane(MIL_ID MilCalibration,
MIL_INT TargetCoordinateSystem,
MIL_INT ReferenceCoordinateSystem) const
{
McalSetCoordinateSystem(MilCalibration,
TargetCoordinateSystem,
ReferenceCoordinateSystem,
M_TRANSLATION + M_ASSIGN,
M_NULL,
0,
0,
m_Z0,
M_DEFAULT);
McalSetCoordinateSystem(MilCalibration,
TargetCoordinateSystem,
TargetCoordinateSystem,
M_ROTATION_MATRIX + M_COMPOSE_WITH_CURRENT,
m_MilRotationMatrix,
M_DEFAULT,
M_DEFAULT,
M_DEFAULT,
M_DEFAULT);
}
void CBestPlaneFitter::DrawPlaneInImage(MIL_ID MilGraContext,
MIL_ID MilImage,
MIL_INT TransparentColor) const
{
MIL_INT ImageSizeX = MbufInquire(MilImage, M_SIZE_X, M_NULL);
MIL_INT ImageSizeY = MbufInquire(MilImage, M_SIZE_Y, M_NULL);
MIL_DOUBLE MinX = m_pWorldChainX[0];
MIL_DOUBLE MinY = m_pWorldChainY[0];
MIL_DOUBLE MaxX = m_pWorldChainX[0];
MIL_DOUBLE MaxY = m_pWorldChainY[0];
for(MIL_INT ChainIdx = 1; ChainIdx < m_NbChains; ChainIdx++)
{
MinX = m_pWorldChainX[ChainIdx] < MinX ? m_pWorldChainX[ChainIdx] : MinX;
MinY = m_pWorldChainY[ChainIdx] < MinY ? m_pWorldChainY[ChainIdx] : MinY;
MaxX = m_pWorldChainX[ChainIdx] > MaxX ? m_pWorldChainX[ChainIdx] : MaxX;
MaxY = m_pWorldChainY[ChainIdx] > MaxY ? m_pWorldChainY[ChainIdx] : MaxY;
}
MIL_INT LinesMinX = ((MIL_INT)(MinX / FLOOR_MESH_SPACING)) * FLOOR_MESH_SPACING;
MIL_INT LinesMaxX = ((MIL_INT)(MaxX / FLOOR_MESH_SPACING)) * FLOOR_MESH_SPACING;
MIL_INT LinesMinY = ((MIL_INT)(MinY / FLOOR_MESH_SPACING)) * FLOOR_MESH_SPACING;
MIL_INT LinesMaxY = ((MIL_INT)(MaxY / FLOOR_MESH_SPACING)) * FLOOR_MESH_SPACING;
MIL_INT NbLinesX = (LinesMaxX - LinesMinX) / FLOOR_MESH_SPACING + 1;
MIL_INT NbLinesY = (LinesMaxY - LinesMinY) / FLOOR_MESH_SPACING + 1;
MIL_INT NbLines = NbLinesX + NbLinesY;
MIL_DOUBLE* pStartX = new MIL_DOUBLE[NbLines];
MIL_DOUBLE* pStartY = new MIL_DOUBLE[NbLines];
MIL_DOUBLE* pEndX = new MIL_DOUBLE[NbLines];
MIL_DOUBLE* pEndY = new MIL_DOUBLE[NbLines];
MIL_INT LineIdx = 0;
for(MIL_DOUBLE Y = (MIL_DOUBLE)LinesMinY; Y <= LinesMaxY; Y += FLOOR_MESH_SPACING, LineIdx++)
{
pStartX[LineIdx] = MinX;
pEndX[LineIdx] = MaxX;
pStartY[LineIdx] = Y;
pEndY[LineIdx] = Y;
}
for(MIL_DOUBLE X = (MIL_DOUBLE)LinesMinX; X <= LinesMaxX; X += FLOOR_MESH_SPACING, LineIdx++)
{
pStartX[LineIdx] = X;
pEndX[LineIdx] = X;
pStartY[LineIdx] = MinY;
pEndY[LineIdx] = MaxY;
}
MgraLines(MilGraContext, MilImage, NbLines, pStartX, pStartY, pEndX, pEndY, M_DEFAULT);
delete [] pEndY;
delete [] pEndX;
delete [] pStartY;
delete [] pStartX;
MIL_ID MilTransparentMask = MbufAllocColor(m_MilSystem, 3, ImageSizeX, ImageSizeY, 8+M_UNSIGNED, M_IMAGE+M_PROC, M_NULL);
MbufClear(MilTransparentMask, (MIL_DOUBLE)TransparentColor);
McalAssociate(MilImage, MilTransparentMask, M_DEFAULT);
MIL_INT MaskDrawColor = TransparentColor == 0 ? 1 : 0;
MgraColor(MilGraContext, (MIL_DOUBLE)MaskDrawColor);
MgraLines(MilGraContext, MilTransparentMask, m_NbChains, m_pWorldChainX, m_pWorldChainY, M_NULL, M_NULL, M_POLYGON+M_FILLED);
MbufCopyCond(MilTransparentMask, MilImage, MilTransparentMask, M_NOT_EQUAL, (MIL_DOUBLE)MaskDrawColor);
MbufFree(MilTransparentMask);
}
void CBestPlaneFitter::CalculatePlaneRotationMatrix()
{
MIL_DOUBLE PlaneNormLength = sqrt(1 + m_Ax*m_Ax + m_Ay*m_Ay);
MIL_DOUBLE Nz = 1 / PlaneNormLength;
MIL_DOUBLE Nx = -m_Ax / PlaneNormLength;
MIL_DOUBLE Ny = -m_Ay / PlaneNormLength;
MIL_DOUBLE PlaneXVecx = Nz;
MIL_DOUBLE PlaneXVecy = 0;
MIL_DOUBLE PlaneXVecz = -Nx;
MIL_DOUBLE PlaneXVecNorm = sqrt(PlaneXVecx*PlaneXVecx + PlaneXVecz*PlaneXVecz);
PlaneXVecx /= PlaneXVecNorm;
PlaneXVecz /= PlaneXVecNorm;
MIL_DOUBLE PlaneYVecx = (Ny * PlaneXVecz - PlaneXVecy * Nz);
MIL_DOUBLE PlaneYVecy = (PlaneXVecx * Nz - Nx * PlaneXVecz );
MIL_DOUBLE PlaneYVecz = (Nx * PlaneXVecy - PlaneXVecx * Ny );
m_RotationMatrix[0][0] = (MIL_FLOAT)PlaneXVecx;
m_RotationMatrix[1][0] = (MIL_FLOAT)PlaneXVecy;
m_RotationMatrix[2][0] = (MIL_FLOAT)PlaneXVecz;
m_RotationMatrix[0][1] = (MIL_FLOAT)PlaneYVecx;
m_RotationMatrix[1][1] = (MIL_FLOAT)PlaneYVecy;
m_RotationMatrix[2][1] = (MIL_FLOAT)PlaneYVecz;
m_RotationMatrix[0][2] = (MIL_FLOAT)Nx;
m_RotationMatrix[1][2] = (MIL_FLOAT)Ny;
m_RotationMatrix[2][2] = (MIL_FLOAT)Nz;
}
bool CBestPlaneFitter::GetDepthWorldChains(MIL_INT BlobLabel)
{
MblobGetResultSingle(m_MilBlobResult, BlobLabel, M_NUMBER_OF_CHAINED_PIXELS + M_TYPE_MIL_INT, &m_NbChains);
if(m_NbChains > 1)
{
if(m_NbChains > m_MaxNbChains)
{
FreeWorldChains();
m_pWorldChainX = new MIL_DOUBLE[m_NbChains];
m_pWorldChainY = new MIL_DOUBLE[m_NbChains];
m_pWorldChainZ = new MIL_DOUBLE[m_NbChains];
m_MaxNbChains = m_NbChains;
}
MblobGetResultSingle(m_MilBlobResult, BlobLabel, M_CHAIN_X, m_pWorldChainX);
MblobGetResultSingle(m_MilBlobResult, BlobLabel, M_CHAIN_Y, m_pWorldChainY);
McalTransformCoordinateList(m_MilSubDepthMapImage,
M_PIXEL_TO_WORLD,
m_NbChains,
m_pWorldChainX,
m_pWorldChainY,
m_pWorldChainX,
m_pWorldChainY);
for(MIL_INT ChainIdx = 0; ChainIdx < m_NbChains; ChainIdx++)
m_pWorldChainZ[ChainIdx] = m_Z0 + m_Ax * m_pWorldChainX[ChainIdx] + m_Ay * m_pWorldChainY[ChainIdx];
return true;
}
return false;
}