git-svn-id: http://moon:8086/svn/software/trunk/libsrc/_to_be_added@1 b431acfa-c32f-4a4a-93f1-934dc6c82436
419 lines
10 KiB
C
Executable File
419 lines
10 KiB
C
Executable File
// ------------------------------------------------------------
|
|
// clob.c
|
|
//
|
|
// A color quantizer using binary-split technique
|
|
// From:
|
|
// "Color Quantization of Images", Orchard, Bouman
|
|
// IEEE Trans. on Sig. Proc., vol. 39, no. 12, pp. 2677-2690, Dec. 1991.
|
|
//
|
|
// 10.03.2005, J.Ahrensfeld
|
|
// ------------------------------------------------------------
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <sys/time.h>
|
|
#include "bintree.h"
|
|
#include "mutil.h"
|
|
#include "lapack_cwrap.h"
|
|
#include "clob.h"
|
|
|
|
// ------------------------------------------------------------
|
|
void CodebookAlloc(codebook_t *pObj, int N_max, int M_max)
|
|
{
|
|
int i;
|
|
|
|
pObj->pColor = (color_vector_t*)malloc(M_max*sizeof(color_vector_t));
|
|
pObj->size_C = (int*)malloc(M_max*sizeof(int));
|
|
memset(pObj->size_C, 0, M_max*sizeof(int));
|
|
pObj->pC = (int*)malloc(N_max*sizeof(int));
|
|
pObj->ppC = (int**)malloc(M_max*sizeof(int*));
|
|
|
|
pObj->ppC[0] = pObj->pC;;
|
|
for (i=1; i < M_max; i++)
|
|
pObj->ppC[i] = NULL;
|
|
|
|
pObj->max_colors = M_max;
|
|
pObj->max_samples = N_max;
|
|
pObj->num_colors = 0;
|
|
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
void CodebookFree(codebook_t *pObj)
|
|
{
|
|
int i;
|
|
|
|
if(!pObj)
|
|
return;
|
|
|
|
free(pObj->pC);
|
|
free(pObj->ppC);
|
|
free(pObj->pColor);
|
|
free(pObj->size_C);
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
void ColorQuant_Init(colorquant_t *pCQ, int N_max, int M_max)
|
|
{
|
|
M_max++;
|
|
pCQ->N_max = N_max;
|
|
pCQ->M_max = M_max;
|
|
|
|
pCQ->pRootData = (node_data_t*)malloc(sizeof(node_data_t));
|
|
memset(pCQ->pRootData, 0, sizeof(node_data_t));
|
|
|
|
pCQ->pData = (node_data_t*)malloc(2*M_max*sizeof(node_data_t));
|
|
memset(pCQ->pData, 0, 2*M_max*sizeof(node_data_t));
|
|
|
|
pCQ->pTemp = (double*)malloc(N_max*sizeof(double));
|
|
pCQ->pXs = (color_vector_t*)malloc(N_max*sizeof(color_vector_t));
|
|
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
void ColorQuant_Free(colorquant_t *pCQ)
|
|
{
|
|
int m;
|
|
|
|
FreeNodeData(pCQ->pRootData);
|
|
for (m=0; m < 2*pCQ->M_max; m++)
|
|
{
|
|
FreeNodeData(&pCQ->pData[m]);
|
|
}
|
|
free(pCQ->pRootData);
|
|
pCQ->pRootData = NULL;
|
|
free(pCQ->pData);
|
|
pCQ->pData = NULL;
|
|
free(pCQ->pXs);
|
|
free(pCQ->pTemp);
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
void FreeNodeData(node_data_t *pObj)
|
|
{
|
|
if(pObj)
|
|
{
|
|
if(pObj->pC)
|
|
{
|
|
free(pObj->pC);
|
|
pObj->pC = NULL;
|
|
}
|
|
if(pObj->pExpr_left)
|
|
{
|
|
free(pObj->pExpr_left);
|
|
pObj->pExpr_left = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
void ColorNodeStat(colorquant_t *pCQ, node_data_t *pObj, color_vector_t *pXs)
|
|
{
|
|
int i, j, s, info;
|
|
|
|
if (pObj->N == 0)
|
|
{
|
|
fprintf(stderr, "ColorNodeStat(): N is zero!\n");
|
|
return;
|
|
}
|
|
|
|
// Calc node statistics
|
|
// R = sum_s(xs_s*xs_s') ; mit s = Element aus Menge C
|
|
memset(pObj->R, 0, ROW_DIM*ROW_DIM*sizeof(double));
|
|
dsyrk('U', 'N', ROW_DIM, pObj->N, 1.0, (double*)pXs, ROW_DIM, 1.0, (double*)pObj->R, ROW_DIM);
|
|
// PrintMatrix("R=",(double*)pObj->R, ROW_DIM, ROW_DIM);
|
|
|
|
// m = sum_s(xs_s)
|
|
memset(pObj->m, 0, ROW_DIM*sizeof(double));
|
|
for(i=0; i < pObj->N; i++)
|
|
{
|
|
pObj->m[0] += pXs[i][0];
|
|
pObj->m[1] += pXs[i][1];
|
|
pObj->m[2] += pXs[i][2];
|
|
|
|
}
|
|
// PrintMatrix("m=",(double*)pObj->m, ROW_DIM, 1);
|
|
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
double ColorLambda(colorquant_t *pCQ, node_data_t *pObj, color_vector_t *pXs)
|
|
{
|
|
|
|
int i, s, info, lwork;
|
|
color_vector_t d = {0};
|
|
double *pWork;
|
|
double *pDiff, Ls;
|
|
|
|
if (pObj->N == 0)
|
|
{
|
|
fprintf(stderr, "ColorLambda(): N is zero!\n");
|
|
pObj->lambda = 0;
|
|
return 0;
|
|
}
|
|
|
|
pObj->pExpr_left = (double*)malloc(pObj->N*sizeof(double));
|
|
|
|
// q = m/N;
|
|
memcpy(pObj->q, pObj->m, sizeof(color_vector_t));
|
|
dscal (ROW_DIM, 1.0/pObj->N, pObj->q, 1);
|
|
// PrintMatrix("q=",(double*)pObj->q, ROW_DIM, 1);
|
|
|
|
// cov = R - 1/N*m*m'
|
|
memcpy(pObj->cov, pObj->R, ROW_DIM*sizeof(color_vector_t));
|
|
dsyr('U', ROW_DIM, -1.0/pObj->N, pObj->m, 1, (double*)pObj->cov, ROW_DIM);
|
|
// PrintMatrix("Cov=",(double*)pObj->cov, ROW_DIM, ROW_DIM);
|
|
|
|
// Query dsyev() for optimal work buffer size
|
|
pWork = pCQ->pTemp;
|
|
pWork[0] = 0;
|
|
info = dsyev('V', 'U', ROW_DIM, (double*)NULL, ROW_DIM, NULL, pWork, -1);
|
|
lwork = (int)pWork[0];
|
|
|
|
// d = eig(cov)
|
|
info = dsyev('V', 'U', ROW_DIM, (double*)pObj->cov, ROW_DIM, d, pWork, lwork);
|
|
if (info != 0)
|
|
{
|
|
fprintf (stderr, "Eigenvalue solver failure with error %d\n", info);
|
|
printf("N=%d\n", pObj->N);
|
|
PrintMatrix("Cov=",(double*)pObj->cov, ROW_DIM, ROW_DIM);
|
|
}
|
|
|
|
// PrintMatrix("d=",(double*)d, ROW_DIM, 1);
|
|
// PrintMatrix("V=",(double*)pObj->cov, ROW_DIM, ROW_DIM);
|
|
|
|
// Form unit-vector e in direction of max. element in d
|
|
dcopy (ROW_DIM, d, 1, pObj->e, 1);
|
|
GetPrincipleEigenVector((double*)pObj->cov, (double*)pObj->e, ROW_DIM);
|
|
// PrintMatrix("e=",(double*)pObj->e, ROW_DIM, 1);
|
|
|
|
// -------------------------
|
|
// Compute Lambda
|
|
|
|
// Expr. left: dot(xs,e) = e'*xs_s = xs_s'*e
|
|
// Also used later in ColorSplit()
|
|
dgemv('T', ROW_DIM, pObj->N, 1.0, (double*)pXs, ROW_DIM, pObj->e, 1, 0.0, pObj->pExpr_left, 1);
|
|
|
|
// Expr. right: dot(q,e) = e'*q = q'*e
|
|
// Also used later in ColorSplit()
|
|
pObj->expr_right = ddot(ROW_DIM, pObj->q, 1, pObj->e, 1);
|
|
|
|
// lambda = sum_s((xs_s - q)'*e).^2);
|
|
pObj->lambda = 0;
|
|
for (i=0; i < pObj->N; i++)
|
|
{
|
|
// xs_s'*e - q'*e
|
|
Ls = pObj->pExpr_left[i] - pObj->expr_right;
|
|
pObj->lambda += Ls*Ls;
|
|
}
|
|
return pObj->lambda;
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
void ColorNodeSplit(colorquant_t *pCQ, node_data_t *pObj, node_data_t *pData2n, node_data_t *pData2n1)
|
|
{
|
|
int N2n, N2n1, s, i;
|
|
|
|
memset(pData2n, 0, sizeof(node_data_t));
|
|
memset(pData2n1, 0, sizeof(node_data_t));
|
|
|
|
pData2n->pC = (int*)malloc(pObj->N*sizeof(int));
|
|
if(!pData2n->pC)
|
|
{
|
|
fprintf(stderr, "pData2n->pC = NULL for N=%d\n", pObj->N);
|
|
}
|
|
|
|
pData2n1->pC = (int*)malloc(pObj->N*sizeof(int));
|
|
if(!pData2n1->pC)
|
|
{
|
|
fprintf(stderr, "pData2n1->pC = NULL for N=%d\n", pObj->N);
|
|
}
|
|
|
|
// Split into two new sets
|
|
N2n = 0;
|
|
N2n1 = 0;
|
|
for (i=0; i < pObj->N; i++)
|
|
{
|
|
s = pObj->pC[i];
|
|
|
|
if(pObj->pExpr_left[i] > pObj->expr_right)
|
|
{
|
|
pData2n1->pC[N2n1] = s;
|
|
N2n1++;
|
|
}
|
|
else
|
|
{
|
|
pData2n->pC[N2n] = s;
|
|
N2n++;
|
|
}
|
|
}
|
|
|
|
// Compute statistics of two new nodes
|
|
// Node(2n)
|
|
pData2n->N = N2n;
|
|
for (i=0; i < N2n; i++)
|
|
{
|
|
s = pData2n->pC[i];
|
|
pCQ->pXs[i][0] = pCQ->pX[s][0];
|
|
pCQ->pXs[i][1] = pCQ->pX[s][1];
|
|
pCQ->pXs[i][2] = pCQ->pX[s][2];
|
|
}
|
|
|
|
ColorNodeStat(pCQ, pData2n, pCQ->pXs);
|
|
ColorLambda(pCQ, pData2n, pCQ->pXs);
|
|
|
|
// Node(2n+1)
|
|
pData2n1->N = N2n1;
|
|
for(i=0; i < ROW_DIM; i++)
|
|
{
|
|
pData2n1->R[i][0] = pObj->R[i][0] - pData2n->R[i][0];
|
|
pData2n1->R[i][1] = pObj->R[i][1] - pData2n->R[i][1];
|
|
pData2n1->R[i][2] = pObj->R[i][2] - pData2n->R[i][2];
|
|
}
|
|
|
|
for(i=0; i < ROW_DIM; i++)
|
|
pData2n1->m[i] = pObj->m[i] - pData2n->m[i];
|
|
|
|
for (i=0; i < N2n1; i++)
|
|
{
|
|
s = pData2n1->pC[i];
|
|
pCQ->pXs[i][0] = pCQ->pX[s][0];
|
|
pCQ->pXs[i][1] = pCQ->pX[s][1];
|
|
pCQ->pXs[i][2] = pCQ->pX[s][2];
|
|
}
|
|
ColorLambda(pCQ, pData2n1, pCQ->pXs);
|
|
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
int ColorQuant(colorquant_t *pCQ, codebook_t *pCB, color_vector_t *pX, int K, int N, int M, double lambda_min)
|
|
{
|
|
int i, m, *pC, bst_state, s, sum_pixel, codebook_size, size, *ptr, *ptr_next;
|
|
node_data_t *pData, *pData2n, *pData2n1;
|
|
node_t *node_max, *node_temp;
|
|
bintree_t bst;
|
|
|
|
if (N == 0)
|
|
{
|
|
fprintf(stderr, "ColorQuant(): N is zero!\n");
|
|
return 0;
|
|
}
|
|
|
|
pCQ->nRow = K;
|
|
pCQ->nCol = N;
|
|
|
|
pCQ->pRootData->N = N;
|
|
pCQ->pX = (color_vector_t*)pX;
|
|
|
|
pCQ->pRootData->pC = (int*)malloc(N*sizeof(int));
|
|
|
|
// Create root data set
|
|
for(i=0; i < N; i++)
|
|
{
|
|
pCQ->pRootData->pC[i] = i; // store indices of vector boundaries
|
|
}
|
|
|
|
ColorNodeStat(pCQ, pCQ->pRootData, pCQ->pX);
|
|
ColorLambda(pCQ, pCQ->pRootData, pCQ->pX);
|
|
|
|
// Create root-node
|
|
bintree_init(&bst, 2*N);
|
|
|
|
bintree_insert(&bst, pCQ->pRootData->lambda, pCQ->pRootData);
|
|
|
|
for (m=0; m < M-1; m++)
|
|
{
|
|
node_max = bintree_GetNodeMax(&bst);
|
|
if (!node_max)
|
|
{
|
|
fprintf(stderr,"node_max = NULL!\n");
|
|
break;
|
|
}
|
|
|
|
if (node_max->value < lambda_min)
|
|
break;
|
|
|
|
pData2n = &pCQ->pData[2*m];
|
|
pData2n1 = &pCQ->pData[2*m+1];
|
|
ColorNodeSplit(pCQ, (node_data_t*)node_max->pData, pData2n, pData2n1);
|
|
if (pData2n->N > 0)
|
|
{
|
|
bintree_insert(&bst, pData2n->lambda, pData2n);
|
|
}
|
|
else
|
|
{
|
|
FreeNodeData(pData2n);
|
|
}
|
|
|
|
if (pData2n1->lambda > 0)
|
|
{
|
|
bintree_insert(&bst, pData2n1->lambda, pData2n1);
|
|
}
|
|
else
|
|
{
|
|
FreeNodeData(pData2n1);
|
|
}
|
|
|
|
// if ((pData2n->lambda == 0) || (pData2n1->lambda == 0))
|
|
// {
|
|
// continue;
|
|
// }
|
|
|
|
node_temp = bintree_node_remove(&bst, node_max);
|
|
if (node_temp == bst.pRoot)
|
|
{
|
|
break;
|
|
}
|
|
FreeNodeData((node_data_t*)node_max->pData);
|
|
|
|
if (!bst.pRoot)
|
|
break;
|
|
|
|
bst_state = bintree_isBST(bst.pRoot);
|
|
if (!bst_state)
|
|
{
|
|
fprintf(stderr,"Binary Tree is not BST!\n");
|
|
}
|
|
}
|
|
|
|
// bintree_print(pCQ->root);
|
|
// printf("\n");
|
|
sum_pixel = 0;
|
|
codebook_size = 0;
|
|
pCB->ppC[0] = pCB->pC;
|
|
do
|
|
{
|
|
node_max = bintree_GetNodeMax(&bst);
|
|
if (!node_max)
|
|
{
|
|
fprintf(stderr,"node_max = NULL!\n");
|
|
}
|
|
// printf("Lambda=%5.5g\n",node_max->value);
|
|
|
|
size = ((node_data_t*)node_max->pData)->N;
|
|
pCB->size_C[codebook_size] = size;
|
|
ptr = pCB->ppC[codebook_size];
|
|
ptr_next = ptr + size;
|
|
sum_pixel += size;
|
|
memcpy(ptr,((node_data_t*)node_max->pData)->pC, size*sizeof(int));
|
|
memcpy(pCB->pColor[codebook_size],((node_data_t*)(node_max->pData))->q, sizeof(color_vector_t));
|
|
if ((codebook_size+1) < pCB->max_colors)
|
|
pCB->ppC[codebook_size+1] = (int*)(ptr_next);
|
|
|
|
FreeNodeData((node_data_t*)(node_max->pData));
|
|
node_temp = bintree_node_remove(&bst, node_max);
|
|
codebook_size++;
|
|
|
|
} while(node_temp);
|
|
pCB->num_colors = codebook_size;
|
|
|
|
// printf("Num. pixels = %d\n",sum_pixel);
|
|
// printf("Codebook size = %d\n",codebook_size);
|
|
// PrintMatrix("Codebook=",(double*)pCB->pColor, ROW_DIM, codebook_size);
|
|
|
|
FreeNodeData(pCQ->pRootData);
|
|
bintree_free(&bst);
|
|
return codebook_size;
|
|
}
|