Image Interpolation with Geometric Contour Stencils
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
sset.c
Go to the documentation of this file.
1 
16 #include <stdio.h>
17 #include "sset.h"
18 #include "imageio.h"
19 
21 #define NUMANGLES 64
22 
23 #define QUAD_RES 8
24 
25 #define INITIAL_CAPACITY 16
26 
28 #define STABILITY_THRESH 1e-4
29 
30 
44 typedef struct
45 {
47  double *Alpha;
49  double *Beta;
50 
58  double (*Phi)(double, double, const void*);
70  double PhiTrans[2];
79  const void *PhiParam;
80 
89  int (*DrawFun)(pen *Pen, const void *Param);
91  const void *DrawParam;
98  const float *DrawColor;
99 } stencilentry;
100 
101 
110 typedef struct
111 {
113  int x;
115  int y;
117 
118 
128 typedef struct
129 {
131  int32_t AngleIndex;
133  float Weight;
134 } stencilqvec;
135 
136 
159 typedef struct ssetstruct
160 {
164  int Capacity;
169 
171  int NumCells;
175  double *QuadDx;
177  double *QuadDy;
178 
180  double (*Psf)(double, double, const void*);
182  const void *PsfParam;
184  double PsfRadius;
185 } ssettype;
186 
187 
193 static int ReallocStencilSet(sset *Set)
194 {
195  stencilentry *Stencil;
196  int S, NewCapacity;
197 
198 
199  if(!Set)
200  return 0;
201 
202  /* Either allocate INITIAL_CAPACITY or increase by 10% */
203  if(Set->Stencil == NULL)
204  NewCapacity = INITIAL_CAPACITY;
205  else
206  NewCapacity = Set->Capacity + (int)(Set->Capacity/10) + 1;
207 
208  /* Reallocate the Stencil and StencilTable arrays */
209  if(!(Stencil = Set->Stencil = (stencilentry *)
210  Realloc(Set->Stencil, sizeof(stencilentry)*NewCapacity))
211  || !(Set->StencilTable = (stencilqvec *)
212  Realloc(Set->StencilTable, sizeof(stencilqvec)*
213  NewCapacity*Set->NumCells)))
214  return 0;
215 
216  /* Set the newly allocated pointers to NULL */
217  for(S = Set->Capacity; S < NewCapacity; S++)
218  {
219  Stencil[S].Phi = NULL;
220  Stencil[S].Alpha = NULL;
221  Stencil[S].Beta = NULL;
222  Stencil[S].DrawFun = NULL;
223  }
224 
225  /* Allocate Alpha and Beta arrays for each new stencil */
226  for(S = Set->Capacity; S < NewCapacity; S++)
227  if(!(Stencil[S].Alpha = (double *)
228  Malloc(sizeof(double)*Set->NumCells))
229  || !(Stencil[S].Beta = (double *)
230  Malloc(sizeof(double)*Set->NumCells)))
231  return 0;
232 
233  Set->Capacity = NewCapacity;
234  return 1;
235 }
236 
237 
245 {
246  if(Set)
247  {
248  if(Set->Stencil)
249  {
250  stencilentry *Stencil = Set->Stencil;
251  int S;
252 
253  for(S = 0; S < Set->Capacity; S++)
254  {
255  Free(Stencil[S].Beta);
256  Free(Stencil[S].Alpha);
257  }
258 
259  Free(Stencil);
260  }
261 
262  Free(Set->StencilTable);
263  Free(Set->Cell);
264  Free(Set->QuadDy);
265  Free(Set->QuadDx);
266  Free(Set);
267  }
268 }
269 
270 
276 static double *SamplePsf(sset *Set)
277 {
278  const int R = (int)ceil(QUAD_RES * Set->PsfRadius);
279  const int W = 2*R + 1;
280  double *Samples = NULL;
281  int i, j, Offset;
282 
283 
284  if(!(Samples = (double *)Malloc(sizeof(double)*W*W)))
285  return NULL;
286 
287  if(!Set->Psf)
288  Samples[0] = QUAD_RES*QUAD_RES;
289  else
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);
294 
295  return Samples;
296 }
297 
298 
309 static int ComputeQuadWeights(sset *Set)
310 {
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;
314  double Sum;
315  int x, y, i, j, Offset;
316 
317 
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)))
323  {
324  Free(PsfSamples);
325  Free(QuadDy);
326  Free(QuadDx);
327  return 0;
328  }
329 
330  /* Compute quadrature weights that incorporate convolution with the PSF */
331  for(y = -R, Offset = 0; y <= QUAD_RES + R; y++)
332  for(x = -R; x <= QUAD_RES + R; x++, Offset++)
333  {
334  for(j = -y, Sum = 0; j <= QUAD_RES - y; j++)
335  {
336  if(abs(j) <= R)
337  {
338  i = QUAD_RES - x;
339 
340  if(abs(i) <= R)
341  Sum += PsfSamples[(i + R) + W*(j + R)];
342 
343  i = -x;
344 
345  if(abs(i) <= R)
346  Sum -= PsfSamples[(i + R) + W*(j + R)];
347  }
348  }
349 
350  QuadDx[Offset] = Sum;
351 
352  for(i = -x, Sum = 0; i <= QUAD_RES - x; i++)
353  {
354  if(abs(i) <= R)
355  {
356  j = QUAD_RES - y;
357 
358  if(abs(j) <= R)
359  Sum += PsfSamples[(i + R) + W*(j + R)];
360 
361  j = -y;
362 
363  if(abs(j) <= R)
364  Sum -= PsfSamples[(i + R) + W*(j + R)];
365  }
366  }
367 
368  QuadDy[Offset] = Sum;
369  }
370 
371  Set->QuadDx = QuadDx;
372  Set->QuadDy = QuadDy;
373  Free(PsfSamples);
374  return 1;
375 }
376 
377 
379 static double Circle(double x, double y, ATTRIBUTE_UNUSED const void *Param)
380 {
381  return sqrt(x*x + y*y);
382 }
383 
384 
393 sset *NewStencilSet(double StencilRadius,
394  double (*Psf)(double, double, const void*),
395  const void *PsfParam, double PsfRadius)
396 {
397  sset *Set = NULL;
398  int x, y, n, R = (int)floor(StencilRadius);
399 
400 
401  if(R <= 0 || PsfRadius < 0
402  || !(Set = (sset *)Malloc(sizeof(struct ssetstruct))))
403  return NULL;
404 
405  /* Initializes an empty stencil set */
406  Set->NumStencils = Set->Capacity = Set->NumCells = 0;
407  Set->QuadDx = NULL;
408  Set->QuadDy = NULL;
409  Set->Cell = NULL;
410  Set->Stencil = NULL;
411  Set->StencilTable = NULL;
412  Set->Psf = Psf;
413  Set->PsfParam = PsfParam;
414  Set->PsfRadius = (Psf) ? PsfRadius : 0;
415 
416  /* Allocate and precompute cell quadrature weights */
417  if(!ComputeQuadWeights(Set)
418  || !(Set->Cell = (stencilsetcell *)
419  Malloc(sizeof(stencilsetcell)*4*R*R)))
420  {
421  FreeStencilSet(Set);
422  return NULL;
423  }
424 
425  /* Construct the circular neighborhood */
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)
430  {
431  Set->Cell[n].x = x;
432  Set->Cell[n].y = y;
433  n++;
434  }
435 
436  Set->NumCells = n;
437 
438  if(!AddStencil(Set, Circle, NULL, 0, NULL, NULL, NULL))
439  {
440  FreeStencilSet(Set);
441  return NULL;
442  }
443 
444  return Set;
445 }
446 
447 
453 int GetNumStencils(const sset *Set)
454 {
455  return (Set) ? Set->NumStencils : 0;
456 }
457 
458 
464 double (*GetPsf(const sset *Set))(double, double, const void*)
465 {
466  return (Set) ? Set->Psf : NULL;
467 }
468 
469 
475 const void *GetPsfParam(const sset *Set)
476 {
477  return (Set) ? Set->PsfParam : NULL;
478 }
479 
480 
486 double GetPsfRadius(const sset *Set)
487 {
488  return (Set) ? Set->PsfRadius : 0;
489 }
490 
491 
502 double EvalPhi(const sset *Set, int S, double x, double y)
503 {
504  const stencilentry *Entry = &Set->Stencil[S];
505 
506  return Entry->Phi(Entry->PhiTrans[0]*x + Entry->PhiTrans[1]*y,
507  -Entry->PhiTrans[1]*x + Entry->PhiTrans[0]*y, Entry->PhiParam);
508 }
509 
510 
522 int AddStencil(sset *Set, double (*Phi)(double, double, const void*),
523  const void *PhiParam, double Rotation,
524  int (*DrawFun)(pen *Pen, const void *Param), const void *DrawParam,
525  const float *DrawColor)
526 {
527  stencilentry *Entry;
528  const double *QuadDx, *QuadDy;
529  double Sum, Alpha, Beta, Temp;
530  int n, x, y, i, R, S, Offset;
531 
532 
533  if(!Set || !Set->Stencil || Set->Capacity <= Set->NumStencils)
534  if(!ReallocStencilSet(Set)) /* Allocate more memory if needed */
535  return 0;
536 
537  S = Set->NumStencils;
538  Set->NumStencils++;
539  Entry = &Set->Stencil[S];
540  Entry->Phi = Phi;
541  Entry->PhiParam = PhiParam;
542  Entry->PhiTrans[0] = cos(Rotation);
543  Entry->PhiTrans[1] = sin(Rotation);
544  Entry->DrawFun = DrawFun;
545  Entry->DrawParam = DrawParam;
546  Entry->DrawColor = DrawColor;
547 
548  R = (int)ceil(QUAD_RES * Set->PsfRadius);
549  QuadDx = Set->QuadDx;
550  QuadDy = Set->QuadDy;
551 
552  for(n = 0, Sum = 0; n < Set->NumCells; n++)
553  {
554  Alpha = Beta = 0;
555 
556  /* Compute the average of (grad (Psf * Phi))^perp over the cell */
557  for(y = -R, Offset = 0; y <= QUAD_RES + R; y++)
558  for(x = -R; x <= QUAD_RES + R; x++, Offset++)
559  {
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;
564  }
565 
566  Entry->Alpha[n] = Alpha;
567  Entry->Beta[n] = Beta;
568  Sum += sqrt(Alpha*Alpha + Beta*Beta);
569  }
570 
571  /* Normalize the stencil */
572  for(n = 0; n < Set->NumCells; n++)
573  {
574  Entry->Alpha[n] /= Sum;
575  Entry->Beta[n] /= Sum;
576  }
577 
578  /* Quantize the stencil */
579  for(n = 0; n < Set->NumCells; n++)
580  {
581  i = (int)ROUND(atan2(Entry->Beta[n], Entry->Alpha[n])*NUMANGLES/M_PI);
582 
583  while(i < 0)
584  i += NUMANGLES;
585  while(i >= NUMANGLES)
586  i -= NUMANGLES;
587 
588  Set->StencilTable[n + Set->NumCells*S].AngleIndex = i;
589  Set->StencilTable[n + Set->NumCells*S].Weight =
590  (float)(sqrt(Entry->Alpha[n]*Entry->Alpha[n]
591  + Entry->Beta[n]*Entry->Beta[n]));
592  }
593 
594  return 1;
595 }
596 
597 
615 int StencilConfusion(double *ConfusionMatrix, sset *Set)
616 {
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;
623 
624  MinX = Set->Cell[0].x;
625  MaxX = MinX + 1;
626  MinY = Set->Cell[0].y;
627  MaxY = MinY + 1;
628 
629  for(n = 1; n < Set->NumCells; n++)
630  {
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;
635 
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;
640  }
641 
642  PsiWidth = MaxX - MinX + 1;
643  PsiHeight = MaxY - MinY + 1;
644 
645  if(!(PsfSamples = SamplePsf(Set))
646  || !(Psi = (double *)Malloc(sizeof(double)*PsiWidth*PsiHeight)))
647  goto Catch;
648 
649  for(i = 0; i < Set->NumStencils; i++)
650  {
651  /* Compute psi_i = (h * phi_i) */
652  for(ny = MinY; ny <= MaxY; ny++)
653  for(nx = MinX; nx <= MaxX; nx++)
654  {
655  for(y = -R, Sum = 0; y <= R; y++)
656  for(x = -R; x <= R; x++)
657  {
658  Sum += PsfSamples[(R - x) + W*(R - y)]
659  * EvalPhi(Set, i, nx + ((double)x)/QUAD_RES,
660  ny + ((double)y)/QUAD_RES);
661  }
662 
663  Psi[(nx - MinX) + PsiWidth*(ny - MinY)] = Sum;
664  }
665 
666  /* Compute the stencil TVs, V(S_j, psi_i) */
667  for(j = 0; j < Set->NumStencils; j++)
668  {
669  for(n = 0, Sum = 0; n < Set->NumCells; n++)
670  {
671  nx = Set->Cell[n].x;
672  ny = Set->Cell[n].y;
673  Alpha = Set->Stencil[j].Alpha[n];
674  Beta = Set->Stencil[j].Beta[n];
675 
676  /* Get the four corners of the cell
677  *
678  * a---b
679  * | |
680  * c---d
681  */
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)];
686 
687  if(Alpha*Beta >= 0)
688  Sum += fabs(Alpha*b - (Alpha - Beta)*a - Beta*c)
689  + fabs(Beta*b + (Alpha - Beta)*d - Alpha*c);
690  else
691  Sum += fabs(Alpha*a - (Alpha + Beta)*b + Beta*d)
692  + fabs(Beta*a - (Alpha + Beta)*c + Alpha*d);
693  }
694 
695  ConfusionMatrix[i + Set->NumStencils*j] = Sum;
696  }
697  }
698 
699  Success = 1;
700 Catch:
701  Free(Psi);
702  Free(PsfSamples);
703  return Success;
704 }
705 
706 
716 {
717  double *ConfusionMatrix = NULL;
718  double TV, MinTV, Min2TV;
719  int i, j, MinS, Success = 0;
720 
721 
722  if(!(ConfusionMatrix = (double *)Malloc(sizeof(double)*
723  GetNumStencils(Set)*GetNumStencils(Set)))
724  || !StencilConfusion(ConfusionMatrix, Set))
725  goto Catch;
726 
727  /* Print table header */
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");
731 
732  for(i = 0; i < GetNumStencils(Set); i++)
733  {
734  MinS = -1;
735  MinTV = Min2TV = 1e30;
736 
737  /* Find the best and second best stencil TVs */
738  for(j = 0; j < GetNumStencils(Set); j++)
739  {
740  TV = ConfusionMatrix[i + GetNumStencils(Set)*j];
741 
742  if(TV < Min2TV)
743  {
744  if(TV < MinTV)
745  {
746  Min2TV = MinTV;
747  MinS = j;
748  MinTV = TV;
749  }
750  else
751  Min2TV = TV;
752  }
753  }
754 
755  printf("%3d %8.4f %3d %8.4f %8.4f {",
756  i, ConfusionMatrix[i + GetNumStencils(Set)*i],
757  MinS, MinTV, Min2TV);
758 
759  /* Print all the indices of stencil who have TV equal to Min2TV */
760  for(j = 0; j < GetNumStencils(Set); j++)
761  if(fabs(ConfusionMatrix[i + GetNumStencils(Set)*j] - Min2TV)
762  < 1e-12)
763  printf("%3d,", j);
764 
765  printf("\b}\n");
766  }
767 
768  Success = 1;
769 Catch:
770  Free(ConfusionMatrix);
771  return Success;
772 }
773 
774 
780 static void Rgb2YPbPr(float *Dest, const float *Src)
781 {
782  float Red = ((float *)Src)[0];
783  float Green = ((float *)Src)[1];
784  float Blue = ((float *)Src)[2];
785 
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;
789 }
790 
791 
819 void FitStencils(int *Stencil, const sset *Set,
820  const float *Image, int ImageWidth, int ImageHeight)
821 {
822  const float TvThresh = (float)(STABILITY_THRESH*(4*sqrt(2)));
823  float *TVCell = NULL;
824  float *TVCellPtr;
825  stencilqvec *TablePtr;
826  const int CellsPerRow = ImageWidth - 1;
827  const float *ImagePtr;
828  const stencilsetcell *Cell;
829  int *NeighborOffset = NULL;
830  float TV, MinTV, Min2TV, Alpha[NUMANGLES], Beta[NUMANGLES];
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;
833 
834 
835  if(!Set || !(TVCell = (float *)Malloc(sizeof(float)*
836  NUMANGLES*CellsPerRow*(ImageHeight - 1)))
837  || !(NeighborOffset = (int *)Malloc(sizeof(int)*Set->NumCells)))
838  goto Catch;
839 
840  NumCells = Set->NumCells;
841  NumStencils = Set->NumStencils;
842  Cell = Set->Cell;
843 
844  for(n = 0; n < NUMANGLES; n++)
845  {
846  Alpha[n] = (float)cos((M_PI*n)/NUMANGLES);
847  Beta[n] = (float)sin((M_PI*n)/NUMANGLES);
848  }
849 
850  for(n = 0; n < NumCells; n++)
851  NeighborOffset[n] = NUMANGLES*(Cell[n].x - CellsPerRow*Cell[n].y);
852 
853  for(y = 0, TVCellPtr = TVCell; y < ImageHeight - 1; y++)
854  {
855  for(x = 0; x < CellsPerRow; x++, TVCellPtr += NUMANGLES)
856  {
857  ImagePtr = Image + 3*(x + ImageWidth*y);
858  /* Get the four corners of the cell in YPbPr representation
859  *
860  * a---b
861  * | |
862  * c---d
863  */
864  Rgb2YPbPr(a, ImagePtr);
865  Rgb2YPbPr(b, ImagePtr + 3);
866  Rgb2YPbPr(c, ImagePtr + 3*ImageWidth);
867  Rgb2YPbPr(d, ImagePtr + 3 + 3*ImageWidth);
868 
869  ab[0] = a[0] - b[0];
870  ab[1] = a[1] - b[1];
871  ab[2] = a[2] - b[2];
872  ac[0] = a[0] - c[0];
873  ac[1] = a[1] - c[1];
874  ac[2] = a[2] - c[2];
875  bd[0] = b[0] - d[0];
876  bd[1] = b[1] - d[1];
877  bd[2] = b[2] - d[2];
878  cd[0] = c[0] - d[0];
879  cd[1] = c[1] - d[1];
880  cd[2] = c[2] - d[2];
881 
882  /* Horizontal TV */
883  TVCellPtr[0] = (float)(fabs(ab[0]) + fabs(ab[1]) + fabs(ab[2])
884  + fabs(cd[0]) + fabs(cd[1]) + fabs(cd[2]));
885 
886  /* Angles between 0 and pi/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]));
895 
896  /* Vertical TV */
897  TVCellPtr[n++] = (float)(fabs(bd[0]) + fabs(bd[1]) + fabs(bd[2])
898  + fabs(ac[0]) + fabs(ac[1]) + fabs(ac[2]));
899 
900  /* Angles between pi/2 and pi */
901  for(; n < NUMANGLES; n++)
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]));
909  }
910  }
911 
912  for(y = 0; y < ImageHeight; y++)
913  {
914  for(x = 0; x < ImageWidth; x++)
915  {
916  TVCellPtr = TVCell + NUMANGLES*(x + CellsPerRow*(y - 1));
917  TablePtr = Set->StencilTable;
918  BestS = 0;
919  MinTV = Min2TV = 1e30f;
920 
921  for(S = 0; S < NumStencils; S++, TablePtr += NumCells)
922  {
923  TV = 0;
924 
925  /* Compute the stencil TV for the Sth stencil */
926  for(n = 0; n < NumCells; n++)
927  {
928  nx = x + Cell[n].x;
929 
930  if(nx >= 0 && nx < CellsPerRow)
931  {
932  ny = y - Cell[n].y - 1;
933 
934  if(ny >= 0 && ny < ImageHeight - 1)
935  TV += TablePtr[n].Weight*TVCellPtr[
936  NeighborOffset[n] + TablePtr[n].AngleIndex ];
937  }
938  }
939 
940  /* Determine the best-fitting stencil */
941  if(TV < Min2TV)
942  {
943  if(TV < MinTV)
944  {
945  BestS = S;
946  Min2TV = MinTV;
947  MinTV = TV;
948  }
949  else
950  Min2TV = TV;
951  }
952 
953  /*if(TV <= MinTV)
954  {
955  BestS = S;
956  Min2TV = MinTV;
957  MinTV = TV;
958  }*/
959  }
960 
961  /* If the separation between smallest and second smallest TVs
962  is below the threshold, select the circle stencil. */
963  if(Min2TV - MinTV < TvThresh)
964  BestS = 0;
965 
966  *Stencil = BestS;
967  Stencil++;
968  }
969  }
970 
971 Catch:
972  Free(NeighborOffset);
973  Free(TVCell);
974 }
975 
976 
977 /***************************************************************************/
978 
979 
980 static float *MakeContoursBgImage(int OutputWidth, int OutputHeight,
981  double ScaleFactor, const float *Input, int InputWidth, int InputHeight,
982  int IsColor)
983 {
984  const int NumChannels = (IsColor) ? 3 : 1;
985  const int OutputStride = NumChannels*OutputWidth;
986  float *Output, *OutputPtr;
987  float Gray;
988  int x, y, bx, bx1, bx2, by, by1, by2;
989 
990  if(!(Output = (float *)Malloc(sizeof(float)*OutputStride*OutputHeight)))
991  return NULL;
992 
993  for(y = 0, by1 = 0; y < InputHeight; y++)
994  {
995  by2 = (y < InputHeight - 1) ?
996  (int)ROUND(ScaleFactor*(y + 1)) : OutputHeight;
997 
998  for(x = 0, bx1 = 0; x < InputWidth; x++)
999  {
1000  /* Darken and desaturate the pixel */
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]);
1005 
1006  bx2 = (x < InputWidth - 1) ?
1007  ((int)ROUND(ScaleFactor*(x + 1))) : OutputWidth;
1008 
1009  if(IsColor)
1010  for(by = by1, OutputPtr = Output + OutputStride*by; by < by2;
1011  by++, OutputPtr += OutputStride)
1012  for(bx = bx1; bx < bx2; bx++)
1013  {
1014  OutputPtr[3*bx + 0] = Gray;
1015  OutputPtr[3*bx + 1] = Gray;
1016  OutputPtr[3*bx + 2] = Gray;
1017  }
1018  else
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;
1023 
1024  bx1 = bx2;
1025  }
1026 
1027  by1 = by2;
1028  }
1029 
1030  return Output;
1031 }
1032 
1033 
1049 int DrawContours(const char *FileName, int OutputWidth, int OutputHeight,
1050  double ScaleFactor, const int *Stencil, const sset *Set,
1051  const float *Input, int InputWidth, int InputHeight)
1052 {
1053  pen *Pen = NewPen();
1054  pentrans Trans;
1055  float *Output = NULL;
1056  stencilentry *S;
1057  int i, x, y, Success = 0;
1058 
1059  if(!Pen
1060  || !(Output = MakeContoursBgImage(OutputWidth, OutputHeight,
1061  ScaleFactor, Input, InputWidth, InputHeight, 1))
1062  || !PenRenderToImage(Pen, Output, OutputWidth, OutputHeight))
1063  goto Catch;
1064 
1065  for(i = 1; i < GetNumStencils(Set); i++)
1066  {
1067  S = &Set->Stencil[i];
1068  PenSetColor(Pen, S->DrawColor);
1069 
1070  for(y = 0; y < InputHeight; y++)
1071  for(x = 0; x < InputWidth; x++)
1072  if(i == Stencil[x + InputWidth*y])
1073  {
1074  Trans = PenGetTrans(Pen);
1075  PenTransformCanvas(Pen,
1076  ScaleFactor*S->PhiTrans[0],
1077  -ScaleFactor*S->PhiTrans[1],
1078  -ScaleFactor*S->PhiTrans[1],
1079  -ScaleFactor*S->PhiTrans[0],
1080  ScaleFactor*(x + 0.5),
1081  ScaleFactor*(y + 0.5));
1082 
1083  if(S->DrawFun && !(S->DrawFun(Pen, S->DrawParam)))
1084  {
1085  ErrorMessage("Error drawing stencil %d at (%d,%d).\n",
1086  i, x, y);
1087  goto Catch;
1088  }
1089 
1090  PenSetTrans(Pen, Trans);
1091  }
1092  }
1093 
1094  /* Write the output image */
1095  if(!WriteImage(Output, OutputWidth, OutputHeight, FileName,
1096  IMAGEIO_FLOAT | IMAGEIO_RGB, 85))
1097  goto Catch;
1098 
1099  Success = 1;
1100 Catch:
1101  Free(Output);
1102  FreePen(Pen);
1103  return Success;
1104 }
1105 
1106 
1120 int DrawContoursEps(const char *EpsFile,
1121  const int *Stencil, const sset *Set,
1122  const float *Input, int InputWidth, int InputHeight)
1123 {
1124  const int DimScale = 6;
1125  pen *Pen = NewPen();
1126  pentrans Trans;
1127  stencilentry *S;
1128  uint8_t *BgImage = NULL;
1129  int i, x, y, Success = 0;
1130 
1131  if(!Pen
1132  || !(BgImage = (uint8_t *)Malloc(
1133  sizeof(uint8_t)*InputWidth*InputHeight))
1134  || !EpsOpen(Pen, EpsFile,
1135  DimScale*InputWidth, DimScale*InputHeight))
1136  goto Catch;
1137 
1138  fprintf(PenGetFile(Pen), "%d %d scale\n", DimScale, DimScale);
1139 
1140  /* Darken and desaturate image */
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);
1148 
1149  if(!EpsWriteGrayImage(PenGetFile(Pen),
1150  BgImage, InputWidth, InputHeight))
1151  goto Catch;
1152 
1153  PenTranslateCanvas(Pen, 0, InputHeight);
1154  PenScaleCanvas(Pen, 1, -1);
1155 
1156  for(i = 1; i < GetNumStencils(Set); i++)
1157  {
1158  S = &Set->Stencil[i];
1159  PenSetColor(Pen, S->DrawColor);
1160 
1161  for(y = 0; y < InputHeight; y++)
1162  for(x = 0; x < InputWidth; x++)
1163  if(i == Stencil[x + InputWidth*y])
1164  {
1165  Trans = PenGetTrans(Pen);
1166  PenTransformCanvas(Pen,
1167  S->PhiTrans[0], -S->PhiTrans[1],
1168  -S->PhiTrans[1], -S->PhiTrans[0],
1169  (x + 0.5), (y + 0.5));
1170 
1171  if(S->DrawFun && !(S->DrawFun(Pen, S->DrawParam)))
1172  {
1173  ErrorMessage("Error writing \"%s\".\n",
1174  EpsFile);
1175  goto Catch;
1176  }
1177 
1178  PenSetTrans(Pen, Trans);
1179  }
1180  }
1181 
1182  if(!EpsClose(Pen))
1183  {
1184  ErrorMessage("Error writing \"%s\".\n", EpsFile);
1185  goto Catch;
1186  }
1187 
1188  Success = 1;
1189 Catch:
1190  Free(BgImage);
1191  FreePen(Pen);
1192  return Success;
1193 }
1194 
1195 
1210 int DrawContoursSvg(const char *SvgFile, const char *BgFile,
1211  const int *Stencil, const sset *Set,
1212  const float *Input, int InputWidth, int InputHeight)
1213 {
1214  const int DimScale = 9;
1215  pen *Pen = NewPen();
1216  pentrans Trans;
1217  stencilentry *S;
1218  float *BgImage = NULL;
1219  int i, x, y, Success = 0;
1220 
1221  if(!Pen)
1222  goto Catch;
1223 
1224  PenSetNumDigits(Pen, 0);
1225 
1226  for(i = 1; i < GetNumStencils(Set); i++)
1227  if(!PenDefineColor(Pen, Set->Stencil[i].DrawColor))
1228  goto Catch;
1229 
1230  if(!SvgOpen(Pen, SvgFile, DimScale*InputWidth, DimScale*InputHeight))
1231  goto Catch;
1232 
1233  /* Make the background image */
1234  if(BgFile)
1235  {
1236  if(!(BgImage = MakeContoursBgImage(InputWidth, InputHeight,
1237  1, Input, InputWidth, InputHeight, 0))
1238  || !WriteImage(BgImage, InputWidth, InputHeight, BgFile,
1239  IMAGEIO_FLOAT | IMAGEIO_GRAYSCALE, 85))
1240  goto Catch;
1241 
1242  fprintf(PenGetFile(Pen),
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);
1248  }
1249 
1250  for(i = 1; i < GetNumStencils(Set); i++)
1251  {
1252  S = &Set->Stencil[i];
1253  PenSetColor(Pen, S->DrawColor);
1254 
1255  for(y = 0; y < InputHeight; y++)
1256  for(x = 0; x < InputWidth; x++)
1257  if(i == Stencil[x + InputWidth*y])
1258  {
1259  Trans = PenGetTrans(Pen);
1260  PenTransformCanvas(Pen,
1261  DimScale*S->PhiTrans[0], -DimScale*S->PhiTrans[1],
1262  -DimScale*S->PhiTrans[1], -DimScale*S->PhiTrans[0],
1263  DimScale*(x + 0.5), DimScale*(y + 0.5));
1264 
1265  if(S->DrawFun && !(S->DrawFun(Pen, S->DrawParam)))
1266  {
1267  ErrorMessage("Error writing \"%s\".\n",
1268  SvgFile);
1269  goto Catch;
1270  }
1271 
1272  PenSetTrans(Pen, Trans);
1273  }
1274  }
1275 
1276  if(!SvgClose(Pen))
1277  {
1278  ErrorMessage("Error writing \"%s\".\n", SvgFile);
1279  goto Catch;
1280  }
1281 
1282  Success = 1;
1283 Catch:
1284  Free(BgImage);
1285  FreePen(Pen);
1286  return Success;
1287 }