home *** CD-ROM | disk | FTP | other *** search
- /*
- Copyright 1990 by John Wiley & Sons, Inc.
- All Rights Reserved.
- */
- /**********************************************************/
- /*** ***/
- /*** Video Digitizer Program ***/
- /*** produces color images from monochrome camera ***/
- /*** utilizes the 256 color 320x200 VGA mode ***/
- /*** written in Turbo C ***/
- /*** by ***/
- /*** Craig A. Lindley ***/
- /*** Median Cut Algorithm code developed by ***/
- /*** Dan Butterfield ***/
- /*** ***/
- /*** Usage: ***/
- /*** cvideo [-a -d -h -o -p -s -v -x] filename <cr> ***/
- /*** -a correct aspect ratio of image ***/
- /*** -d use Floyd-Steinberg dithering ***/
- /*** -h or ? show help ***/
- /*** -o calculates optimum color palette ***/
- /*** -p create PCX output file ***/
- /*** -s scales color register values to max ***/
- /*** -v displays program progress information ***/
- /*** -x create executable display program ***/
- /*** filename is name given to output files ***/
- /*** ***/
- /*** Ver: 1.0 Last Update: 12/05/89 ***/
- /**********************************************************/
-
- #include <stdio.h>
- #include <conio.h>
- #include <dos.h>
- #include <process.h>
- #include <graphics.h>
- #include <string.h>
- #include <alloc.h>
- #include <math.h>
- #include "vga.h"
- #include "misc.h"
- #include "pcx.h"
- #include "digitize.h"
-
- /*
- Please see chapter 12 for a complete discussion of aspect ratio
- problems and correction techniques. What we will do in this
- program is to acquire three 320x240 images and compress them into
- three 320x200 images correcting the aspect ratio problem caused by
- the VGA graphics adapter. The three corrected images will then be
- used to produce a single full color aspect ratio corrected image.
- The aspect ratio correction is performed in the function
- AspectCorrect.
- */
- #define ROWASPECTCORRECTION (double) 1.2
- #define SOURCEIMAGEROWS 240
-
- /*
- Memory allocation bits used to keep track of how much far heap memory
- has been allocated by this program.
- */
-
- #define RGBMEM 1
- #define REDMEM 2
- #define GREENMEM 4
- #define BLUEMEM 8
- #define IMAGEMEM 16
-
- unsigned MemoryAlloc = 0; /* variable used to keep track */
-
- #define COLLEVELS 32 /* number of brightness levels */
- #define MAXNUMCOLREGS 256 /* max num of color registers */
- #define NUMCOLS 320 /* image dimensions */
- #define NUMROWS 200
- #define NUMAXIS 3 /* num of axes in RGB cube */
- #define NUMPIXELS (long)NUMROWS*NUMCOLS /* total pixels in image */
- #define SQUARE(x) (x)*(x)
-
- union REGS regs;
-
- char huge *Red; /* pts to image data */
- char huge *Green;
- char huge *Blue;
- char huge *ImageBuf;
-
- struct ImageReq Req; /* image request structure */
-
- /*
- RGBCube: Three dimensional array implemented in such a way as to work well
- on a machine that has trouble with objects greater than 64K. The indices
- into this array are the color components, rgb, normalized to fit in
- the range defined by COLLEVELS. The values in the array are frequency counts
- of the particular color. The array is set up here to be an array of pointers
- to smaller 2 dimensional arrays, so no object is greater than 64K.
- */
-
- unsigned far *RGBCube[COLLEVELS];
- unsigned NumBoxes;
- unsigned FloydStein;
- unsigned OptimumColors;
- unsigned Verbose;
- unsigned GenComFile;
- unsigned GenPCXFile;
- unsigned ScaleColRegs;
- unsigned CorrectAspectRatio;
- /*
- Boxes: Structure holding the unsorted generated color boxes. Includes the
- low and high value along each axis of RGBCube, and the number of elements
- in the box.
- */
-
- struct Box
- {
- unsigned Lo[3];
- unsigned Hi[3];
- unsigned long NumElements;
- } Boxes[MAXNUMCOLREGS];
-
- /*
- Sorted version of Boxes.
- */
-
- struct Box SBoxes[MAXNUMCOLREGS];
-
- /*
- ColRegs: Holds the determined values of the color registers.
- */
-
- struct Regs
- {
- BYTE Red;
- BYTE Green;
- BYTE Blue;
- } ColRegs[MAXNUMCOLREGS];
-
- /*
- SColRegs: Sorted version of ColRegs.
- */
-
- struct Regs SColRegs[MAXNUMCOLREGS];
-
-
- /* beginning of program functions */
-
- /*
- This function will output the message string passed to it if the Verbose
- option variable is set true.
- */
-
- void Message(char *String)
- {
- if (Verbose)
- printf("%s",String);
- }
-
- /*
- This function deallocates all far heap memory that has been allocated
- */
-
- void DeAllocMemory( void )
- {
- register unsigned Index;
-
- /* test MemoryAlloc bit to see what was allocated then dispose */
-
- Message("Deallocating program memory\n");
-
- if (MemoryAlloc & RGBMEM)
- for (Index=0; Index < COLLEVELS; Index++)
- if (RGBCube[Index] != NULL)
- farfree((unsigned far *) RGBCube[Index]);
-
- if (MemoryAlloc & REDMEM)
- farfree((char far *) Red);
- if (MemoryAlloc & GREENMEM)
- farfree((char far *) Green);
- if (MemoryAlloc & BLUEMEM)
- farfree((char far *) Blue);
- if (MemoryAlloc & IMAGEMEM)
- farfree((char far *) ImageBuf);
- }
-
- /*
- Allocate all memory for the entire program. The MemoryAlloc variable is
- used to keep track of all memory that has been allocated from the far
- heap. That way DeAllocMemory can give it all back to the system when the
- program terminates.
- */
-
- unsigned AllocMemory( void )
- {
- register unsigned Index;
-
- Message("Allocating program memory\n");
-
- /* create COLLEVELS number of pointer to 2 D arrays */
-
- /*
- Set the initial values of the RGBCube sub-array pointers to NULL,
- so we can do proper checks later to see if they have been allocated
- or not.
- */
-
- for (Index=0; Index < COLLEVELS; Index++)
- RGBCube[Index] = NULL;
-
- for (Index=0; Index < COLLEVELS; Index++)
- {
- RGBCube[Index] = (unsigned far *) farcalloc(COLLEVELS*COLLEVELS,
- (unsigned long) sizeof(short));
-
- if (RGBCube[Index] == NULL)
- {
- printf("RGBCube memory allocation failed\n");
- printf("\t only %ld bytes of memory available\n",farcoreleft());
- DeAllocMemory();
- return(FALSE);
- }
- }
- MemoryAlloc |= RGBMEM; /* indicate success */
-
- Red = (char huge *) farcalloc(NUMPIXELS,(unsigned long) sizeof(char));
- if (Red == NULL)
- {
- printf("Red allocation failed\n");
- printf("\t only %ld bytes of memory available\n",farcoreleft());
- DeAllocMemory();
- return(FALSE);
- }
- MemoryAlloc |= REDMEM; /* indicate success */
-
- Green = (char huge *) farcalloc(NUMPIXELS,(unsigned long) sizeof(char));
- if (Green == NULL)
- {
- printf("Green allocation failed\n");
- printf("\t only %ld bytes of memory available\n",farcoreleft());
- DeAllocMemory();
- return(FALSE);
- }
- MemoryAlloc |= GREENMEM; /* indicate success */
-
- Blue = (char huge *) farcalloc(NUMPIXELS,(unsigned long) sizeof(char));
- if (Blue == NULL)
- {
- printf("Blue allocation failed\n");
- printf("\t only %ld bytes of memory available\n",farcoreleft());
- DeAllocMemory();
- return(FALSE);
- }
- MemoryAlloc |= BLUEMEM; /* indicate success */
-
- /* allocate buffer into which all three images will be acquired */
- ImageBuf = (char huge *) farcalloc(320L*240L,(unsigned long) sizeof(char));
- if (ImageBuf == NULL)
- {
- printf("Image buffer allocation failed\n");
- printf("\t only %ld bytes of memory available\n",farcoreleft());
- DeAllocMemory();
- return(FALSE);
- }
- MemoryAlloc |= IMAGEMEM; /* indicate success */
- return(TRUE);
- }
-
-
- /*
- This function forces the input data to fit in the range described by
- COLLEVELS. When it is complete, the specified array will only contain
- values between 0 and COLLEVELS-1 inclusive.
- */
-
- void Normalize(char huge *ColorData)
- {
- register BYTE Min, Max;
- unsigned long LongIndex;
-
- /* find minimum and maximum values in the array. */
-
- Min = 0;
- Max = 0;
-
- for (LongIndex=0; LongIndex < NUMPIXELS; LongIndex++)
- {
- if (ColorData[LongIndex] < Min)
- Min = ColorData[LongIndex];
- if (ColorData[LongIndex] > Max)
- Max = ColorData[LongIndex];
- }
-
- /* Force each pixel to the range 0..COLLEVELS-1 */
-
- for (LongIndex=0; LongIndex < NUMPIXELS; LongIndex++)
- ColorData[LongIndex] = ((ColorData[LongIndex]-Min)*(COLLEVELS-1))/
- (Max-Min);
- }
-
- /*
- This function fills the RGBCube array with the color frequencies, and as a
- side effect, counts and prints the number of unique colors in the input.
- */
-
- void ScanColorFrequencies( void )
- {
- register unsigned c, k, NumColors;
- unsigned long LongIndex;
-
- /* Initialize RGBCube to all zeros */
-
- for (c=0; c < COLLEVELS; c++)
- for (k=0; k < COLLEVELS*COLLEVELS; k++)
- RGBCube[c][k] = 0;
-
- /* For each pixel, count the number of pixels with that color */
-
- for (LongIndex=0; LongIndex < NUMPIXELS; LongIndex++)
- RGBCube[Red[LongIndex]][(Blue[LongIndex]*COLLEVELS)+Green[LongIndex]]++;
-
- /*
- Count and print the number of unique colors in the input by scanning the
- RGBCube array and looking for non-zero frequencies.
- */
-
- NumColors = 0;
- for (c=0; c < COLLEVELS; c++)
- for (k=0; k < COLLEVELS*COLLEVELS; k++)
- if (RGBCube[c][k])
- NumColors++;
-
- if (Verbose)
- printf("%d unique colors in image data\n",NumColors);
- }
-
- /*
- This function sets the indices to the numbers of the other axes after
- a main axis has been selected.
- */
-
- void OtherAxes(unsigned mainaxis,unsigned *other1,unsigned *other2)
- {
- switch (mainaxis)
- {
- case 0:
- *other1 = 1;
- *other2 = 2;
- break;
- case 1:
- *other1 = 0;
- *other2 = 2;
- break;
- case 2:
- *other1 = 0;
- *other2 = 1;
- }
- }
-
-
- /*
- This function takes a index value into the Boxes array, and shrinks the
- specified box to tightly fit around the input color frequency data (eg.
- there are no zero planes on the sides of the box).
- */
-
- void Shrink(unsigned BoxIndex)
- {
- unsigned axis,aax1,aax2;
- register unsigned ind[3], flag;
-
- /* Along each axis: */
-
- for (axis=0; axis < NUMAXIS; axis++)
- {
- OtherAxes(axis,&aax1,&aax2);
-
- /* Scan off zero planes on from the low end of the axis */
-
- flag = 0;
- for (ind[axis]=Boxes[BoxIndex].Lo[axis];
- ind[axis] <= Boxes[BoxIndex].Hi[axis]; ind[axis]++)
- {
- for (ind[aax1]=Boxes[BoxIndex].Lo[aax1];
- ind[aax1] <= Boxes[BoxIndex].Hi[aax1]; ind[aax1]++)
- {
- for (ind[aax2]=Boxes[BoxIndex].Lo[aax2];
- ind[aax2] <= Boxes[BoxIndex].Hi[aax2]; ind[aax2]++)
- if (RGBCube[ind[0]][ind[1]*COLLEVELS+ind[2]])
- {
- flag=1;
- break;
- }
- if (flag) break;
- }
- if (flag) break;
- }
- Boxes[BoxIndex].Lo[axis] = ind[axis];
-
- /* Scan off zero planes from the high end of the axis */
- flag = 0;
- for (ind[axis]=Boxes[BoxIndex].Hi[axis];
- ind[axis]+1 >= Boxes[BoxIndex].Lo[axis]+1; ind[axis]--)
- {
- for (ind[aax1]=Boxes[BoxIndex].Hi[aax1];
- ind[aax1]+1 >= Boxes[BoxIndex].Lo[aax1]+1; ind[aax1]--)
- {
- for (ind[aax2]=Boxes[BoxIndex].Hi[aax2];
- ind[aax2]+1>=Boxes[BoxIndex].Lo[aax2]+1; ind[aax2]--)
- if (RGBCube[ind[0]][ind[1]*COLLEVELS+ind[2]])
- {
- flag = 1;
- break;
- }
- if (flag) break;
- }
- if (flag) break;
- }
- Boxes[BoxIndex].Hi[axis] = ind[axis];
- }
- }
-
- /* print box debug function */
- void PrtBox(unsigned BoxIndex)
- {
- printf("\nBox Number %d\n",BoxIndex);
- printf("Hi[0]=%d Lo[0]=%d\n",Boxes[BoxIndex].Hi[0],Boxes[BoxIndex].Lo[0]);
- printf("Hi[1]=%d Lo[1]=%d\n",Boxes[BoxIndex].Hi[1],Boxes[BoxIndex].Lo[1]);
- printf("Hi[2]=%d Lo[2]=%d\n",Boxes[BoxIndex].Hi[2],Boxes[BoxIndex].Lo[2]);
- printf("Elements %ld\n\n",Boxes[BoxIndex].NumElements);
- getch();
- }
-
-
- /*
- This function selects the optimum colors from the color frequency data,
- using the Median Cut algorithm. It prints the number of colors used at
- its termination.
- */
-
- void SelectColorBoxes( void )
- {
- register unsigned SelectedBox, c;
- register unsigned ind[3], Max, axis, TargetBox, k;
- unsigned aax1,aax2;
- unsigned long LongMax, PlaneSum, ElementSum;
-
- /*
- Initialize the first and only box in the array to contain the entire RGBCube,
- then discard unused zero planes surrounding it.
- */
-
- for (c=0; c < NUMAXIS; c++)
- {
- Boxes[0].Lo[c] = 0;
- Boxes[0].Hi[c] = COLLEVELS-1;
- }
- Boxes[0].NumElements = NUMPIXELS;
- NumBoxes = 1;
-
- Shrink(0);
-
- /* Perform the following until all color registers are used up */
-
- while(NumBoxes < MAXNUMCOLREGS)
- {
- /*
- Pick the box with the maximum number of elements that is not a single
- color value to work with. It will be the box we will split.
- */
-
- LongMax = 0;
- SelectedBox = 1000;
- for (c=0; c < NumBoxes; c++)
- {
- if ((Boxes[c].NumElements > LongMax) &&
- ((Boxes[c].Lo[0] != Boxes[c].Hi[0]) ||
- (Boxes[c].Lo[1] != Boxes[c].Hi[1]) ||
- (Boxes[c].Lo[2] != Boxes[c].Hi[2])))
- {
- LongMax = Boxes[c].NumElements;
- SelectedBox = c;
- }
- }
-
- /*
- If we couldn't find any box that was not a single color, we don't
- need to assign any more colors, so we can terminate this loop.
- */
-
- if (SelectedBox == 1000)
- break;
-
- /* Choose the longest axis of the box to split it along */
-
- axis = 0;
- Max = Boxes[SelectedBox].Hi[axis] - Boxes[SelectedBox].Lo[axis];
- for (k=1; k < NUMAXIS; k++)
- {
- if (Max < (c=(Boxes[SelectedBox].Hi[k]-Boxes[SelectedBox].Lo[k])))
- {
- Max = c;
- axis = k;
- }
- }
-
- /*
- Check to see if any of our previously assigned boxes have zero elements
- (may happen in degenerate cases), if so, re-use them. If not, use the
- next available box.
- */
-
- TargetBox = NumBoxes;
- for (c=0; c < NumBoxes; c++)
- {
- if (Boxes[c].NumElements == 0)
- {
- TargetBox = c;
- break;
- }
- }
-
- OtherAxes(axis,&aax1,&aax2);
- if (Boxes[SelectedBox].Hi[axis] != Boxes[SelectedBox].Lo[axis])
- {
-
- /*
- Sum planes of box from low end until the sum exceeds half the total
- number of elements in the box. That is the point where we will
- split it.
- */
-
- ElementSum = 0;
- for (ind[axis]=Boxes[SelectedBox].Lo[axis];
- ind[axis] <= Boxes[SelectedBox].Hi[axis]; ind[axis]++)
- {
- PlaneSum = 0;
- for (ind[aax1]=Boxes[SelectedBox].Lo[aax1];
- ind[aax1] <= Boxes[SelectedBox].Hi[aax1]; ind[aax1]++)
- for (ind[aax2]=Boxes[SelectedBox].Lo[aax2];
- ind[aax2] <= Boxes[SelectedBox].Hi[aax2]; ind[aax2]++)
- PlaneSum += RGBCube[ind[0]][ind[1]*COLLEVELS+ind[2]];
- ElementSum += PlaneSum;
- if (ElementSum > Boxes[SelectedBox].NumElements/2)
- break;
- }
- /*
- If we did not exceed half the total until we added the last plane
- (such as in a case where the last plane contains the bulk of the data
- points), back up so we do not create the new box as a degenerate box.
- */
-
- if (ind[axis] == Boxes[SelectedBox].Hi[axis])
- {
- ind[axis]--;
- ElementSum -= PlaneSum;
- }
-
- /*
- The new box has most of the data the same as the old box, but its low
- extent is the index above the point where we needed to split, and its
- number of elements is the total number of elements in this whole box,
- minus the number in the planes we just summed.
- */
-
- for (c=0; c < NUMAXIS; c++)
- {
- Boxes[TargetBox].Lo[c] = Boxes[SelectedBox].Lo[c];
- Boxes[TargetBox].Hi[c] = Boxes[SelectedBox].Hi[c];
- }
- Boxes[TargetBox].Lo[axis] = ind[axis]+1;
- Boxes[TargetBox].NumElements = Boxes[SelectedBox].NumElements -
- ElementSum;
-
- /*
- The high extent of our old box is now cut off at the plane we just
- split at and the number of elements in it is the number we just
- summed.
- */
-
- Boxes[SelectedBox].Hi[axis] = ind[axis];
- Boxes[SelectedBox].NumElements = ElementSum;
-
- /* Discard zero planes around both our new boxes */
-
- Shrink(SelectedBox);
- Shrink(TargetBox);
-
- /*
- If we used the top box in our list, we have to increment the
- total number of boxes used, to make ready for the use of the next
- free box.
- */
-
- if (TargetBox == NumBoxes)
- NumBoxes++;
- }
- }
-
- /* show number of display colors to be used if requested to */
- if (Verbose)
- printf("%d colors will be used for display of the image\n",NumBoxes);
- }
-
-
- /*
- This function calculates the actual color register values for each box,
- based on the weighted distribution of data in the box. It then sorts the
- color registers by brightness (using a calculation described by the VGA
- technical reference for calculating brightness).
- */
-
- void SortColors( void )
- {
- register unsigned Index,c,flag,temp,r,b,g;
- unsigned indices[MAXNUMCOLREGS];
- unsigned long weightedcolor[MAXNUMCOLREGS],rsum,bsum,gsum,tmp;
-
- for (Index=0; Index < NumBoxes; Index++)
- {
-
- /* Calculate a weighted sum of the color values in the box */
-
- rsum = bsum = gsum = 0;
- for (r=Boxes[Index].Lo[0]; r<=Boxes[Index].Hi[0]; r++)
- for (b=Boxes[Index].Lo[1]; b<=Boxes[Index].Hi[1]; b++)
- for (g=Boxes[Index].Lo[2]; g<=Boxes[Index].Hi[2]; g++)
- {
- tmp = RGBCube[r][b*COLLEVELS+g];
- rsum += r*tmp;
- bsum += b*tmp;
- gsum += g*tmp;
- }
-
- /* Pick the actual color for that box based on the the weighted sum */
-
- ColRegs[Index].Red = rsum/Boxes[Index].NumElements;
- ColRegs[Index].Blue = bsum/Boxes[Index].NumElements;
- ColRegs[Index].Green = gsum/Boxes[Index].NumElements;
- }
- /*
- Set up for an index sort of the brightnesses by first calculating the
- weighted brightness of each color (based on the calculation described in the
- VGA manual.
- */
-
- for (Index=0; Index < NumBoxes; Index++)
- {
- indices[Index] = Index;
- weightedcolor[Index] = ColRegs[Index].Red *30 +
- ColRegs[Index].Blue *11 +
- ColRegs[Index].Green*59;
- }
-
- /*
- Do a bubble sort of the weighted colors via indices. Sort is done in
- ascending order.
- */
-
- flag = 1;
- while (flag)
- {
- flag = 0;
- for (Index=0; Index < NumBoxes-1; Index++)
- if (weightedcolor[indices[Index]] > weightedcolor[indices[Index+1]])
- {
- temp = indices[Index];
- indices[Index] = indices[Index+1];
- indices[Index+1] = temp;
- flag = 1;
- }
- }
-
- /*
- Re-map the boxes and the color registers into SBoxes and SColRegs via the
- sorted indices found above.
- */
-
- for (Index=0; Index < NumBoxes; Index++)
- {
- SColRegs[Index].Red = ColRegs[indices[Index]].Red;
- SColRegs[Index].Blue = ColRegs[indices[Index]].Blue;
- SColRegs[Index].Green = ColRegs[indices[Index]].Green;
- SBoxes[Index].NumElements = Boxes[indices[Index]].NumElements;
- for (c=0; c < NUMAXIS; c++)
- {
- SBoxes[Index].Hi[c] = Boxes[indices[Index]].Hi[c];
- SBoxes[Index].Lo[c] = Boxes[indices[Index]].Lo[c];
- }
- }
- }
-
- /* Get the color value of the specified pixel on VGA screen */
- unsigned GetPixelValue(unsigned long PixNum)
- {
- register unsigned Col, Row;
-
- Col = (unsigned)(PixNum / (long) NUMROWS);
- Row = (unsigned)(PixNum % (long) NUMROWS);
- return(GetPixel256(Col,Row));
- }
-
- /* Set the color value of the specified pixel on VGA screen */
- void SetPixelValue(unsigned long PixNum, unsigned Value)
- {
- register unsigned Col, Row;
-
- Col = (unsigned)(PixNum / (long) NUMROWS);
- Row = (unsigned)(PixNum % (long) NUMROWS);
- PutPixel256(Col,Row,Value);
- }
-
- /*
- This function maps the raw image pixel data in the input array into the
- new color map we've come up with in the SColRegs array. In addition, it
- may use Floyd-Steinberg dithering to reduce the error in the image conversion.
- */
-
- void DisplayImageData( void )
- {
- register unsigned c,k,goodindex, PixVal;
- unsigned long minerror,error,LongIndex;
- register int RedDif,GreenDif,BlueDif,r,b,g,i;
-
- /*
- Set the RGBCube array to a value that can't be a color register index
- (MAXNUMCOLREGS*2) so we can detect when we hit on a part of the array that
- is not included in a color box.
- */
-
- for (c=0; c < COLLEVELS; c++)
- for (k=0; k < COLLEVELS*COLLEVELS; k++)
- RGBCube[c][k] = MAXNUMCOLREGS*2;
-
- /*
- Fill the boxes in the RGBCube array with the index number for that box, so
- we can tell what box a particular color index into the RGBCube array is in
- by a single access.
- */
-
- for (i=0; i < NumBoxes; i++)
- for (r=SBoxes[i].Lo[0]; r <= SBoxes[i].Hi[0]; r++)
- for (b=SBoxes[i].Lo[1]; b <= SBoxes[i].Hi[1]; b++)
- for (g=SBoxes[i].Lo[2]; g <= SBoxes[i].Hi[2]; g++)
- RGBCube[r][b*COLLEVELS+g] = i;
-
-
-
- /* for each pixel */
-
- for (LongIndex=0; LongIndex < NUMPIXELS; LongIndex++)
- {
- /*
- If the color levels at that pixel are within proper range, and
- this particular color is inside one of the boxes, and we are not
- in optimum color mode, assign the color index for this pixel to the
- value at that spot in the cube.
- */
-
- if (Red[LongIndex] < COLLEVELS &&
- Blue[LongIndex] < COLLEVELS &&
- Green[LongIndex] < COLLEVELS &&
- (RGBCube[Red[LongIndex]][Blue[LongIndex]*COLLEVELS+Green[LongIndex]]
- !=MAXNUMCOLREGS*2) && !OptimumColors)
- {
- PixVal = RGBCube[Red[LongIndex]][Blue[LongIndex]*COLLEVELS+
- Green[LongIndex]];
- SetPixelValue(LongIndex,PixVal);
- }
- else
- {
- /*
- Otherwise, we need to scan the array of colors to find which is
- the closest to our prospective color.
- */
-
- goodindex = 0;
- minerror = SQUARE(Red[LongIndex]-SColRegs[goodindex].Red)+
- SQUARE(Blue[LongIndex]-SColRegs[goodindex].Blue)+
- SQUARE(Green[LongIndex]-SColRegs[goodindex].Green);
- /*
- Scan all color registers to find which has the smallest error
- when it is used for this pixel.
- */
-
- for (k=1; k < NumBoxes; k++)
- {
- error = SQUARE(Red[LongIndex]-SColRegs[k].Red)+
- SQUARE(Blue[LongIndex]-SColRegs[k].Blue)+
- SQUARE(Green[LongIndex]-SColRegs[k].Green);
- if (error < minerror)
- {
- minerror = error;
- goodindex = k;
- }
- }
- /* Assign that register to this pixel */
- SetPixelValue(LongIndex,goodindex);
- }
-
- /* do dithering if requested */
-
- if (FloydStein)
- {
- /*
- Calculate the difference between the actual color at this pixel
- and the color of the register we are assigning it to.
- */
- RedDif = ((int) SColRegs[GetPixelValue(LongIndex)].Red) -
- ((int) Red[LongIndex]);
- BlueDif = ((int) SColRegs[GetPixelValue(LongIndex)].Blue) -
- ((int) Blue[LongIndex]);
- GreenDif = ((int) SColRegs[GetPixelValue(LongIndex)].Green) -
- ((int) Green[LongIndex]);
-
- /* If we are not at the right hand column of the image */
-
- if ((((LongIndex+NUMROWS) / NUMROWS) < NUMCOLS) &&
- (LongIndex+NUMROWS) < NUMPIXELS)
- {
- /* Diffuse 3/8s of the error to the pixel to our right */
-
- Red[LongIndex+NUMROWS] += (RedDif*3)/8;
- Blue[LongIndex+NUMROWS] += (BlueDif*3)/8;
- Green[LongIndex+NUMROWS] += (GreenDif*3)/8;
-
- /* if that caused the pixel to our right to wrap */
-
- if (Red[LongIndex+NUMROWS] > COLLEVELS-1 ||
- Green[LongIndex+NUMROWS] > COLLEVELS-1 ||
- Blue[LongIndex+NUMROWS] > COLLEVELS-1)
- {
- /* undo the addition on that pixel */
- Red[LongIndex+NUMROWS] -= (RedDif*3)/8;
- Blue[LongIndex+NUMROWS] -= (BlueDif*3)/8;
- Green[LongIndex+NUMROWS] -= (GreenDif*3)/8;
- }
- }
- /* if not at bottom of image */
- if (LongIndex % NUMROWS < NUMROWS-1)
- {
- /* Diffuse 3/8s of the error to the pixel below */
- Red[LongIndex+1] += (RedDif*3)/8;
- Blue[LongIndex+1] += (BlueDif*3)/8;
- Green[LongIndex+1] += (GreenDif*3)/8;
-
- /* If that caused the pixel below us to wrap */
- if (Red[LongIndex+1] > COLLEVELS-1 ||
- Green[LongIndex+1] > COLLEVELS-1 ||
- Blue[LongIndex+1] > COLLEVELS-1)
- {
- /* Undo the addition on that pixel */
- Red[LongIndex+1] -= (RedDif*3)/8;
- Blue[LongIndex+1] -= (BlueDif*3)/8;
- Green[LongIndex+1] -= (GreenDif*3)/8;
- }
- }
- /* if not on last row and not in last column of image */
- if ((((LongIndex+NUMROWS) / NUMROWS) < NUMCOLS) &&
- (LongIndex % NUMROWS < NUMROWS-1))
- {
- /* Diffuse 1/4 of error to the pixel below and to our right */
- Red[LongIndex+1+NUMROWS] += RedDif/4;
- Blue[LongIndex+1+NUMROWS] += BlueDif/4;
- Green[LongIndex+1+NUMROWS] += GreenDif/4;
-
- /* if pixel value wrapped */
- if (Red[LongIndex+1+NUMROWS] > COLLEVELS-1 ||
- Green[LongIndex+1+NUMROWS] > COLLEVELS-1 ||
- Blue[LongIndex+1+NUMROWS] > COLLEVELS-1)
- {
- /* Undo the addition on that pixel */
- Red[LongIndex+1+NUMROWS] -= RedDif/4;
- Blue[LongIndex+1+NUMROWS] -= BlueDif/4;
- Green[LongIndex+1+NUMROWS] -= GreenDif/4;
- }
- }
- }
- }
- }
-
- /*
- The purpose of this function is to partially make up
- for the normalization of pixel values performed previously.
- The values in the color registers are scaled upwards toward
- the maximum value of 63 to increase image briteness.
- */
-
- void ScaleColRegisters(void)
- {
- unsigned Index;
- unsigned MaxVal = 0;
- unsigned Temp;
-
- /* Find the maximum value of any RGB component value */
- for (Index = 0; Index < MAXNUMCOLREGS; Index++)
- {
- if (SColRegs[Index].Red > MaxVal)
- MaxVal = SColRegs[Index].Red;
- if (SColRegs[Index].Green > MaxVal)
- MaxVal = SColRegs[Index].Green;
- if (SColRegs[Index].Blue > MaxVal)
- MaxVal = SColRegs[Index].Blue;
- }
- /* Scale all color register components accordingly */
- for (Index = 0; Index < MAXNUMCOLREGS; Index++)
- {
- /* temp used to prevent overflow of BYTE value */
- Temp = SColRegs[Index].Red * (unsigned) MAXCOLREGVAL;
- Temp /= MaxVal;
- SColRegs[Index].Red = Temp;
-
- Temp = SColRegs[Index].Green * (unsigned) MAXCOLREGVAL;
- Temp /= MaxVal;
- SColRegs[Index].Green = Temp;
-
- Temp = SColRegs[Index].Blue * (unsigned) MAXCOLREGVAL;
- Temp /= MaxVal;
- SColRegs[Index].Blue = Temp;
- }
- }
-
- void InstallPalette256( void )
- {
- register unsigned Index;
-
- /*
- With a graphics mode set, we can proceed to load our colors
- into the DAC. A palette is not used in the 256 color mode of VGA.
- */
-
- for (Index = 0; Index < MAXNUMCOLREGS; Index++)
- {
- /* set a Color Register */
- regs.h.ah = 0x10;
- regs.h.al = 0x10;
- regs.x.bx = Index;
- regs.h.dh = SColRegs[Index].Red;
- regs.h.ch = SColRegs[Index].Green;
- regs.h.cl = SColRegs[Index].Blue;
- int86(VIDEO,®s,®s);
- }
- }
-
- /*
- This function produces an executable .COM file for display of the
- digitized color image. It writes a small code segment followed by
- the 256 color register value followed by the image data to the
- specified file.
- */
-
- void WriteComFile(char *FileName)
- {
- FILE *OutPutFile;
- char String[80];
- unsigned Index, PixelValue, Col, Row;
- BYTE FileCode[] =
- {0xB4,0x0F,0xCD,0x10,0xA2,0x37,0x01,0xB4,0x00,0xB0,0x13,0xCD,0x10,
- 0xB8,0x12,0x10,0xBB,0x00,0x00,0xB9,0x00,0x01,0xBA,0x38,0x01,0xCD,
- 0x10,0xB9,0x00,0xFA,0xBE,0x38,0x04,0xB8,0x00,0xA0,0x8E,0xC0,0xBF,
- 0x00,0x00,0xF3,0xA4,0xB4,0x00,0xCD,0x16,0xB4,0x00,0xA0,0x37,0x01,
- 0xCD,0x10,0xC3,0x00};
-
-
- if (!strchr(FileName,'.')) /* is there an ext ? */
- {
- strcpy(String,FileName); /* copy filename to buffer */
- FileName = String; /* FileName now pts at buffer */
- strcat(FileName,".com"); /* if not add .com ext */
- }
- /* open the output file */
-
- if ((OutPutFile = fopen(FileName,"wb")) == NULL)
- {
- printf("Cannot open Image .COM file\n");
- exit(1);
- }
-
- /* write code segment to the file */
- for (Index=0; Index < sizeof(FileCode); Index++)
- if (fputc(FileCode[Index],OutPutFile) != FileCode[Index])
- {
- restorecrtmode();
- DeAllocMemory();
- printf("Error writing Image .COM code seg\n");
- exit(1);
- }
-
- /* now write the color register rgb values to the file */
- for (Index = 0; Index < MAX256PALETTECOLORS; Index++)
- {
- if (fputc(SColRegs[Index].Red,OutPutFile) != SColRegs[Index].Red)
- {
- restorecrtmode();
- DeAllocMemory();
- printf("Error writing Image .COM red color reg\n");
- exit(1);
- }
- if (fputc(SColRegs[Index].Green,OutPutFile) != SColRegs[Index].Green)
- {
- restorecrtmode();
- DeAllocMemory();
- printf("Error writing Image .COM green color reg\n");
- exit(1);
- }
- if (fputc(SColRegs[Index].Blue,OutPutFile) != SColRegs[Index].Blue)
- {
- restorecrtmode();
- DeAllocMemory();
- printf("Error writing Image .COM blue color reg\n");
- exit(1);
- }
- }
- /* now write the actual image data to the file */
- for (Row=0; Row < NUMROWS; Row++)
- for (Col=0; Col < NUMCOLS; Col++)
- {
- PixelValue = GetPixel256(Col,Row); /* read the value from display */
- fputc(PixelValue,OutPutFile);
- }
-
- fclose(OutPutFile);
- }
-
- /*
- This function corrects the aspect ratio distortion caused by
- the 320x200 VGA display mode. Essentially, a 320x240 pixel image
- is acquired by the digitizer and compressed into a 320x200 buffer.
- See chapter 12 of this book for details.
- */
- void AspectCorrect(unsigned CorrectAspect, char huge *InImage,
- char huge *OutImage)
- {
- register unsigned Col, Row;
- register unsigned LowerBufferRow, UpperBufferRow;
- register unsigned UpperIntensity, LowerIntensity;
- register BYTE Intensity;
- unsigned long InImageBufOffset, OutImageBufOffset;
- double FractionalRowAddr, RowDelta;
-
-
- if (CorrectAspect)
- {
- Message("Correcting Aspect Ratio of Image\n");
-
- /* For each column of the destination buffer (320 total) */
- for (Col=0; Col < LRMAXCOLS; Col++)
- {
- /*
- Calculate the start of the digitized video information
- in the image buffer for this column.
- */
- InImageBufOffset = (long) SOURCEIMAGEROWS * Col;
- OutImageBufOffset = (long) LRMAXROWS * Col;
- /*
- For each rows in the destination buffer ...
- */
- for (Row=0; Row < LRMAXROWS; Row++)
- {
- /*
- Which actual digitized video row out of the
- total of 240 should we accessed ? The calculated
- address will reside between two actual addresses.
- The address will be fractional.
- */
- FractionalRowAddr = ROWASPECTCORRECTION * (double) Row;
- /*
- Get the address of the row bytes just below and just
- above the calculated fractional address. Fetch the
- intensity values of each.
- */
- LowerBufferRow = (unsigned) FractionalRowAddr;
- UpperBufferRow = LowerBufferRow + 1;
- LowerIntensity = InImage[InImageBufOffset + LowerBufferRow];
- UpperIntensity = InImage[InImageBufOffset + UpperBufferRow];
-
- /*
- Calculate the distance the fractional address is off
- from the lower real address. This distance is required
- for the interpolation process.
- */
- RowDelta = FractionalRowAddr - LowerBufferRow;
- /*
- Interpolate for the value of the intensity to assign
- to the pixel at this row.
- */
- Intensity = RowDelta*((double) UpperIntensity - LowerIntensity) +
- LowerIntensity;
-
- /*
- Store the calculated intensity in the destination
- image buffer;
- */
- OutImage[OutImageBufOffset+Row] = Intensity;
- }
- }
- }
- }
-
-
-
- /*
- This function provides help in the advent of operator error. Program
- terminates after help is given
- */
-
- void ShowHelp( void )
- {
- printf("\nThis program digitizes and displays a color image. It\n");
- printf("is envoked as follows:\n\n");
- printf("Usage: cvideo [-a -d -h -o -p -s -v -x] filename <cr>\n");
- printf(" -a correct aspect ratio of image\n");
- printf(" -d use Floyd-Steinberg dithering\n");
- printf(" -h or ? show help\n");
- printf(" -o calculates optimum color palette\n");
- printf(" -p create PCX output file\n");
- printf(" -s scales color register values to max\n");
- printf(" -v displays program progress information\n");
- printf(" -x create executable display program\n");
- printf(" filename is name given to generated display file(s).\n");
- printf(" Do not specify a file extension, it will be provided.\n\n");
- exit(1);
- }
-
- /* main digitizer program */
-
- void main(short argc, char *argv[])
- {
- unsigned FileNameCounter, ArgIndex;
- char *ImageFileName;
-
-
- InitGraphics();
-
- clrscr();
- printf("Digitize, Display and Save a Color Image\n\n");
-
- /* install default options */
- FloydStein = FALSE; /* no dithering */
- OptimumColors = FALSE; /* use best guess color approx */
- Verbose = FALSE; /* don't be wordy */
- GenComFile = FALSE; /* don't generate an .COM file */
- GenPCXFile = FALSE; /* don't generate a. PCX file */
- ScaleColRegs = FALSE; /* don't scale col reg values */
- CorrectAspectRatio = FALSE; /* don't correct aspect ratio */
-
- /* parse all command line arguments */
-
- FileNameCounter = 0; /* count of user specified filenames */
- for (ArgIndex=1; ArgIndex < argc; ArgIndex++)
- {
- if (*argv[ArgIndex] != '-') /* if not a cmd line switch */
- { /* must be a filename */
- if (*argv[ArgIndex] == '?') /* help requested ? */
- ShowHelp();
- if (FileNameCounter > 1) /* only one filename allowed */
- ShowHelp(); /* if more then error exit */
- ImageFileName = argv[ArgIndex]; /* save image filename */
- FileNameCounter++; /* inc count for error check */
- }
- else /* its a cmd line switch */
- {
- switch (*(argv[ArgIndex]+1)) /* parse the cmd line */
- {
- case 'a':
- case 'A':
- CorrectAspectRatio = TRUE;
- break;
- case 'd':
- case 'D':
- FloydStein = TRUE;
- break;
- case 'h':
- case 'H':
- ShowHelp();
- break;
- case 'o':
- case 'O':
- OptimumColors = TRUE;
- break;
- case 'p':
- case 'P':
- GenPCXFile = TRUE;
- break;
- case 's':
- case 'S':
- ScaleColRegs = TRUE;
- break;
- case 'v':
- case 'V':
- Verbose = TRUE;
- break;
- case 'x':
- case 'X':
- GenComFile = TRUE;
- break;
- default:
- printf("Error - invalid cmd line switch encountered\n");
- ShowHelp();
- }
- }
- }
- if ((GenComFile | GenPCXFile) && (FileNameCounter != 1))
- {
- printf("Error - single filename required for output file(s)\n");
- ShowHelp();
- }
-
-
- /* attempt to allocate all required memory */
- if (!AllocMemory())
- {
- printf("memory allocation error - program terminated !\n");
- exit(ENoMemory);
- }
-
- /*
- Build the structure that defines what the digitizer should acquire. This
- will be passed to the digitizer by a call to InitializeDigitizer
- function
- */
-
- Req.ComputerType = PS220;
- Req.PrtBase = LPT1;
- Req.HMode = LowRes;
- Req.VMode = NonInterlace;
- Req.NumberOfPasses = 1;
- Req.Flags = 0L;
- Req.FirstLine = 0;
- Req.FirstPixel = 0;
- Req.LastPixel = 320;
-
- /* acquire a 320x240 image if aspect ratio is to be corrected */
- if (CorrectAspectRatio)
- Req.LastLine = 240;
- else
- Req.LastLine = 200;
-
-
- InitializeDigitizer(&Req); /* tell hardware what to do */
-
- printf("Put Red filter in front of the camera\n");
- printf("Press a key when ready\n");
- getch();
- Message("Acquiring the Red Picture Data from the Digitizer\n");
- if (CorrectAspectRatio)
- Req.PictBuf = ImageBuf;
- else
- Req.PictBuf = Red;
- GetPicture();
- AspectCorrect(CorrectAspectRatio,ImageBuf,Red);
-
- printf("Put Green filter in front of the camera\n");
- printf("Press a key when ready\n");
- getch();
- Message("Acquiring the Green Picture Data from the Digitizer\n");
- if (CorrectAspectRatio)
- Req.PictBuf = ImageBuf;
- else
- Req.PictBuf = Green;
- GetPicture();
- AspectCorrect(CorrectAspectRatio,ImageBuf,Green);
-
- printf("Put Blue filter in front of the camera\n");
- printf("Press a key when ready\n");
- getch();
- Message("Acquiring the Blue Picture Data from the Digitizer\n");
- if (CorrectAspectRatio)
- Req.PictBuf = ImageBuf;
- else
- Req.PictBuf = Blue;
- GetPicture();
- AspectCorrect(CorrectAspectRatio,ImageBuf,Blue);
-
- Message("Picture data acquired. Processing the palette data ...\n");
-
- Message("Normalizing color data\n");
- Normalize(Red);
- Normalize(Blue);
- Normalize(Green);
-
- Message("Building color frequency table\n");
- ScanColorFrequencies();
-
- Message("Selecting optimum color palette\n");
- SelectColorBoxes();
- SortColors();
-
- /* display the resultant color image with the proper palette */
- Set256ColorMode();
- DisplayImageData();
-
- if (ScaleColRegs)
- ScaleColRegisters();
-
- InstallPalette256();
-
- if (GenPCXFile)
- {
- /* create a PCX file for display of color image */
- WritePCXFile(ImageFileName,8,320,200,1,320);
- }
-
- if (GenComFile)
- {
- /* create an executable file for display of color image */
- WriteComFile(ImageFileName);
- }
-
- /* prepare to return to dos */
- getch();
- restorecrtmode();
- DeAllocMemory();
- closegraph();
- }
-