home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / Libraries / VideoToolbox 95.04.18 / Utilities / CalibrateLuminance / CheckContrast.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-17  |  12.4 KB  |  374 lines  |  [TEXT/MMCC]

  1. /*
  2. CheckContrast.c
  3.  
  4. CheckContrast does a rigorous testing of the contrasts produced on your monitor.
  5. It was developed in order to TEST the theory behind the ISR Video Attenuator during
  6. preparation of the Pelli & Zhang (1991) paper. It requires a 12-bit Analog to 
  7. Digital Converter (ADC). At present the code (in GetVoltage.c) assumes the Data 
  8. Translation Forerunner card in slot e.
  9.  
  10. The results including both the measured contrast errors and the theoretical error bounds
  11. are stored in a CricketGraph data file, CheckContrast.data, which may be plotted
  12. using the CricketGraph format file CheckContrast.format. The resulting figure
  13. is comparable to one in Pelli and Zhang (1991).
  14.  
  15. Getting all the errors within the bounds was tough. Everything has to work right, 
  16. including the luminance calibration and the contrast calibration done here. We now
  17. feel that the residual error is due to, in order of decreasing size: 
  18. 1. random measurement error in the contrast calibration, which can be reduced by 
  19. averaging more frames
  20. 2. drifting of the monitor gamma function between the luminance and contrast 
  21. calibrations.
  22. 3. errors of the dac (seem to be within manufacturer spec.)
  23.  
  24. You must supply a LuminanceRecord?.h, (where ? designates which screen, e.g. 1),
  25. as produced by CalibrateLuminance, for your setup, and recompile CheckContrast 
  26. to #include that file.
  27.  
  28. D.G. Pelli and L. Zhang (1991) Accurate control of contrast on microcomputer displays. 
  29. Vision Research, 31:1337-1360.
  30.  
  31. HISTORY
  32.  
  33. Lan ?        Change from RandomLetter.c to measure the real contrast display in the
  34.             testing screen by setting up a TestRect painted by pmForeColor.
  35. Lan 8/4/89    add GetVoltage from Preeti & Denis to measure the real contrast display by ForeRunner
  36. Lan 9/6/89    change contrast to Michelson contrast(set MICHELSON=1), mean luminance is set to
  37.             the mid point of luminance.
  38. 9/11/89 dgp    Increased the delay before each voltage measurement up to 40 ticks, and
  39.             then synched to monitor. This gives the photometer more time to settle, and
  40.             synchs the measurement to the display frame phase. The result is that the
  41.             calibrations are now in spec. Yay!
  42. 9/19/89 Lan Check contrast at different region of screen: divide the screen into 12
  43.             regions, namely, row 1-3, column 1-4.
  44. 10/30/89 dgp Fix bug in computation of tolerance bounds for CheckContrast.data which
  45.             would occasionally introduce a spurious bound at a discontinuity.
  46. 10/30/89 dgp Cleaned up documentation.
  47. 2/12/90    dgp    Incorporated old bug fixes 9/25/89 that by mistake were left out of this
  48.             version: Increased number of digits in printouts, to avoid contrasts becoming equal,
  49.             which confuses CricketGraph. Fixed minor bug in code that detects 
  50.             discontinuity in tolerance. Previously, a reduction in contrast ratio which
  51.             wasn't a discontinuity resulted in one of the tolerances never being 
  52.             initialized and coming out as zero. 
  53. 10/10/90 dgp Renamed the program CheckContrast from "CalibrateContrast".
  54. 10/17/90 dgp Removed unused variables.
  55. 2/16/91     dgp Now check for fpu and color quickdraw.
  56. 8/24/91    dgp    Made compatible with THINK C 5.0.
  57. 3/10/92    dgp    include mc68881.h
  58. 8/27/92    dgp    replace SysEnvirons() by Gestalt()
  59. 2/23/93    dgp    use new GDOpenWindow1 and GDDisposeWindow1.
  60. 12/19/93 dgp use ReadLuminanceRecord. Check for success.
  61. */
  62. #include "VideoToolbox.h"
  63. #include <math.h>
  64. #include "Luminance.h"
  65. double contrastRatio(double t, double L1, double L2);
  66.  
  67. #define MICHELSON    1        /* 1=use Michelson contrast, 0=use Weber contrast */
  68. #define MEASURE    1            /* 1=use ADC, 0=skip measurements to debug */
  69. #define    NCONTRAST    20        /* number of contrasts to test */
  70. #define TRSIZE        160        /* size of test patch, in pixels */
  71. #define LUMINANCE    85.0    /* background luminance in cd/m^2 */
  72. #define NFRAME        200        /* number of total frames used in VoltsDuringFrame */
  73. #define NSAMPLE        2        /* number of repetitions */
  74.                             /* NFRAME/NSAMPLE must be int. */
  75. void CheckContrast(void);
  76.  
  77. void main(void)
  78. {
  79.     Require(gestalt8BitQD);
  80.     CheckContrast();
  81. }
  82.  
  83. void CheckContrast(void)
  84. {
  85.     register short i;
  86.     int j;
  87.     Rect myRect,TestRect;
  88.     WindowPtr window = NULL,oldWindow=NULL;
  89.     double L,L1,L2;
  90.     double contrast,theContrast[NCONTRAST];
  91.     GDHandle device=NULL,oldGDHandle=NULL;
  92.     LuminanceRecord LR;
  93.     static double measuredLTest[NCONTRAST],measuredLBackground[NCONTRAST],measuredContrast[NCONTRAST];
  94.     static double tolerance[NCONTRAST],LTest[NCONTRAST],LBackground[NCONTRAST];
  95.     long int finalTicks;
  96.     FILE *myfile;
  97.     double LuminancePerVolt=1000.0; /* cd/m^2 */
  98.     double e,t;
  99.     double th,tl,ch,cl,r,rh,rl;
  100.     
  101.     SetApplLimit(GetApplLimit()-50000);
  102.     GetPort(&oldWindow);
  103.     printf("\n");    /* init QuickDraw automatically */
  104.  
  105.     /* parameters of the screen calibration */    
  106.     i=ReadLuminanceRecord("LuminanceRecord1.h",&LR,0);
  107.     if(i<=0)PrintfExit("CheckContrast: couldn't find the LuminanceRecord file.\n");
  108.     printf("Using luminance calibration with notes:\n%s\n",LR.notes);
  109.     
  110.     /* Find device corresponding to the experimental screen. */
  111.     oldGDHandle = GetGDevice();
  112.     device = GetScreenDevice(LR.screen);
  113.     if(device==NULL)PrintfExit("CheckContrast: sorry the calibrated device is not"
  114.         " installed\n");
  115.     
  116.     /* Open window once, and keep open for whole experiment. */
  117.     window = GDOpenWindow1(device);
  118.     
  119.     /* create a testing Rect */
  120.     SetGDevice(device);
  121.     SetPort(window);
  122.     myRect = window->portRect;
  123.     
  124.     SetRect(&TestRect, (myRect.right+myRect.left-TRSIZE)/2, (myRect.top+myRect.bottom-TRSIZE)/2, 
  125.         (myRect.right+myRect.left+TRSIZE)/2, (myRect.top+myRect.bottom+TRSIZE)/2 );    
  126.     /*SetRect(&TestRect, (myRect.right-TRSIZE),(myRect.bottom-TRSIZE),myRect.right, myRect.bottom);    
  127.     SetRect(&TestRect, myRect.left, myRect.top,(myRect.left+TRSIZE), (myRect.top+TRSIZE));
  128.     SetRect(&TestRect, (myRect.right-TRSIZE),myRect.top,myRect.right,(myRect.top+TRSIZE));
  129.     SetRect(&TestRect, myRect.left,(myRect.bottom-TRSIZE),(myRect.left+TRSIZE), myRect.bottom);
  130.     
  131.     SetRect(&TestRect, (myRect.left+(COLUMN-1)*TRSIZE), (myRect.top+(ROW-1)*TRSIZE)
  132.                         ,(myRect.left+COLUMN*TRSIZE), (myRect.top+ROW*TRSIZE));*/
  133.  
  134.     contrast=1.0;
  135.     PmBackColor(2);            /* white background */
  136.     EraseRect(&myRect);
  137.     PmBackColor(1);
  138.     EraseRect(&TestRect);    /* test contrast */
  139.     SetPort(oldWindow);
  140.     SetGDevice(oldGDHandle);
  141.     for (i=0; i<NCONTRAST;i++) {
  142.         #if MICHELSON
  143.             L=(LR.LMax+LR.LMin)/2.0;
  144.             L=LR.LBackground;    /* use same background as in CalibrateLuminance */
  145.             L1=L*(1.0-contrast);
  146.             L2=L*(1.0+contrast);
  147.         #else
  148.             L=LUMINANCE;
  149.             if(L>LR.LMax)L=LR.LMax;
  150.             L1=(1.0-contrast)*L;
  151.             L2=L;
  152.         #endif
  153.         SetLuminance(device,&LR,2,L,L1,L2);            /* set background luminance */
  154.         tolerance[i]=SetLuminance(NULL,&LR,1,L1,L1,L2);    /* accuracy+precision */
  155.         tolerance[i]=LR.tolerance;                        /* accuracy */
  156.         SetLuminance(NULL,&LR,1,L1,L1,L2);
  157.         LTest[i]=GetLuminance(NULL,&LR,1);
  158.         SetLuminance(NULL,&LR,1,L2,L1,L2);
  159.         LBackground[i]=GetLuminance(NULL,&LR,1);
  160.         #if MICHELSON
  161.             theContrast[i]=(LBackground[i]-LTest[i])/(LBackground[i]+LTest[i]);
  162.         #else
  163.             theContrast[i]=(LBackground[i]-LTest[i])/LBackground[i];
  164.         #endif
  165.         printf("\nContrast %d, requested %.10f, nominally %.10f\n",(int)i,contrast,theContrast[i]);
  166.         measuredLTest[i]=0.0;
  167.         measuredLBackground[i]=0.0;
  168.         
  169.         for(j=0;j<NSAMPLE;j++)    {
  170.             SetLuminance(device,&LR,1,L1,L1,L2);
  171.             #if MEASURE    
  172.                 /* measure voltage */
  173.                 if(theContrast[i]>0.1)Delay(60L,&finalTicks);
  174.                 if(theContrast[i]>0.01)Delay(6L,&finalTicks);
  175.                 LoadLuminances(device,&LR,1,2);    /* synch to monitor */
  176.                 measuredLTest[i]+=VoltsDuringFrame(NFRAME/NSAMPLE)*LuminancePerVolt/NSAMPLE;
  177.             #endif    
  178.             SetLuminance(device,&LR,1,L2,L1,L2);
  179.             #if MEASURE
  180.                 if(theContrast[i]>0.1)Delay(60L,&finalTicks);
  181.                 if(theContrast[i]>0.01)Delay(6L,&finalTicks);
  182.                 LoadLuminances(device,&LR,1,2);    /* synch to monitor */
  183.                 measuredLBackground[i]+=VoltsDuringFrame(NFRAME/NSAMPLE)*LuminancePerVolt/NSAMPLE;
  184.             #endif            
  185.         }
  186.         #if MICHELSON
  187.             measuredContrast[i]=(measuredLBackground[i]-measuredLTest[i])/(measuredLBackground[i]+measuredLTest[i]);
  188.             e=tolerance[i]/(measuredLBackground[i]+measuredLTest[i]);
  189.         #else
  190.             measuredContrast[i]=(measuredLBackground[i]-measuredLTest[i])/measuredLBackground[i];
  191.             e=tolerance[i]/measuredLBackground[i];
  192.         #endif
  193.         printf("LTest %6.2f %6.2f LBackground %6.2f %6.2f tolerance %6.2f\n",
  194.             LTest[i],measuredLTest[i],LBackground[i],measuredLBackground[i],tolerance[i]);
  195.         printf("contrast %5.5f %5.5f",theContrast[i],measuredContrast[i]);
  196.         printf(" error/tolerance %10.5f\n",(measuredContrast[i]-theContrast[i])/e);
  197.         contrast /= sqrt(2.0);
  198.         FlushEvents(everyEvent,0);
  199.     }
  200.     SetPort(oldWindow);
  201.     SetGDevice(oldGDHandle);
  202.     printf("\007Measurements are done. Now saving results to disk\n");
  203.     /* open and write into file */
  204.     myfile=fopen("CheckContrast.data","w");
  205.     SetFileInfo("CheckContrast.data",'TEXT','CGRF');
  206.     fprintf(myfile,"*\n");
  207.     fprintf(myfile,
  208.         "C\tmC\tmC-C"
  209.         "\t+e"
  210.         "\t-e"
  211.         "\t|mC-C|/C"
  212.         "\te/C"
  213.         "\terror/tolerance"
  214.         "\tmC/C"
  215.         "\t(C+e)/C"
  216.         "\t(C-e)/C"
  217.         "\tLTest\tmLTest\tmLTest-LTest"
  218.         "\tLBackground\tmLBackground\tmLBackground-LBackground"
  219.         "\ttolerance\n");
  220.     for(i=0;i<NCONTRAST;i++){
  221.         /* after first iteration, compute contrast ratio errors */
  222.         if(i>0) {
  223.             rl=contrastRatio(tolerance[i-1],LTest[i-1],LBackground[i-1]);
  224.             rh=contrastRatio(tolerance[i],LTest[i],LBackground[i]);
  225.         }
  226.         /* if there's a discontinuity in tolerance, then insert extra pair of records */
  227.         if(i>0 && rh<rl){
  228.             /* find discontinuity */
  229.             cl=theContrast[i-1];
  230.             ch=theContrast[i];
  231.             tl=tolerance[i-1];
  232.             th=tolerance[i];
  233.             for(j=0;j<15;j++){
  234.                 if((cl-ch)<0.0001) break;    /* Cricket Graph gets confused if (cl-ch) is too small */
  235.                 contrast=(cl+ch)/2.0;
  236.                 #if MICHELSON
  237.                     L=(LR.LMax+LR.LMin)/2.0;
  238.                     L=LR.LBackground;    /* use same background as in CalibrateLuminance */
  239.                     L1=L*(1.0-contrast);
  240.                     L2=L*(1.0+contrast);
  241.                 #else
  242.                     L=LUMINANCE;
  243.                     if(L>LR.LMax)L=LR.LMax;
  244.                     L1=(1.0-contrast)*L;
  245.                     L2=L;
  246.                 #endif
  247.                 t=SetLuminance(NULL,&LR,1,L1,L1,L2);    /* accuracy+precision */
  248.                 t=LR.tolerance;                            /* accuracy */
  249.                 r=contrastRatio(t,L1,L2);
  250.                 if(r>=rl){
  251.                     rl=r;
  252.                     tl=t;
  253.                     cl=contrast;
  254.                 }
  255.                 else {
  256.                     rh=r;
  257.                     th=t;
  258.                     ch=contrast;
  259.                 }
  260.             }
  261.             /* make sure there IS a discontinuity */
  262.             if(rh*1.001<rl){
  263.                 /* print out one record at lower bound w/o data */
  264.                 t=tl;
  265.                 contrast=cl;
  266.                 #if MICHELSON
  267.                     e=t/(L1+L2);
  268.                 #else
  269.                     e=t/L2;
  270.                 #endif
  271.                 #if MICHELSON
  272.                     L=(LR.LMax+LR.LMin)/2.0;
  273.                     L=LR.LBackground;    /* use same background as in CalibrateLuminance */
  274.                     L1=L*(1.0-contrast);
  275.                     L2=L*(1.0+contrast);
  276.                 #else
  277.                     L=LUMINANCE;
  278.                     if(L>LR.LMax)L=LR.LMax;
  279.                     L1=(1.0-contrast)*L;
  280.                     L2=L;
  281.                 #endif
  282.                 fprintf(myfile,"%.10f\t\t\t%.10f\t%.10f\t"
  283.                     ,contrast
  284.                     ,e
  285.                     ,-e
  286.                 );
  287.                 fprintf(myfile,"\t%.10f\t\t\t%.10f\t%.10f\t%.10f\t\t\t%.10f\t\t\t%.10f\n"
  288.                     ,e/contrast
  289.                     ,(contrast+e)/contrast
  290.                     ,(contrast-e)/contrast
  291.                     ,L1
  292.                     ,L2
  293.                     ,t
  294.                 );
  295.                 /* print out one record at upper bound w/o data */
  296.                 t=th;
  297.                 contrast=ch;
  298.                 #if MICHELSON
  299.                     e=t/(L1+L2);
  300.                 #else
  301.                     e=t/L2;
  302.                 #endif
  303.                 #if MICHELSON
  304.                     L=(LR.LMax+LR.LMin)/2.0;
  305.                     L=LR.LBackground;    /* use same background as in CalibrateLuminance */
  306.                     L1=L*(1.0-contrast);
  307.                     L2=L*(1.0+contrast);
  308.                 #else
  309.                     L=LUMINANCE;
  310.                     if(L>LR.LMax)L=LR.LMax;
  311.                     L1=(1.0-contrast)*L;
  312.                     L2=L;
  313.                 #endif
  314.                 fprintf(myfile,"%.10f\t\t\t%.10f\t%.10f\t"
  315.                     ,contrast
  316.                     ,e
  317.                     ,-e
  318.                 );
  319.                 fprintf(myfile,"\t%.10f\t\t\t%.10f\t%.10f\t%.10f\t\t\t%.10f\t\t\t%.10f\n"
  320.                     ,e/contrast
  321.                     ,(contrast+e)/contrast
  322.                     ,(contrast-e)/contrast
  323.                     ,L1
  324.                     ,L2
  325.                     ,t
  326.                 );
  327.             }
  328.         }
  329.         /* print out one record of data */
  330.         #if MICHELSON
  331.             e=tolerance[i]/(LBackground[i]+LTest[i]);
  332.         #else
  333.             e=tolerance[i]/LBackground[i];
  334.         #endif
  335.         fprintf(myfile,"%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t"
  336.             ,theContrast[i]
  337.             ,measuredContrast[i]
  338.             ,measuredContrast[i]-theContrast[i]
  339.             ,e
  340.             ,-e
  341.         );
  342.         fprintf(myfile,"%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t%.10f\n"
  343.             ,fabs(measuredContrast[i]-theContrast[i])/theContrast[i]
  344.             ,e/theContrast[i]
  345.             ,(measuredContrast[i]-theContrast[i])/e
  346.             ,measuredContrast[i]/theContrast[i]
  347.             ,(theContrast[i]+e)/theContrast[i]
  348.             ,(theContrast[i]-e)/theContrast[i]
  349.             ,LTest[i]
  350.             ,measuredLTest[i]
  351.             ,measuredLTest[i]-LTest[i]
  352.             ,LBackground[i]
  353.             ,measuredLBackground[i]
  354.             ,measuredLBackground[i]-LBackground[i]
  355.             ,tolerance[i]
  356.         );
  357.     }
  358.     fclose(myfile);
  359.     SetGDevice(oldGDHandle);
  360.     GDDisposeWindow1(window);
  361. }
  362.  
  363. double contrastRatio(double t, double L1, double L2)
  364. {
  365. double e,contrast;
  366.         #if MICHELSON
  367.             e=t/(L1+L2);
  368.             contrast=fabs(L1-L2)/(L1+L2);
  369.         #else
  370.             e=t/L2;
  371.             contrast=(L2-L1)/L2;
  372.         #endif
  373.         return (contrast+e)/contrast;
  374. }