home *** CD-ROM | disk | FTP | other *** search
/ MacFormat España 21 / macformat_21.iso / Shareware / Programación / VideoToolbox / (Utilities) / CalibrateLuminance / CheckContrast.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-13  |  12.4 KB  |  372 lines  |  [TEXT/CWIE]

  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. 8/27/92    dgp    replace SysEnvirons() by Gestalt()
  58. 2/23/93    dgp    use new GDOpenWindow1 and GDDisposeWindow1.
  59. 12/19/93 dgp use ReadLuminanceRecord. Check for success.
  60. */
  61. #include "VideoToolbox.h"
  62. #include "Luminance.h"
  63. double contrastRatio(double t, double L1, double L2);
  64.  
  65. #define MICHELSON    1        /* 1=use Michelson contrast, 0=use Weber contrast */
  66. #define MEASURE    1            /* 1=use ADC, 0=skip measurements to debug */
  67. #define    NCONTRAST    20        /* number of contrasts to test */
  68. #define TRSIZE        160        /* size of test patch, in pixels */
  69. #define LUMINANCE    85.0    /* background luminance in cd/m^2 */
  70. #define NFRAME        200        /* number of total frames used in VoltsDuringFrame */
  71. #define NSAMPLE        2        /* number of repetitions */
  72.                             /* NFRAME/NSAMPLE must be int. */
  73. void CheckContrast(void);
  74.  
  75. void main(void)
  76. {
  77.     Require(gestalt8BitQD);
  78.     CheckContrast();
  79. }
  80.  
  81. void CheckContrast(void)
  82. {
  83.     register short i;
  84.     int j;
  85.     Rect myRect,TestRect;
  86.     WindowPtr window = NULL,oldWindow=NULL;
  87.     double L,L1,L2;
  88.     double contrast,theContrast[NCONTRAST];
  89.     GDHandle device=NULL,oldGDHandle=NULL;
  90.     LuminanceRecord LR;
  91.     static double measuredLTest[NCONTRAST],measuredLBackground[NCONTRAST],measuredContrast[NCONTRAST];
  92.     static double tolerance[NCONTRAST],LTest[NCONTRAST],LBackground[NCONTRAST];
  93.     long int finalTicks;
  94.     FILE *myfile;
  95.     double LuminancePerVolt=1000.0; /* cd/m^2 */
  96.     double e,t;
  97.     double th,tl,ch,cl,r,rh,rl;
  98.     
  99.     SetApplLimit(GetApplLimit()-50000);
  100.     GetPort(&oldWindow);
  101.     printf("\n");    /* init QuickDraw automatically */
  102.  
  103.     /* parameters of the screen calibration */    
  104.     i=ReadLuminanceRecord("LuminanceRecord1.h",&LR,0);
  105.     if(i<=0)PrintfExit("CheckContrast: couldn't find the LuminanceRecord file.\n");
  106.     printf("Using luminance calibration with notes:\n%s\n",LR.notes);
  107.     
  108.     /* Find device corresponding to the experimental screen. */
  109.     oldGDHandle = GetGDevice();
  110.     device = GetScreenDevice(LR.screen);
  111.     if(device==NULL)PrintfExit("CheckContrast: sorry the calibrated device is not"
  112.         " installed\n");
  113.     
  114.     /* Open window once, and keep open for whole experiment. */
  115.     window = GDOpenWindow1(device);
  116.     
  117.     /* create a testing Rect */
  118.     SetGDevice(device);
  119.     SetPort(window);
  120.     myRect = window->portRect;
  121.     
  122.     SetRect(&TestRect, (myRect.right+myRect.left-TRSIZE)/2, (myRect.top+myRect.bottom-TRSIZE)/2, 
  123.         (myRect.right+myRect.left+TRSIZE)/2, (myRect.top+myRect.bottom+TRSIZE)/2 );    
  124.     /*SetRect(&TestRect, (myRect.right-TRSIZE),(myRect.bottom-TRSIZE),myRect.right, myRect.bottom);    
  125.     SetRect(&TestRect, myRect.left, myRect.top,(myRect.left+TRSIZE), (myRect.top+TRSIZE));
  126.     SetRect(&TestRect, (myRect.right-TRSIZE),myRect.top,myRect.right,(myRect.top+TRSIZE));
  127.     SetRect(&TestRect, myRect.left,(myRect.bottom-TRSIZE),(myRect.left+TRSIZE), myRect.bottom);
  128.     
  129.     SetRect(&TestRect, (myRect.left+(COLUMN-1)*TRSIZE), (myRect.top+(ROW-1)*TRSIZE)
  130.                         ,(myRect.left+COLUMN*TRSIZE), (myRect.top+ROW*TRSIZE));*/
  131.  
  132.     contrast=1.0;
  133.     PmBackColor(2);            /* white background */
  134.     EraseRect(&myRect);
  135.     PmBackColor(1);
  136.     EraseRect(&TestRect);    /* test contrast */
  137.     SetPort(oldWindow);
  138.     SetGDevice(oldGDHandle);
  139.     for (i=0; i<NCONTRAST;i++) {
  140.         #if MICHELSON
  141.             L=(LR.LMax+LR.LMin)/2.0;
  142.             L=LR.LBackground;    /* use same background as in CalibrateLuminance */
  143.             L1=L*(1.0-contrast);
  144.             L2=L*(1.0+contrast);
  145.         #else
  146.             L=LUMINANCE;
  147.             if(L>LR.LMax)L=LR.LMax;
  148.             L1=(1.0-contrast)*L;
  149.             L2=L;
  150.         #endif
  151.         SetLuminance(device,&LR,2,L,L1,L2);            /* set background luminance */
  152.         tolerance[i]=SetLuminance(NULL,&LR,1,L1,L1,L2);    /* accuracy+precision */
  153.         tolerance[i]=LR.tolerance;                        /* accuracy */
  154.         SetLuminance(NULL,&LR,1,L1,L1,L2);
  155.         LTest[i]=GetLuminance(NULL,&LR,1);
  156.         SetLuminance(NULL,&LR,1,L2,L1,L2);
  157.         LBackground[i]=GetLuminance(NULL,&LR,1);
  158.         #if MICHELSON
  159.             theContrast[i]=(LBackground[i]-LTest[i])/(LBackground[i]+LTest[i]);
  160.         #else
  161.             theContrast[i]=(LBackground[i]-LTest[i])/LBackground[i];
  162.         #endif
  163.         printf("\nContrast %d, requested %.10f, nominally %.10f\n",(int)i,contrast,theContrast[i]);
  164.         measuredLTest[i]=0.0;
  165.         measuredLBackground[i]=0.0;
  166.         
  167.         for(j=0;j<NSAMPLE;j++)    {
  168.             SetLuminance(device,&LR,1,L1,L1,L2);
  169.             #if MEASURE    
  170.                 /* measure voltage */
  171.                 if(theContrast[i]>0.1)Delay(60L,&finalTicks);
  172.                 if(theContrast[i]>0.01)Delay(6L,&finalTicks);
  173.                 LoadLuminances(device,&LR,1,2);    /* synch to monitor */
  174.                 measuredLTest[i]+=VoltsDuringFrame(NFRAME/NSAMPLE)*LuminancePerVolt/NSAMPLE;
  175.             #endif    
  176.             SetLuminance(device,&LR,1,L2,L1,L2);
  177.             #if MEASURE
  178.                 if(theContrast[i]>0.1)Delay(60L,&finalTicks);
  179.                 if(theContrast[i]>0.01)Delay(6L,&finalTicks);
  180.                 LoadLuminances(device,&LR,1,2);    /* synch to monitor */
  181.                 measuredLBackground[i]+=VoltsDuringFrame(NFRAME/NSAMPLE)*LuminancePerVolt/NSAMPLE;
  182.             #endif            
  183.         }
  184.         #if MICHELSON
  185.             measuredContrast[i]=(measuredLBackground[i]-measuredLTest[i])/(measuredLBackground[i]+measuredLTest[i]);
  186.             e=tolerance[i]/(measuredLBackground[i]+measuredLTest[i]);
  187.         #else
  188.             measuredContrast[i]=(measuredLBackground[i]-measuredLTest[i])/measuredLBackground[i];
  189.             e=tolerance[i]/measuredLBackground[i];
  190.         #endif
  191.         printf("LTest %6.2f %6.2f LBackground %6.2f %6.2f tolerance %6.2f\n",
  192.             LTest[i],measuredLTest[i],LBackground[i],measuredLBackground[i],tolerance[i]);
  193.         printf("contrast %5.5f %5.5f",theContrast[i],measuredContrast[i]);
  194.         printf(" error/tolerance %10.5f\n",(measuredContrast[i]-theContrast[i])/e);
  195.         contrast /= sqrt(2.0);
  196.         FlushEvents(everyEvent,0);
  197.     }
  198.     SetPort(oldWindow);
  199.     SetGDevice(oldGDHandle);
  200.     printf("\007Measurements are done. Now saving results to disk\n");
  201.     /* open and write into file */
  202.     myfile=fopen("CheckContrast.data","w");
  203.     SetFileInfo("CheckContrast.data",'TEXT','CGRF');
  204.     fprintf(myfile,"*\n");
  205.     fprintf(myfile,
  206.         "C\tmC\tmC-C"
  207.         "\t+e"
  208.         "\t-e"
  209.         "\t|mC-C|/C"
  210.         "\te/C"
  211.         "\terror/tolerance"
  212.         "\tmC/C"
  213.         "\t(C+e)/C"
  214.         "\t(C-e)/C"
  215.         "\tLTest\tmLTest\tmLTest-LTest"
  216.         "\tLBackground\tmLBackground\tmLBackground-LBackground"
  217.         "\ttolerance\n");
  218.     for(i=0;i<NCONTRAST;i++){
  219.         /* after first iteration, compute contrast ratio errors */
  220.         if(i>0) {
  221.             rl=contrastRatio(tolerance[i-1],LTest[i-1],LBackground[i-1]);
  222.             rh=contrastRatio(tolerance[i],LTest[i],LBackground[i]);
  223.         }
  224.         /* if there's a discontinuity in tolerance, then insert extra pair of records */
  225.         if(i>0 && rh<rl){
  226.             /* find discontinuity */
  227.             cl=theContrast[i-1];
  228.             ch=theContrast[i];
  229.             tl=tolerance[i-1];
  230.             th=tolerance[i];
  231.             for(j=0;j<15;j++){
  232.                 if((cl-ch)<0.0001) break;    /* Cricket Graph gets confused if (cl-ch) is too small */
  233.                 contrast=(cl+ch)/2.0;
  234.                 #if MICHELSON
  235.                     L=(LR.LMax+LR.LMin)/2.0;
  236.                     L=LR.LBackground;    /* use same background as in CalibrateLuminance */
  237.                     L1=L*(1.0-contrast);
  238.                     L2=L*(1.0+contrast);
  239.                 #else
  240.                     L=LUMINANCE;
  241.                     if(L>LR.LMax)L=LR.LMax;
  242.                     L1=(1.0-contrast)*L;
  243.                     L2=L;
  244.                 #endif
  245.                 t=SetLuminance(NULL,&LR,1,L1,L1,L2);    /* accuracy+precision */
  246.                 t=LR.tolerance;                            /* accuracy */
  247.                 r=contrastRatio(t,L1,L2);
  248.                 if(r>=rl){
  249.                     rl=r;
  250.                     tl=t;
  251.                     cl=contrast;
  252.                 }
  253.                 else {
  254.                     rh=r;
  255.                     th=t;
  256.                     ch=contrast;
  257.                 }
  258.             }
  259.             /* make sure there IS a discontinuity */
  260.             if(rh*1.001<rl){
  261.                 /* print out one record at lower bound w/o data */
  262.                 t=tl;
  263.                 contrast=cl;
  264.                 #if MICHELSON
  265.                     e=t/(L1+L2);
  266.                 #else
  267.                     e=t/L2;
  268.                 #endif
  269.                 #if MICHELSON
  270.                     L=(LR.LMax+LR.LMin)/2.0;
  271.                     L=LR.LBackground;    /* use same background as in CalibrateLuminance */
  272.                     L1=L*(1.0-contrast);
  273.                     L2=L*(1.0+contrast);
  274.                 #else
  275.                     L=LUMINANCE;
  276.                     if(L>LR.LMax)L=LR.LMax;
  277.                     L1=(1.0-contrast)*L;
  278.                     L2=L;
  279.                 #endif
  280.                 fprintf(myfile,"%.10f\t\t\t%.10f\t%.10f\t"
  281.                     ,contrast
  282.                     ,e
  283.                     ,-e
  284.                 );
  285.                 fprintf(myfile,"\t%.10f\t\t\t%.10f\t%.10f\t%.10f\t\t\t%.10f\t\t\t%.10f\n"
  286.                     ,e/contrast
  287.                     ,(contrast+e)/contrast
  288.                     ,(contrast-e)/contrast
  289.                     ,L1
  290.                     ,L2
  291.                     ,t
  292.                 );
  293.                 /* print out one record at upper bound w/o data */
  294.                 t=th;
  295.                 contrast=ch;
  296.                 #if MICHELSON
  297.                     e=t/(L1+L2);
  298.                 #else
  299.                     e=t/L2;
  300.                 #endif
  301.                 #if MICHELSON
  302.                     L=(LR.LMax+LR.LMin)/2.0;
  303.                     L=LR.LBackground;    /* use same background as in CalibrateLuminance */
  304.                     L1=L*(1.0-contrast);
  305.                     L2=L*(1.0+contrast);
  306.                 #else
  307.                     L=LUMINANCE;
  308.                     if(L>LR.LMax)L=LR.LMax;
  309.                     L1=(1.0-contrast)*L;
  310.                     L2=L;
  311.                 #endif
  312.                 fprintf(myfile,"%.10f\t\t\t%.10f\t%.10f\t"
  313.                     ,contrast
  314.                     ,e
  315.                     ,-e
  316.                 );
  317.                 fprintf(myfile,"\t%.10f\t\t\t%.10f\t%.10f\t%.10f\t\t\t%.10f\t\t\t%.10f\n"
  318.                     ,e/contrast
  319.                     ,(contrast+e)/contrast
  320.                     ,(contrast-e)/contrast
  321.                     ,L1
  322.                     ,L2
  323.                     ,t
  324.                 );
  325.             }
  326.         }
  327.         /* print out one record of data */
  328.         #if MICHELSON
  329.             e=tolerance[i]/(LBackground[i]+LTest[i]);
  330.         #else
  331.             e=tolerance[i]/LBackground[i];
  332.         #endif
  333.         fprintf(myfile,"%.10f\t%.10f\t%.10f\t%.10f\t%.10f\t"
  334.             ,theContrast[i]
  335.             ,measuredContrast[i]
  336.             ,measuredContrast[i]-theContrast[i]
  337.             ,e
  338.             ,-e
  339.         );
  340.         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"
  341.             ,fabs(measuredContrast[i]-theContrast[i])/theContrast[i]
  342.             ,e/theContrast[i]
  343.             ,(measuredContrast[i]-theContrast[i])/e
  344.             ,measuredContrast[i]/theContrast[i]
  345.             ,(theContrast[i]+e)/theContrast[i]
  346.             ,(theContrast[i]-e)/theContrast[i]
  347.             ,LTest[i]
  348.             ,measuredLTest[i]
  349.             ,measuredLTest[i]-LTest[i]
  350.             ,LBackground[i]
  351.             ,measuredLBackground[i]
  352.             ,measuredLBackground[i]-LBackground[i]
  353.             ,tolerance[i]
  354.         );
  355.     }
  356.     fclose(myfile);
  357.     SetGDevice(oldGDHandle);
  358.     GDDisposeWindow1(window);
  359. }
  360.  
  361. double contrastRatio(double t, double L1, double L2)
  362. {
  363. double e,contrast;
  364.         #if MICHELSON
  365.             e=t/(L1+L2);
  366.             contrast=fabs(L1-L2)/(L1+L2);
  367.         #else
  368.             e=t/L2;
  369.             contrast=(L2-L1)/L2;
  370.         #endif
  371.         return (contrast+e)/contrast;
  372. }