25 #define INITIAL_CAPACITY 16
28 #define STABILITY_THRESH 1e-4
58 double (*Phi)(double, double,
const void*);
89 int (*DrawFun)(
pen *Pen,
const void *Param);
180 double (*
Psf)(double, double,
const void*);
193 static int ReallocStencilSet(
sset *Set)
217 for(S = Set->
Capacity; S < NewCapacity; S++)
219 Stencil[S].
Phi = NULL;
220 Stencil[S].
Alpha = NULL;
221 Stencil[S].
Beta = NULL;
226 for(S = Set->
Capacity; S < NewCapacity; S++)
227 if(!(Stencil[S].Alpha = (
double *)
229 || !(Stencil[S].Beta = (
double *)
255 Free(Stencil[S].Beta);
256 Free(Stencil[S].Alpha);
276 static double *SamplePsf(
sset *Set)
279 const int W = 2*R + 1;
280 double *Samples = NULL;
284 if(!(Samples = (
double *)
Malloc(
sizeof(
double)*W*W)))
290 for(j = -R, Offset = 0; j <= R; j++)
291 for(i = -R; i <= R; i++, Offset++)
292 Samples[Offset] = Set->
Psf(i/((
double)QUAD_RES),
293 j/((
double)QUAD_RES), Set->
PsfParam);
309 static int ComputeQuadWeights(
sset *Set)
311 const int R = (int)ceil(QUAD_RES * Set->
PsfRadius);
312 const int W = 2*R + 1;
313 double *QuadDx = NULL, *QuadDy = NULL, *PsfSamples = NULL;
315 int x, y, i, j, Offset;
318 if(!(QuadDx = (
double *)
Malloc(
sizeof(
double)*
319 (QUAD_RES + W)*(QUAD_RES + W)))
320 || !(QuadDy = (
double *)
Malloc(
sizeof(
double)*
321 (QUAD_RES + W)*(QUAD_RES + W)))
322 || !(PsfSamples = SamplePsf(Set)))
331 for(y = -R, Offset = 0; y <= QUAD_RES + R; y++)
332 for(x = -R; x <= QUAD_RES + R; x++, Offset++)
334 for(j = -y, Sum = 0; j <= QUAD_RES - y; j++)
341 Sum += PsfSamples[(i + R) + W*(j + R)];
346 Sum -= PsfSamples[(i + R) + W*(j + R)];
350 QuadDx[Offset] = Sum;
352 for(i = -x, Sum = 0; i <= QUAD_RES - x; i++)
359 Sum += PsfSamples[(i + R) + W*(j + R)];
364 Sum -= PsfSamples[(i + R) + W*(j + R)];
368 QuadDy[Offset] = Sum;
379 static double Circle(
double x,
double y,
ATTRIBUTE_UNUSED const void *Param)
381 return sqrt(x*x + y*y);
394 double (*Psf)(
double,
double,
const void*),
395 const void *PsfParam,
double PsfRadius)
398 int x, y, n, R = (int)floor(StencilRadius);
401 if(R <= 0 || PsfRadius < 0
417 if(!ComputeQuadWeights(Set)
426 for(y = -R, n = 0; y < R; y++)
427 for(x = -R; x < R; x++)
428 if((x + 0.5)*(x + 0.5) + (y + 0.5)*(y + 0.5)
429 <= StencilRadius*StencilRadius)
438 if(!
AddStencil(Set, Circle, NULL, 0, NULL, NULL, NULL))
464 double (*
GetPsf(
const sset *Set))(double, double,
const void*)
466 return (Set) ? Set->
Psf : NULL;
477 return (Set) ? Set->
PsfParam : NULL;
523 const void *PhiParam,
double Rotation,
524 int (*DrawFun)(
pen *Pen,
const void *Param),
const void *DrawParam,
525 const float *DrawColor)
528 const double *QuadDx, *QuadDy;
529 double Sum, Alpha, Beta, Temp;
530 int n, x, y, i, R, S, Offset;
534 if(!ReallocStencilSet(Set))
548 R = (int)ceil(QUAD_RES * Set->
PsfRadius);
552 for(n = 0, Sum = 0; n < Set->
NumCells; n++)
557 for(y = -R, Offset = 0; y <= QUAD_RES + R; y++)
558 for(x = -R; x <= QUAD_RES + R; x++, Offset++)
560 Temp =
EvalPhi(Set, S, Set->
Cell[n].
x + ((
double)x)/QUAD_RES,
561 Set->
Cell[n].
y + ((
double)y)/QUAD_RES);
562 Beta -= QuadDx[Offset] * Temp;
563 Alpha += QuadDy[Offset] * Temp;
566 Entry->
Alpha[n] = Alpha;
567 Entry->
Beta[n] = Beta;
568 Sum += sqrt(Alpha*Alpha + Beta*Beta);
574 Entry->
Alpha[n] /= Sum;
575 Entry->
Beta[n] /= Sum;
617 const int R = (int)ceil(QUAD_RES * Set->
PsfRadius);
618 const int W = 2*R + 1;
619 double *PsfSamples = NULL, *Psi = NULL;
620 double Sum, Alpha, Beta, a, b, c, d;
621 int MinX, MaxX, MinY, MaxY, PsiWidth, PsiHeight;
622 int i, j, n, nx, ny, x, y, Success = 0;
624 MinX = Set->
Cell[0].
x;
626 MinY = Set->
Cell[0].
y;
631 if(Set->
Cell[n].
x >= MaxX)
632 MaxX = Set->
Cell[n].
x + 1;
633 else if(Set->
Cell[n].
x < MinX)
634 MinX = Set->
Cell[n].
x;
636 if(Set->
Cell[n].
y >= MaxY)
637 MaxY = Set->
Cell[n].
y + 1;
638 else if(Set->
Cell[n].
y < MinY)
639 MinY = Set->
Cell[n].
y;
642 PsiWidth = MaxX - MinX + 1;
643 PsiHeight = MaxY - MinY + 1;
645 if(!(PsfSamples = SamplePsf(Set))
646 || !(Psi = (
double *)
Malloc(
sizeof(
double)*PsiWidth*PsiHeight)))
652 for(ny = MinY; ny <= MaxY; ny++)
653 for(nx = MinX; nx <= MaxX; nx++)
655 for(y = -R, Sum = 0; y <= R; y++)
656 for(x = -R; x <= R; x++)
658 Sum += PsfSamples[(R - x) + W*(R - y)]
659 *
EvalPhi(Set, i, nx + ((
double)x)/QUAD_RES,
660 ny + ((
double)y)/QUAD_RES);
663 Psi[(nx - MinX) + PsiWidth*(ny - MinY)] = Sum;
669 for(n = 0, Sum = 0; n < Set->
NumCells; n++)
682 a = Psi[(nx - MinX) + PsiWidth*(ny + 1 - MinY)];
683 b = Psi[(nx + 1 - MinX) + PsiWidth*(ny + 1 - MinY)];
684 c = Psi[(nx - MinX) + PsiWidth*(ny - MinY)];
685 d = Psi[(nx + 1 - MinX) + PsiWidth*(ny - MinY)];
688 Sum += fabs(Alpha*b - (Alpha - Beta)*a - Beta*c)
689 + fabs(Beta*b + (Alpha - Beta)*d - Alpha*c);
691 Sum += fabs(Alpha*a - (Alpha + Beta)*b + Beta*d)
692 + fabs(Beta*a - (Alpha + Beta)*c + Alpha*d);
717 double *ConfusionMatrix = NULL;
718 double TV, MinTV, Min2TV;
719 int i, j, MinS, Success = 0;
722 if(!(ConfusionMatrix = (
double *)
Malloc(
sizeof(
double)*
728 printf(
"Stencil confusion for the ith distance function phi_i\n"
729 " i V(S_i) S^* V(S^*) V(S^*2) S^*2\n"
730 "------------------------------------------------------------\n");
735 MinTV = Min2TV = 1e30;
755 printf(
"%3d %8.4f %3d %8.4f %8.4f {",
757 MinS, MinTV, Min2TV);
770 Free(ConfusionMatrix);
780 static void Rgb2YPbPr(
float *Dest,
const float *Src)
782 float Red = ((
float *)Src)[0];
783 float Green = ((
float *)Src)[1];
784 float Blue = ((
float *)Src)[2];
786 Dest[0] = 0.299f*Red + 0.587f*Green + 0.114f*Blue;
787 Dest[1] = -0.168736f*Red - 0.331264f*Green + 0.5f*Blue;
788 Dest[2] = 0.5f*Red - 0.418688f*Green - 0.081312f*Blue;
820 const float *Image,
int ImageWidth,
int ImageHeight)
823 float *TVCell = NULL;
826 const int CellsPerRow = ImageWidth - 1;
827 const float *ImagePtr;
829 int *NeighborOffset = NULL;
831 float a[3], b[3], c[3], d[4], ab[3], ac[3], bd[3], cd[3];
832 int x, y, n, nx, ny, S, BestS, NumCells, NumStencils;
835 if(!Set || !(TVCell = (
float *)
Malloc(
sizeof(
float)*
836 NUMANGLES*CellsPerRow*(ImageHeight - 1)))
850 for(n = 0; n < NumCells; n++)
851 NeighborOffset[n] = NUMANGLES*(Cell[n].x - CellsPerRow*Cell[n].y);
853 for(y = 0, TVCellPtr = TVCell; y < ImageHeight - 1; y++)
855 for(x = 0; x < CellsPerRow; x++, TVCellPtr +=
NUMANGLES)
857 ImagePtr = Image + 3*(x + ImageWidth*y);
864 Rgb2YPbPr(a, ImagePtr);
865 Rgb2YPbPr(b, ImagePtr + 3);
866 Rgb2YPbPr(c, ImagePtr + 3*ImageWidth);
867 Rgb2YPbPr(d, ImagePtr + 3 + 3*ImageWidth);
883 TVCellPtr[0] = (float)(fabs(ab[0]) + fabs(ab[1]) + fabs(ab[2])
884 + fabs(cd[0]) + fabs(cd[1]) + fabs(cd[2]));
887 for(n = 1; n < (NUMANGLES/2); n++)
888 TVCellPtr[n] = (
float)(
889 fabs(Alpha[n]*ab[0] - Beta[n]*ac[0]) +
890 fabs(Alpha[n]*ab[1] - Beta[n]*ac[1]) +
891 fabs(Alpha[n]*ab[2] - Beta[n]*ac[2]) +
892 fabs(Beta[n]*bd[0] - Alpha[n]*cd[0]) +
893 fabs(Beta[n]*bd[1] - Alpha[n]*cd[1]) +
894 fabs(Beta[n]*bd[2] - Alpha[n]*cd[2]));
897 TVCellPtr[n++] = (float)(fabs(bd[0]) + fabs(bd[1]) + fabs(bd[2])
898 + fabs(ac[0]) + fabs(ac[1]) + fabs(ac[2]));
902 TVCellPtr[n] = (
float)(
903 fabs(Alpha[n]*ab[0] - Beta[n]*bd[0]) +
904 fabs(Alpha[n]*ab[1] - Beta[n]*bd[1]) +
905 fabs(Alpha[n]*ab[2] - Beta[n]*bd[2]) +
906 fabs(Beta[n]*ac[0] - Alpha[n]*cd[0]) +
907 fabs(Beta[n]*ac[1] - Alpha[n]*cd[1]) +
908 fabs(Beta[n]*ac[2] - Alpha[n]*cd[2]));
912 for(y = 0; y < ImageHeight; y++)
914 for(x = 0; x < ImageWidth; x++)
916 TVCellPtr = TVCell + NUMANGLES*(x + CellsPerRow*(y - 1));
919 MinTV = Min2TV = 1e30f;
921 for(S = 0; S < NumStencils; S++, TablePtr += NumCells)
926 for(n = 0; n < NumCells; n++)
930 if(nx >= 0 && nx < CellsPerRow)
932 ny = y - Cell[n].
y - 1;
934 if(ny >= 0 && ny < ImageHeight - 1)
935 TV += TablePtr[n].Weight*TVCellPtr[
936 NeighborOffset[n] + TablePtr[n].AngleIndex ];
963 if(Min2TV - MinTV < TvThresh)
972 Free(NeighborOffset);
980 static float *MakeContoursBgImage(
int OutputWidth,
int OutputHeight,
981 double ScaleFactor,
const float *Input,
int InputWidth,
int InputHeight,
984 const int NumChannels = (IsColor) ? 3 : 1;
985 const int OutputStride = NumChannels*OutputWidth;
986 float *Output, *OutputPtr;
988 int x, y, bx, bx1, bx2, by, by1, by2;
990 if(!(Output = (
float *)
Malloc(
sizeof(
float)*OutputStride*OutputHeight)))
993 for(y = 0, by1 = 0; y < InputHeight; y++)
995 by2 = (y < InputHeight - 1) ?
996 (
int)
ROUND(ScaleFactor*(y + 1)) : OutputHeight;
998 for(x = 0, bx1 = 0; x < InputWidth; x++)
1001 Gray = 0.05f + 0.7f*(
1002 0.299f*Input[3*(x + InputWidth*y) + 0] +
1003 0.587f*Input[3*(x + InputWidth*y) + 1] +
1004 0.114f*Input[3*(x + InputWidth*y) + 2]);
1006 bx2 = (x < InputWidth - 1) ?
1007 ((
int)
ROUND(ScaleFactor*(x + 1))) : OutputWidth;
1010 for(by = by1, OutputPtr = Output + OutputStride*by; by < by2;
1011 by++, OutputPtr += OutputStride)
1012 for(bx = bx1; bx < bx2; bx++)
1014 OutputPtr[3*bx + 0] = Gray;
1015 OutputPtr[3*bx + 1] = Gray;
1016 OutputPtr[3*bx + 2] = Gray;
1019 for(by = by1, OutputPtr = Output + OutputStride*by; by < by2;
1020 by++, OutputPtr += OutputStride)
1021 for(bx = bx1; bx < bx2; bx++)
1022 OutputPtr[bx] = Gray;
1050 double ScaleFactor,
const int *Stencil,
const sset *Set,
1051 const float *Input,
int InputWidth,
int InputHeight)
1055 float *Output = NULL;
1057 int i, x, y, Success = 0;
1060 || !(Output = MakeContoursBgImage(OutputWidth, OutputHeight,
1061 ScaleFactor, Input, InputWidth, InputHeight, 1))
1070 for(y = 0; y < InputHeight; y++)
1071 for(x = 0; x < InputWidth; x++)
1072 if(i == Stencil[x + InputWidth*y])
1080 ScaleFactor*(x + 0.5),
1081 ScaleFactor*(y + 0.5));
1095 if(!
WriteImage(Output, OutputWidth, OutputHeight, FileName,
1096 IMAGEIO_FLOAT | IMAGEIO_RGB, 85))
1121 const int *Stencil,
const sset *Set,
1122 const float *Input,
int InputWidth,
int InputHeight)
1124 const int DimScale = 6;
1128 uint8_t *BgImage = NULL;
1129 int i, x, y, Success = 0;
1132 || !(BgImage = (uint8_t *)
Malloc(
1133 sizeof(uint8_t)*InputWidth*InputHeight))
1135 DimScale*InputWidth, DimScale*InputHeight))
1138 fprintf(
PenGetFile(Pen),
"%d %d scale\n", DimScale, DimScale);
1141 for(y = 0; y < InputHeight; y++)
1142 for(x = 0; x < InputWidth; x++)
1143 BgImage[x + InputWidth*y] =
1144 (uint8_t)floor(255*(0.05f + 0.7f*(
1145 0.299f*Input[3*(x + InputWidth*y) + 0]
1146 + 0.587f*Input[3*(x + InputWidth*y) + 1]
1147 + 0.114f*Input[3*(x + InputWidth*y) + 2])) + 0.5f);
1150 BgImage, InputWidth, InputHeight))
1161 for(y = 0; y < InputHeight; y++)
1162 for(x = 0; x < InputWidth; x++)
1163 if(i == Stencil[x + InputWidth*y])
1169 (x + 0.5), (y + 0.5));
1211 const int *Stencil,
const sset *Set,
1212 const float *Input,
int InputWidth,
int InputHeight)
1214 const int DimScale = 9;
1218 float *BgImage = NULL;
1219 int i, x, y, Success = 0;
1230 if(!
SvgOpen(Pen, SvgFile, DimScale*InputWidth, DimScale*InputHeight))
1236 if(!(BgImage = MakeContoursBgImage(InputWidth, InputHeight,
1237 1, Input, InputWidth, InputHeight, 0))
1238 || !
WriteImage(BgImage, InputWidth, InputHeight, BgFile,
1239 IMAGEIO_FLOAT | IMAGEIO_GRAYSCALE, 85))
1243 "<image x=\"0\" y=\"0\" width=\"%d\" height=\"%d\"\n"
1244 " xlink:href=\"%s\"\n"
1245 " style=\"image-rendering:-moz-crisp-edges;"
1246 " -ms-interpolation-mode: nearest-neighbor;\" />\n",
1247 DimScale*InputWidth, DimScale*InputHeight, BgFile);
1255 for(y = 0; y < InputHeight; y++)
1256 for(x = 0; x < InputWidth; x++)
1257 if(i == Stencil[x + InputWidth*y])
1263 DimScale*(x + 0.5), DimScale*(y + 0.5));