home *** CD-ROM | disk | FTP | other *** search
- _CONVERTING DITHERED IMAGES BACK TO GRAY SCALE_
- by Allen Stenger
-
- [LISTING ONE]
-
- unit User;
- { This is an addition to, and incorporates parts of, the NIH Image program. }
- { NIH Image is written by Wayne Rasband at the National Institutes of Health }
- { and is in the public domain. This addition was written by Allen Stenger, }
- { March 1992. Written in THINK Pascal version 4.0.1. }
- { Replace the User.p supplied with Image with this one. Be sure to uncomment }
- { the call to InitUser in Image.p. If you have a small display you may need }
- { to use ResEdit to shorten the names of the other menu items in Image.rsrc }
- { so the User menu (which comes last) won't be pushed off the end. Use }
- { ResEdit to modify the User Menu in Image.rsrc to make the items Lee Local }
- { Statistics, Ordered Dither, Floyd-Steinberg Dither. }
- { Algorithm references: }
- { Ordered dither: C.N. Judice, J.F. Jarvis, and W.H.Ninke, "Using }
- { Ordered Dither to Display Continuous Tone Pictures on an AC Plasma }
- { Panel." Proceeding of the Society for Information Display v. 15 }
- { no. 4 (Fourth Quarter 1974), not paged. Reprinted in: John C. }
- { Beatty and Kellogg S. Booth (editors), Tutorial: Computer }
- { Graphics, 2nd edition. Silver Spring, MD: IEEE Computer Society }
- { Press, 1982, pp. 220-228.}
- { Lee local statistics: Jong-Sen Lee, "Digital Image Enhancement and }
- { Noise Filtering by Use of Local Statistics." IEEE Transactions on }
- { Pattern Analysis and Machine Intelligence, v. PAMI-2, no. 2 (March }
- { 1980), pp. 165-168. Reprinted in: Rama Chellapa and Alexander A. }
- { Sawchuk (eds.),Digital Image Processing and Analysis v. 1. Silver }
- { Spring, MD: IEEE Computer Society Press, 1985, pp. 440-443. }
-
- interface
- uses
- QuickDraw, Palettes, PrintTraps, globals, Utilities, Graphics, Analysis,
- Camera, Functions;
- procedure InitUser;
- procedure DoUserMenuEvent (MenuItem: integer);
- implementation
- type
- UserFilterType = (LeeLocalStats, OrderedDither, FloydSteinbergDither);
- procedure InitUser;
- begin
- UserMenuH := GetMenu(UserMenu);
- InsertMenu(UserMenuH, 0);
- DrawMenuBar;
- end;
- { Most of UserFilter is copied with minor modifications from Image (procedure }
- { Filter in Functions.p). The new parts are the Lee local statistics and }
- { ordered dither code. Floyd-Steinberg dither is copied from Filter. }
- procedure UserFilter (filterType: UserFilterType);
- const
- PixelsPerUpdate = 5000; { controls screen updating }
- { constants for Lee local statistics method }
- NoiseVariance = 150; { empirical value for Lee method }
- { constants for ordered dither }
- DitherSize = 8; { dimensions of ordered dither matrix }
- DitherSizeMinus1 = 7; { ditto minus 1 }
- type
- DitherPattern = array[0..DitherSizeMinus1, 0..DitherSizeMinus1] of 0..255;
- var
- { general variables for this procedure }
- row, width, r1, r2, r3, c, value, error, sum, tmp, center: integer;
- mark, NewMark, LinesPerUpdate, LineCount: integer;
- MaskRect, frame: rect;
- L1, L2, L3, result: LineType;
- pt: point;
- AutoSelectAll, UseMask: boolean;
- StartTicks: LongInt;
- { variables for Lee local statistics method }
- localVariance: longint;
- localMean: longint;
- gain: real;
- i: integer; { loop control }
- { variables for ordered dither }
- thePattern: DitherPattern;
- procedure PutLineUsingMask (h, v, count: integer;
- var line: LineType);
- var
- aLine, MaskLine: LineType;
- i: integer;
- SaveInfo: InfoPtr;
- begin
- if count > MaxPixelsPerLine then
- count := MaxPixelsPerLine;
- GetLine(h, v, count, aline);
- SaveInfo := Info;
- Info := UndoInfo;
- GetLine(h, v, count, MaskLine);
- for i := 0 to count - 1 do
- if MaskLine[i] = BlackIndex then
- aLine[i] := line[i];
- info := SaveInfo;
- PutLine(h, v, count, aLine);
- end;
- procedure MakeDitherPattern (var p: DitherPattern);
- var
- row: 0..DitherSizeMinus1;
- column: 0..DitherSizeMinus1;
- halfsize: 1..DitherSize;
- scaleFactor: 1..256;
- begin
- { The pattern is defined recursively; we implement the recursion }
- { as an iteration. }
- p[0, 0] := 0;
- halfsize := 1;
- while halfsize < DitherSize do begin
- for row := 0 to halfsize - 1 do
- for column := 0 to halfsize - 1 do begin
- p[row, column] := 4 * p[row, column];
- p[row, column + halfsize] := p[row, column] + 2;
- p[row + halfsize, column] := p[row, column] + 3;
- p[row + halfsize, column + halfsize] := p[row, column] + 1;
- end;
- halfsize := halfsize * 2;
- end;
- { adjust scaling for pixel ranges 0..255 }
- scaleFactor := 256 div SQR(DitherSize);
- for row := 0 to DitherSizeMinus1 do
- for column := 0 to DitherSizeMinus1 do
- p[row, column] := scaleFactor * p[row, column] + scaleFactor div 2;
- end; {MakeDitherPattern}
- begin
- if NotinBounds then
- exit(UserFilter);
- StopDigitizing;
- AutoSelectAll := not Info^.RoiShowing;
- if AutoSelectAll then
- with info^ do begin
- SelectAll(false);
- SetPort(wptr);
- PenNormal;
- PenPat(pat[PatIndex]);
- FrameRect(wrect);
- end;
- if TooWide then
- exit(UserFilter);
- ShowWatch;
- if info^.RoiType <> RectRoi then
- UseMask := SetupMask
- else
- UseMask := false;
- WhatToUndo := UndoFilter;
- SetupUndoFromClip;
- ShowMessage(CmdPeriodToStop);
- frame := info^.RoiRect;
- StartTicks := TickCount;
- {Set up for ordered dither }
- if filterType = OrderedDither then
- MakeDitherPattern(thePattern);
- with frame, Info^ do begin
- changes := true;
- RoiShowing := false;
- if left > 0 then
- left := left - 1;
- if right < PicRect.right then
- right := right + 1;
- width := right - left;
- LinesPerUpdate := PixelsPerUpdate div width;
- GetLine(left, top, width, L2);
- GetLine(left, top + 1, width, L3);
- Mark := RoiRect.top;
- LineCount := 0;
- for row := top + 1 to bottom - 1 do begin
- {Move Convolution Window Down}
- BlockMove(@L2, @L1, width);
- BlockMove(@L3, @L2, width);
- GetLine(left, row + 1, width, L3);
- {Process One Row}
- if CommandPeriod then
- exit(UserFilter);
- case filterType of
- LeeLocalStats:
- for c := 1 to width - 2 do begin
- localMean := (L1[c] + L1[c + 1] + L1[c + 2]
- + L2[c] + L2[c + 1] + L2[c + 2]
- + L3[c] + L3[c + 1] + L3[c + 2]) div 9;
- localVariance := 0;
- for i := 0 to 2 do begin
- localVariance := localVariance + SQR(L1[c + i]
- - localMean);
- localVariance := localVariance + SQR(L2[c + i]
- - localMean);
- localVariance := localVariance + SQR(L3[c + i]
- - localMean);
- end;
- localVariance := localVariance div (3 * 3);
- if OptionKeyWasDown then { do extra smoothing }
- gain := localVariance /
- (localVariance + NoiseVariance * 16.0)
- else
- gain := localVariance / (localVariance + NoiseVariance);
- result[c - 1] :=
- round(localMean + gain * (L2[c + 1] - localMean));
- if result[c - 1] > 255 then
- result[c - 1] := 255;
- if result[c - 1] < 0 then
- result[c - 1] := 0;
- end; {LeeLocalStats}
- OrderedDither:
- for c := 1 to width - 2 do begin
- if L2[c + 1] >=
- thePattern[row mod DitherSize, c mod DitherSize] then
- result[c - 1] := 255 { dither to black pixel }
- else
- result[c - 1] := 0; { dither to white pixel }
- end; {OrderedDither}
- FloydSteinbergDither:
- for c := 1 to width - 2 do begin
- value := L2[c + 1];
- if value < 128 then begin
- result[c - 1] := 0;
- error := -value;
- end
- else begin
- result[c - 1] := 255;
- error := 255 - value
- end;
- tmp := L2[c + 2]; {A}
- tmp := tmp - (7 * error) div 16;
- if tmp < 0 then
- tmp := 0;
- if tmp > 255 then
- tmp := 255;
- L2[c + 2] := tmp;
- tmp := L3[c + 2]; {B}
- tmp := tmp - error div 16;
- if tmp < 0 then
- tmp := 0;
- if tmp > 255 then
- tmp := 255;
- L3[c + 2] := tmp;
- tmp := L3[c + 1]; {C}
- tmp := tmp - (5 * error) div 16;
- if tmp < 0 then
- tmp := 0;
- if tmp > 255 then
- tmp := 255;
- L3[c + 1] := tmp;
- tmp := L3[c]; {D}
- tmp := tmp - (3 * error) div 16;
- if tmp < 0 then
- tmp := 0;
- if tmp > 255 then
- tmp := 255;
- L3[c] := tmp;
- end; {FloydSteinbergDither}
- end; {case filterType}
- if UseMask then
- PutLineUsingMask(left + 2, row, width - 3, result)
- else
- PutLine(left + 2, row, width - 3, result);
- LineCount := LineCount + 1;
- if LineCount = LinesPerUpdate then begin
- pt.h := RoiRect.left;
- pt.v := row + 1;
- NewMark := pt.v;
- with RoiRect do
- SetRect(MaskRect, left, mark, right, NewMark);
- UpdateScreen(MaskRect);
- LineCount := 0;
- Mark := NewMark;
- if magnification > 1.0 then
- Mark := Mark - 1;
- if CommandPeriod then begin
- UpdatePicWindow;
- beep;
- if AutoSelectAll then
- KillRoi;
- exit(UserFilter)
- end;
- end;
- end; {for row:=...}
- trect := frame;
- InsetRect(trect, 1, 1);
- ShowTime(StartTicks, trect, '');
- end; {with}
- if LineCount > 0 then begin
- with frame do
- SetRect(MaskRect, left, mark, right, bottom);
- UpdateScreen(MaskRect)
- end;
- SetupRoiRect;
- if AutoSelectAll then
- KillRoi;
- end;
- procedure DoUserMenuEvent (MenuItem: integer);
- begin
- case MenuItem of { User menu must be set up in this order }
- 1:
- UserFilter(LeeLocalStats);
- 2:
- UserFilter(OrderedDither);
- 3:
- UserFilter(FloydSteinbergDither);
- end;
- end;
- end.
-
-