unit Camera; {Routines used by the NIH Image to support the Data Translation} {QuickCapture, the Scion LG-3, Scion AG-5 and Scion VG-5 frame grabber cards.} interface uses Memory, QuickDraw, Packages, Menus, Events, Fonts, Scrap, ToolUtils, OSUtils, Resources, Errors, Palettes, globals, Utilities, Graphics, File1, Analysis; function DoAveragingOptions: boolean; procedure AverageFrames; procedure GetFrame; procedure CaptureAndDisplayFrame; procedure HighlightPixels; procedure ShowTriggerMessage; procedure StartDigitizing; procedure StopDigitizing; procedure SetVideoChannel; function GetFGPixel (h, v: integer): integer; procedure CopyOffscreen (src, dst: PixMapHandle; sRect, dRect: rect); procedure WaitForTrigger; procedure ShowChannel; procedure ShowVideoControl; procedure UpdateVideoControl; procedure DoVideoControl (item: integer); procedure SelectCameraWindow; procedure SetOffset (var offset, gain: integer); procedure SetGain (var offset, gain: integer); procedure ShowOffsetAndGain (offset, gain: integer); implementation type IntPtr = ^integer; var SavePicBaseAddr: ptr; StopFlagLoc: IntPtr; procedure CorrectShadingOfLine (PicPtr, BFPtr: ptr; width, BFMean: integer); {$IFC PowerPC} VAR PicLine,BFLine:LinePtr; i,value:LongInt; BEGIN PicLine:=LinePtr(PicPtr); BFLine:=LinePtr(BFPtr); FOR i:=0 TO width-1 DO BEGIN value:=PicLine^[i]; value:=255-value; value:=(value * BFMean + (BFLine^[i] div 2)) DIV BFLine^[i]; IF value>254 THEN value:=254; IF value<1 THEN value:=1; PicLine^[i]:=255-value; END; END; {$ELSEC} {a0=data pointer} {a1=blank field data pointer} {d0=count} {d1=pixel value} {d2=blank field pixel value} {d3=blank field mean} {d4=temp} {d5=max pixel value(245)} {d6=min pixel value(1)} inline $4E56, $0000, { link a6,#0} $48E7, $FEC0, { movem.l a0-a1/d0-d6,-(sp)} $206E, $000C, { move.l 12(a6),a0} $226E, $0008, { move.l 8(a6),a1} $4280, { clr.l d0} $302E, $0006, { move.w 6(a6),d0} $362E, $0004, { move.w 4(a6),d3} $2A3C, $0000, $00FE, { move.l #254,d5} $2C3C, $0000, $0001, { move.l #1,d6} $5380, { subq.l #1,d0} $4281, { clr.l d1} $4282, { clr.l d2} $1210, {L1 move.b (a0),d1} $1419, { move.b (a1)+,d2} $4601, { not.b d1} $C2C3, { mulu.w d3,d1} $2802, { move.l d2,d4} $E244, { asr.w #1,d4} $D284, { add.l d4,d1} $82C2, { divu.w d2,d1} $B245, { cmp.w d5,d1} $6F02, { ble.s L2} $3205, { move.w d5,d1} $B246, {L2 cmp.w d6,d1} $6C02, { bge.s L3} $3206, { move.w d6,d1} $4601, {L3 not.b d1} $10C1, { move.b d1,(a0)+} $51C8, $FFDE, { dbra d0,L1} $4CDF, $037F, { movem.l (sp)+,a0-a1/d0-d6} $4E5E, { unlk a6} $DEFC, $000C; { add.w #12,sp} {$ENDC} procedure CorrectShading; var i, tag, width: integer; offset, NextUpdate: LongInt; p1, p2: ptr; str: str255; MaskRect:rect; begin with info^ do begin if ImageSize <> BlankFieldInfo^.ImageSize then begin beep; exit(CorrectShading); end; ShowWatch; tag:=0; NextUpdate:=TickCount+6; width:=PicRect.right; p1 := PicBaseAddr; p2 := BlankFieldInfo^.PicBaseAddr; for i := 1 to nLines do begin CorrectShadingOfLine(p1, p2, PixelsPerLine, BlankFieldMean); p1 := ptr(ord4(p1) + info^.BytesPerRow); p2 := ptr(ord4(p2) + BlankFieldInfo^.BytesPerRow); if TickCount>=NextUpdate then begin SetRect(MaskRect, 0, tag, width, i); UpdateScreen(MaskRect); tag:=i; NextUpdate:=TickCount+6; end; end; SetRect(MaskRect, 0, tag, width, nLines); UpdateScreen(MaskRect); str := title; if SpatiallyCalibrated then str := concat(str, chr($13)); {Black Diamond} if fit <> uncalibrated then str := concat(str, ''); if wptr <> nil then SetWTitle(wptr, concat(str, ' (Corrected)')); end; end; { procedure CopyOffscreen (src, dst: PixMapHandle; sRect, dRect: rect); var srcPtr,dstPtr: ptr; i, width, height, srcRowBytes, dstRowBytes: LongInt; begin with sRect,src^^ do begin width := right - left; height := bottom - top; srcRowBytes:=band(RowBytes, $3FFF); srcPtr := ptr(ord4(baseAddr) + top*srcRowBytes + left); end; with dRect,dst^^ do begin dstRowBytes:=band(RowBytes, $3FFF); dstPtr := ptr(ord4(baseAddr) + top*dstRowBytes + left); end; for i := 0 to height - 1 do begin BlockMove(srcPtr, dstPtr, width); srcPtr := ptr(ord4(srcPtr) + srcRowBytes); dstPtr := ptr(ord4(dstPtr) + dstRowBytes); end; end; } procedure CopyOffscreen (src, dst: PixMapHandle; sRect, dRect: rect); var SaveGDevice: GDHandle; begin SaveGDevice := GetGDevice; SetGDevice(osGDevice); pmForeColor(BlackIndex); pmBackColor(WhiteIndex); CopyBits(BitMapHandle(src)^^, BitMapHandle(dst)^^, sRect, dRect, SrcCopy, nil); pmForeColor(ForegroundIndex); pmBackColor(BackgroundIndex); SetGDevice(SaveGDevice); end; procedure StopDigitizing; begin if digitizing then with info^ do begin ShowFrameRate('', fgStartTicks, fgFrameCount); CopyOffscreen(fgPort^.portPixMap, osPort^.portPixMap, PicRect, PicRect); SetMenuItemText(SpecialMenuH, StartItem, 'Start Capturing'); Digitizing := false; ContinuousHistogram := false; with info^ do if PictureType = FrameGrabberType then begin title := 'Camera'; UpdateTitleBar; if HighlightSaturatedPixels then LoadLUT(ctable); end; if (ScreenDepth<>8) and HighlightSaturatedPixels then UpdatePicWindow; if (BlankFieldInfo <> nil) and not OptionKeyDown then CorrectShading; end; end; procedure GetFrame; var ticks, timeout: LongInt; temp:integer; begin if (FrameGrabber = ScionLG3) or (FrameGrabber = ScionVG5f) then begin if ExternalTrigger then begin {Wait for trigger} ControlReg^ := $90; repeat if button then ExternalTrigger := false; until (BitAnd(ControlReg^, $80) = $80) or not ExternalTrigger; ControlReg^ := 0; if Digitizing then StopDigitizing; UpdateVideoControl; end {if External Trigger} else begin TimeOut := TickCount + 30; {1/2sec. timeout} ControlReg^ := $80; {Start frame capture} while BitAnd(ControlReg^, $80) = 0 do begin {Wait for it to complete} if TickCount > TimeOut then begin ControlReg^ := 0; leave end; end; ControlReg^ := 0; end; end else if FrameGrabber = ScionAG5 then begin if ExternalTrigger then begin {Wait for trigger} ControlReg^ := bor(bor(ord(AG5GrabMode), $90), bsl(ord(AG5LutMode and not ControlKeyDown), 2)); repeat if button then ExternalTrigger := false; until (BitAnd(ControlReg^, $80) = $80) or not ExternalTrigger; ControlReg^ := 0; if Digitizing then StopDigitizing; UpdateVideoControl; end {if External Trigger} else begin TimeOut := TickCount + 30; {1/2sec. timeout} ControlReg^ := bor(bor(ord(AG5GrabMode), $80), bsl(ord(AG5LutMode and not ControlKeyDown), 2)); {Start frame capture} repeat if TickCount > TimeOut then leave; temp:=ControlReg^; {ppc-bug} until BitAnd(temp, $80) <> 0; {Wait for it to complete} ControlReg^ := 0; end; end else begin {QuickCapture} if ExternalTrigger then begin {Wait for trigger} ControlReg^ := BitOr(ControlReg^,$82); {Set Busy and External Trigger Enable bits} repeat if button then ExternalTrigger := false; temp:=ControlReg^; {ppc-bug} until (BitAnd(temp, $80) = 0) or not ExternalTrigger; if Digitizing then StopDigitizing; UpdateVideoControl; end {if External Trigger} else begin TimeOut := TickCount + 30; {1/2sec. timeout} ControlReg^ := BitOr(ControlReg^,$80); {Start frame capture by setting busy bit} repeat if TickCount > TimeOut then leave; temp:=ControlReg^; {ppc-bug} until BitAnd(temp, $80) = 0; {Wait for frame capture to complete} end; end; {QuickCapture} fgFrameCount := fgFrameCount + 1; end; procedure CaptureAndDisplayFrame; var tPort: GrafPtr; SaveGDevice: GDHandle; begin with info^ do begin if (PictureType <> FrameGrabberType) or (PixelsPerLine <> fgWidth) or (nlines <> fgHeight) then begin Digitizing := false; exit(CaptureAndDisplayFrame); end; GetFrame; SaveGDevice := GetGDevice; SetGDevice(GetMainDevice); getPort(tPort); SetPort(wptr); SetFColor(BlackIndex); SetBColor(WhiteIndex); CopyBits(BitMapHandle(fgPort^.portPixMap)^^, BitMapHandle(CGrafPtr(wptr)^.PortPixMap)^^, SrcRect, wrect, gCopyMode, nil); SetPort(tPort); SetGDevice(SaveGDevice); end; end; procedure SetReg (index, value: integer); const RegOffset = $f5fe0; var reg: ptr; begin reg := ptr(fgSlotBase + RegOffset + index * 4); reg^ := value; end; {$ifc PowerPC} {ppc-bug} procedure SwapMMUMode(var mode:SignedByte); begin end; {$endc} procedure SelectCameraWindow; {If there is a Camera window, activate it, otherwise, do nothing.} var i: integer; TempInfo: InfoPtr; begin for i := 1 to nPics do begin TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon); if TempInfo^.PictureType = FrameGrabberType then begin if PicWindow[i] <> nil then begin if OpPending then KillRoi; SelectWindow(PicWindow[i]); Info := TempInfo; ActivateWindow; end; {if} leave; end; {if} end; {for} end; procedure HighlightPixels; var lut: MyCSpecArray; begin with info^ do begin lut := ctable; lut[1].rgb := Highlight1; lut[254].rgb := Highlight254; LoadLUT(lut); end; end; procedure ShowTriggerMessage; begin if ExternalTrigger and (frameGrabber <> noFrameGrabber) then ShowMessage(concat('EXTERNAL TRIGGER MODE', crStr, '(Press mouse button to exit)')); end; procedure StartDigitizing; var i, width, height: integer; trect: rect; NewWindow: boolean; begin if Digitizing then begin StopDigitizing; if BlankFieldInfo <> nil then wait(15); FlushEvents(EveryEvent, 0); {In case user holds key down too long} exit(StartDigitizing) end; if FrameGrabber = NoFrameGrabber then begin PutError('Capturing requires a Data Translation or Scion frame grabber card.'); exit(StartDigitizing) end; if info^.PictureType <> FrameGrabberType then SelectCameraWindow; NewWindow := false; with info^ do if (PictureType <> FrameGrabberType) or (PixelsPerLine <> fgWidth) or (nlines <> fgHeight) then begin if not NewPicWindow('Camera', fgWidth, fgHeight) then exit(StartDigitizing); NewWindow := true; end; with info^ do begin PictureType := FrameGrabberType; if NewWindow and (not EqualRect(SrcRect, PicRect)) then {Center Frame} with SrcRect do begin width := right - left; height := bottom - top; left := (PicRect.right - width) div 2; right := left + width; top := (PicRect.bottom - height) div 2; bottom := top + height; end; KillRoi; if ScaleToFitWindow then ScaleToFit; with SrcRect do begin width := right - left; left := band(left, $fffc); right := left + width; end; GetWindowRect(wptr, trect); with trect do if band(left, 3) <> 0 then MoveWindow(wptr, band(left, $fffc), top, true); {Forces window to be word aligned} with SrcRect do {Prevents bus errors when Camera window moved.} if (top = 0) and (bottom < PicRect.bottom) then begin top := top + 1; bottom := bottom + 1; end; ResetFrameGrabber; Digitizing := true; SetMenuItemText(SpecialMenuH, StartItem, 'Stop Capturing'); changes := true; BinaryPic := false; UpdateTitleBar; if HighlightSaturatedPixels then HighlightPixels; end; {with info} fgFrameCount := 0; fgStartTicks := TickCount; ContinuousHistogram := false; ShowTriggerMessage; end; procedure AddLineToSum (src, dst: ptr; width: LongInt); {$IFC PowerPC} type SumLineType = array[0..2047] of integer; fptr = ^SumLineType; var FrameLine: LinePtr; SumLine: fptr; i: integer; begin FrameLine := LinePtr(src); SumLine := fptr(dst); for i := 0 to width - 1 do SumLine^[i] := SumLine^[i] + FrameLine^[i]; end; {$ELSEC} inline {a0=data pointer} {a1=sum buffer pointer} {d0=count} {d1=pixel value} {d2=temp} $4E56, $0000, {link a6,#0} $48E7, $E0C0, {movem.l a0-a1/d0-d2,-(sp)} $206E, $000C, {move.l 12(a6),a0} $226E, $0008, {move.l 8(a6),a1} $202E, $0004, {move.l 4(a6),d0} $5380, {subq.l #1,d0} $4281, {clr.l d1} $4282, {clr.l d2} $1218, {L1 move.b (a0)+,d1} $3411, {move.w (a1),d2} $D441, {add.w d1,d2} $32C2, {move.w d2,(a1)+} $51C8, $FFF6, {dbra d0,L1} $4CDF, $0307, {movem.l (sp)+,a0-a1/d0-d2} $4E5E, {unlk a6} $DEFC, $000C; {add.w #12,sp} {$ENDC} function DoAveragingOptions: boolean; const FramesID = 8; VideoRateID = 9; SumID = 10; ShowID = 11; FixID = 12; MinID = 13; MaxID = 14; OnChipID = 15; var mylog: DialogPtr; item, i: integer; begin InitCursor; mylog := GetNewDialog(140, nil, pointer(-1)); if not SumFrames then begin ShowIntegratedValues := false; FixIntegrationScale := false; end; SetDNum(MyLog, FramesID, FramesToAverage); SetDlogItem(mylog, SumID, ord(SumFrames)); SetDlogItem(mylog, VideoRateID, ord(VideoRateAveraging)); SetDlogItem(mylog, ShowID, ord(ShowIntegratedValues)); SetDlogItem(mylog, FixID, ord(FixIntegrationScale)); SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip)); SetDNum(MyLog, MinID, IntegrationMin); SetDNum(MyLog, MaxID, IntegrationMax); SelectDialogItemText(MyLog, FramesID, 0, 32767); repeat ModalDialog(nil, item); if item = FramesID then FramesToAverage := GetDNum(MyLog, FramesID); if item = SumID then begin SumFrames := not SumFrames; if SumFrames then IntegrateOnChip := false else begin FixIntegrationScale := false; ShowIntegratedValues := false; end; SetDlogItem(mylog, SumID, ord(SumFrames)); SetDlogItem(mylog, FixID, ord(FixIntegrationScale)); SetDlogItem(mylog, ShowID, ord(ShowIntegratedValues)); SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip)); end; if item = VideoRateID then begin VideoRateAveraging := not VideoRateAveraging; if VideoRateAveraging then IntegrateOnChip := false; SetDlogItem(mylog, VideoRateID, ord(VideoRateAveraging)); SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip)); end; if item = ShowID then begin ShowIntegratedValues := not ShowIntegratedValues; if ShowIntegratedValues then begin SumFrames := true; IntegrateOnChip := false; end; SetDlogItem(mylog, ShowID, ord(ShowIntegratedValues)); SetDlogItem(mylog, SumID, ord(SumFrames)); SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip)); end; if item = FixID then begin FixIntegrationScale := not FixIntegrationScale; if FixIntegrationScale then begin SumFrames := true; IntegrateOnChip := false; end; SetDlogItem(mylog, FixID, ord(FixIntegrationScale)); SetDlogItem(mylog, SumID, ord(SumFrames)); SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip)); end; if (item = MinID) or (item = MaxID) then begin if item = MinID then IntegrationMin := GetDNum(MyLog, MinID) else IntegrationMax := GetDNum(MyLog, MaxID); SumFrames := true; FixIntegrationScale := true; IntegrateOnChip := false; SetDlogItem(mylog, SumID, ord(SumFrames)); SetDlogItem(mylog, FixID, ord(FixIntegrationScale)); SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip)); end; if item = OnChipID then begin IntegrateOnChip := not IntegrateOnChip; if IntegrateOnChip then begin SumFrames := false; VideoRateAveraging := false; FixIntegrationScale := false; ShowIntegratedValues := false; end; SetDlogItem(mylog, OnChipID, ord(IntegrateOnChip)); SetDlogItem(mylog, SumID, ord(SumFrames)); SetDlogItem(mylog, VideoRateID, ord(VideoRateAveraging)); SetDlogItem(mylog, FixID, ord(FixIntegrationScale)); SetDlogItem(mylog, ShowID, ord(ShowIntegratedValues)); end; until (item = ok) or (item = cancel); DisposeDialog(mylog); if FramesToAverage < 2 then FramesToAverage := 2; if IntegrationMin < 0 then IntegrationMin := 0; if IntegrationMax > 32767 then IntegrationMax := 32767; if VideoRateAveraging and (item <> cancel) then begin if (FrameGrabber <> ScionLG3) and (FrameGrabber <> ScionAG5) then begin VideoRateAveraging := false; PutError('Video rate averaging or summation requires a Scion LG-3 or a Scion AG-5.'); DoAveragingOptions := false; exit(DoAveragingOptions); end; if (FrameGrabber = ScionLG3) and (FramesToAverage > MaxLG3Frames) then begin FramesToAverage := MaxLG3Frames; DoAveragingOptions := false; PutError(concat('This ', long2str(MaxLG3Frames div 2), 'MB LG-3 can capture a maximum of ', long2str(MaxLG3Frames), ' frames at video rates.')); exit(DoAveragingOptions); end; if (FrameGrabber = ScionAG5) and (FramesToAverage > 127) then begin FramesToAverage := 127; DoAveragingOptions := false; PutError(concat('The AG-5 can average or sum a maximum of 127 frames at video rates.')); exit(DoAveragingOptions); end; end; if IntegrateOnChip and (item <> cancel) then if (FrameGrabber <> ScionLG3) and (FrameGrabber <> ScionAG5) and (FrameGrabber <> ScionVG5f) then begin IntegrateOnChip := false; PutError('On-chip integration requires a Scion frame grabber.'); DoAveragingOptions := false; exit(DoAveragingOptions); end; DoAveragingOptions := item <> cancel; end; function OddEven: boolean; {Looks at the the Field Status bit of the Status Register, which has the same address as Control Register 1. This bit is high during the odd field and low during the even field.} begin if band(ControlReg^, $10) = $10 then OddEven := true else OddEven := false; end; procedure WaitForOdd; var timeout: LongInt; begin TimeOut := TickCount + 30; {1/2sec. timeout} while OddEven do if TickCount > TimeOut then Exit(WaitForOdd); TimeOut := TickCount + 30; {1/2sec. timeout} while not OddEven do if TickCount > TimeOut then Exit(WaitForOdd); end; procedure IntegrateOn; {Sets bit 3 (Open Drain Output) of Control Register 1 high which pulls pin 11 of the 15 pin connector low, causing the Cohu camera to start integrating.} begin ControlReg^ := $08; end; procedure IntegrateOff; {Sets bit 3 of Control Register 1 low which open circuits pin 11, causing the Cohu camera to stop integrating.} begin ControlReg^ := $00; end; procedure DoOnChipIntegration; {Requires a Scion LG-3, a Cohu 4910 series camera, and a cable available from Scion.} var i,StartTicks:LongInt; str:str255; begin WaitForOdd; IntegrateOn; StartTicks := TickCount; for i := 1 to FramesToAverage - 1 do begin WaitForOdd; if (i mod 30) = 0 then ShowAnimatedWatch; if CommandPeriod then leave; end; IntegrateOff; GetFrame; RealToString((TickCount - StartTicks) / 60.0, 1, 2, str); ShowFrameRate(concat(Long2str(FramesToAverage), ' frames', cr, str, ' seconds', cr), StartTicks, FramesToAverage); with info^ do CopyOffscreen(fgPort^.portPixMap, osPort^.portPixMap, RoiRect, RoiRect); UpdatePicWindow; KillRoi; if BlankFieldInfo <> nil then CorrectShading; if info^.fit<>uncalibrated then RemoveDensityCalibration; end; procedure DoHardwareAveraging; {Do averaging or integration at video rates using the Scion Ag-5.} var StartTicks,ActualMin,ActualMax:LongInt; str1,str2:str255; frame,i:integer; roi:rect; begin roi:=info^.RoiRect; KillRoi; if FramesToAverage > 127 then FramesToAverage := 127; ExternalTrigger := false; AG5GrabMode := GrabNormal; GetFrame; StartTicks := TickCount; AG5GrabMode := GrabSum; for frame := 1 to FramesToAverage - 1 do begin GetFrame; end; RealToString((TickCount - StartTicks) / 60.0, 1, 2, str2); if not SumFrames then begin ConstantReg^ := FramesToAverage; AG5GrabMode := GrabDivide; GetFrame; AG5GrabMode := GrabNormal; str1 := ''; end else begin ActualMin := Ord4(ScaleLowReg^); ActualMax := Ord4(ScaleHighReg^); if FixIntegrationScale then begin ScaleLowReg^ := integer(IntegrationMin); ScaleHighReg^ := integer(IntegrationMax); end; AG5GrabMode := GrabScale; GetFrame; AG5GrabMode := GrabNormal; if FixIntegrationScale then str1 := concat('min=', long2str(IntegrationMin), ' (', long2str(ActualMin), ')', cr, 'max=', long2str(IntegrationMax), ' (', long2str(ActualMax), ')', cr) else str1 := concat('min=', long2str(ActualMin), cr, 'max=', long2str(ActualMax), cr) end; ShowFrameRate(concat(Long2str(FramesToAverage), ' frames', cr, str1, str2, ' seconds', cr), StartTicks, FramesToAverage); with info^ do CopyOffscreen(fgPort^.portPixMap, osPort^.portPixMap, roi, roi); UpdatePicWindow; if not EqualRect(roi, info^.PicRect) then RestoreRoi; if BlankFieldInfo <> nil then CorrectShading; if ShowIntegratedValues then with info^ do begin fit := StraightLine; nCoefficients := 2; coefficient[2] := (ActualMax - ActualMin) / 253.0; coefficient[1] := ActualMin - coefficient[2]; ZeroClip := false; UpdateTitleBar; if macro then GenerateValues; end else if SumFrames and (info^.fit<>uncalibrated) then RemoveDensityCalibration; end; {DoAG5HardwareAveraging} procedure AverageFrames; type IntPtr = ^integer; SumLineType = array[0..2047] of integer; sptr = ^SumLineType; var AutoSelectAll: boolean; SelectionSize, FrameBufferSize, offset, StartTicks: LongInt; SumBase, src, srcbase, dst, OffscreenBase: ptr; str1, str2: str255; xLines, xPixelsPerLine, BytesPerLine, frame, line, pixel: integer; aline, BlankLine: LineType; GrabRect: rect; hstart, vstart, wwidth, wheight: integer; j, FramesAveraged: integer; SrcRowBytes, DstRowBytes, i, value, MinV, MaxV, range, ActualMin, ActualMax: LongInt; iptr: IntPtr; FrameLine: LinePtr; SumLine: sptr; SaveBlankFieldInfo: InfoPtr; myMMUMode: signedbyte; begin with info^ do if PictureType <> FrameGrabberType then begin PutError('You must have an active Camera window (created using Start Capturing) in order to average frames.'); AbortMacro; exit(AverageFrames) end; if NotRectangular or NotinBounds then begin AbortMacro; exit(AverageFrames); end; if (not OptionKeyWasDown) and (not macro) then begin if not DoAveragingOptions then exit(AverageFrames); end; SaveBlankFieldInfo := BlankFieldInfo; BlankFieldInfo := nil; {We don't want to do shading correction now} StopDigitizing; BlankFieldInfo := SaveBlankFieldInfo; OptionKeyWasDown := false; if (FrameGrabber <> ScionLG3) and (FrameGrabber <> ScionAG5) then VideoRateAveraging := false; if (FrameGrabber <> ScionLG3) and (FrameGrabber <> ScionAG5) and (FrameGrabber <> ScionVG5f) then IntegrateOnChip := false; ShowWatch; ShowTriggerMessage; AutoSelectAll := not Info^.RoiShowing; if AutoSelectAll then SelectAll(false); WhatToUndo := NothingToUndo; ContinuousHistogram := false; ResetFrameGrabber; if IntegrateOnChip then begin DoOnChipIntegration; exit(AverageFrames); end; if VideoRateAveraging and (FrameGrabber=ScionAg5) then begin DoHardwareAveraging; exit(AverageFrames); end; DrawLabels('Frame:', 'Total:', ''); with info^.RoiRect do SelectionSize := (ord4(right) - left) * (bottom - top); FrameBufferSize := SelectionSize * 2; if FrameBufferSize > BigBufSize then begin NumToString(FrameBufferSize div 1024, str1); NumToString(BigBufSize div 1024, str2); str2 := concat(str1, 'K bytes are required, but only ', str2, 'K bytes are available.'); PutError(concat('There is not enough memory to do the requested frame averaging. ', str2)); if AutoSelectAll or (BlankFieldInfo <> nil) then KillRoi else ShowRoi; exit(AverageFrames) end; WhatsOnClip := NothingOnClip; SumBase := BigBuf; with info^, info^.RoiRect do begin offset := left + ord4(top) * BytesPerRow; OffscreenBase := ptr(ord4(PicBaseAddr) + offset); offset := left + ord4(top) * fgRowBytes; srcbase := ptr(ord4(ptr(fgSlotBase)) + offset); SrcRowBytes := fgRowBytes; xLines := bottom - top; xPixelsPerLine := right - left; BytesPerLine := xPixelsPerLine * 2; end; {with} for i := 0 to BytesPerLine - 1 do BlankLine[i] := WhiteIndex; dst := SumBase; for line := 1 to xLines do begin {zero buffer} BlockMove(@BlankLine, dst, BytesPerLine); dst := ptr(ord4(dst) + BytesPerLine); end; info^.title := 'Camera'; UpdateTitleBar; StartTicks := TickCount; if VideoRateAveraging then begin if FramesToAverage > MaxLG3Frames then FramesToAverage := MaxLG3Frames; ExternalTrigger := false; BufferReg^ := 0; GetFrame; StartTicks := TickCount - 2; for frame := 1 to FramesToAverage - 1 do begin BufferReg^ := Frame; GetFrame; end; BufferReg^ := 0; RealToString((TickCount - StartTicks) / 60.0, 1, 2, str1); ShowFrameRate(concat(Long2str(FramesToAverage), ' frames', crStr, str1, ' seconds', crStr), StartTicks, FramesToAverage); end; {if VideoRateAveraging} for frame := 0 to FramesToAverage - 1 do begin Show2Values(frame + 1, FramesToAverage); if VideoRateAveraging then BufferReg^ := Frame else GetFrame; src := srcbase; dst := SumBase; myMMUMode := 1; SwapMMUMode(myMMUMode); for line := 1 to xLines do begin AddLineToSum(src, dst, xPixelsPerLine); src := ptr(ord4(src) + SrcRowBytes); dst := ptr(ord4(dst) + BytesPerLine); end; SwapMMUMode(myMMUMode); if CommandPeriod then begin beep; if AutoSelectAll then KillRoi else ShowRoi; exit(AverageFrames); end; end; {for} src := SumBase; dst := OffscreenBase; DstRowBytes := info^.BytesPerRow; if SumFrames then begin MinV := 2000000000; MaxV := 0; iptr := IntPtr(src); for i := 1 to SelectionSize do begin value := iptr^; if value > MaxV then MaxV := value; if value < MinV then MinV := value; iptr := IntPtr(ord4(iptr) + 2); end; ActualMin := MinV; ActualMax := MaxV; if FixIntegrationScale then begin MinV := IntegrationMin; MaxV := IntegrationMax; end; range := MaxV - MinV; if range <> 0 then for line := 1 to xLines do begin SumLine := sptr(src); FrameLine := LinePtr(dst); for j := 0 to xPixelsPerLine - 1 do begin value := ord4(SumLine^[j] - MinV) * 253 div range + 1; if value < 0 then value := 0; if value > 255 then value := 255; FrameLine^[j] := value; end; src := ptr(ord4(src) + BytesPerLine); dst := ptr(ord4(dst) + DstRowBytes); end else beep; end else for line := 1 to xLines do begin SumLine := sptr(src); FrameLine := LinePtr(dst); for j := 0 to xPixelsPerLine - 1 do FrameLine^[j] := SumLine^[j] div FramesToAverage; src := ptr(ord4(src) + BytesPerLine); dst := ptr(ord4(dst) + DstRowBytes); end; if not VideoRateAveraging then begin if SumFrames then begin if FixIntegrationScale then str1 := concat('min=', long2str(MinV), ' (', long2str(ActualMin), ')', crStr, 'max=', long2str(MaxV), ' (', long2str(ActualMax), ')', crStr) else str1 := concat('min=', long2str(MinV), crStr, 'max=', long2str(MaxV), crStr) end else str1 := ''; RealToString((TickCount - StartTicks) / 60.0, 1, 2, str2); ShowFrameRate(concat(Long2str(FramesToAverage), ' frames', crStr, str1, str2, ' seconds', crStr), StartTicks, FramesToAverage); end; UpdatePicWindow; if AutoSelectAll then KillRoi else ShowRoi; if BlankFieldInfo <> nil then CorrectShading; if ShowIntegratedValues then with info^ do begin fit := StraightLine; nCoefficients := 2; coefficient[2] := (MaxV - MinV) / 253.0; coefficient[1] := MinV - coefficient[2]; nKnownValues := 0; ZeroClip := false; UpdateTitleBar; if macro then GenerateValues; end else if SumFrames and (info^.fit<>uncalibrated) then RemoveDensityCalibration; end; function GetFGPixel (h, v: integer): integer; var offset: LongInt; p: ptr; begin with Info^ do begin if (h < 0) or (v < 0) or (h >= fgWidth) or (v >= fgHeight) then begin GetFGPixel := WhiteIndex; exit(GetFGPixel); end; offset := ord4(v) * fgRowBytes + h; if offset >= ord4(fgHeight) * fgRowBytes then begin GetFGPixel := WhiteIndex; exit(GetFGPixel); end; p := ptr(ord4(ptr(fgSlotBase)) + offset); GetFGPixel := BAND(p^, 255); end; end; procedure WaitForTrigger; begin StopDigitizing; ShowWatch; case FrameGrabber of QuickCapture: begin ControlReg^ := BitAnd($82, 255); {Wait for external trigger and capture one frame} repeat until (BitAnd(ControlReg^, $80) = $00) or Button; {Wait for it to complete} end; ScionLG3, ScionAg5, ScionVG5f: begin ControlReg^ := $90; {Wait for external trigger and capture one frame} repeat until (BitAnd(ControlReg^, $80) = $80) or Button; {Wait for it to complete} end; otherwise repeat until Button; end; end; procedure SetOffset (var offset, gain: integer); begin if offset < 0 then offset := 0; if offset > 255 then offset := 255; if offset > gain then offset := gain; DacLow := offset; DacHigh := DacLow + (255 - gain); end; procedure SetGain (var offset, gain: integer); begin if gain < 0 then gain := 0; if gain > 255 then gain := 255; if gain < DacLow then gain := DacLow; DacHigh := DacLow + (255 - gain); end; procedure ShowChannel; begin SetDlogItem(VideoControl, FirstChannelID, ord(VideoChannel = 0)); SetDlogItem(VideoControl, FirstChannelID + 1, ord(VideoChannel = 1)); SetDlogItem(VideoControl, FirstChannelID + 2, ord(VideoChannel = 2)); SetDlogItem(VideoControl, FirstChannelID + 3, ord(VideoChannel = 3)); end; procedure UpdateVideoControl; begin if VideoControl <> nil then SetDlogItem(VideoControl, TriggerID, ord(ExternalTrigger)); end; procedure ShowOffsetAndGain (offset, gain: integer); var str: str255; begin RealToString(offset, 3, 0, str); if str[1] = ' ' then str[1] := '0'; if str[2] = ' ' then str[2] := '0'; SetDString(VideoControl, OffsetID, str); RealToString(gain, 3, 0, str); if str[1] = ' ' then str[1] := '0'; if str[2] = ' ' then str[2] := '0'; SetDString(VideoControl, GainID, str); end; procedure ShowVideoControl; var gain: integer; begin InitCursor; VideoControl := GetNewDialog(130, nil, pointer(-1)); ShowChannel; SetDlogItem(VideoControl, InvertID, ord(InvertVideo)); SetDlogItem(VideoControl, HighlightID, ord(HighlightSaturatedPixels)); SetDlogItem(VideoControl, TriggerID, ord(ExternalTrigger)); SetDlogItem(VideoControl, SyncID, ord(SyncMode = SeparateSync)); gain := 255 - (DacHigh - DacLow); ShowOffsetAndGain(DacLow, gain); end; function NoScion:boolean; var NotFound:boolean; begin NotFound:=(FrameGrabber <> ScionLG3) and (FrameGrabber<>ScionAg5) and (FrameGrabber<>ScionVG5f); if NotFound then PutError('Programmable offset and gain are only supported on Scion frame grabbers.'); NoScion:=NotFound; end; procedure DoVideoControl (item: integer); var i: integer; OutOfRange, WasDigitizing: boolean; offset, gain, inc, count: integer; procedure SetVideoItem (item, value: integer); begin if VideoControl <> nil then SetDlogItem(VideoControl, item, value); end; begin InitCursor; gain := 255 - (DacHigh - DacLow); if (item >= FirstChannelID) and (item <= (FirstChannelID + 3)) then begin VideoChannel := item - FirstChannelID; if VideoControl <> nil then ShowChannel; if digitizing then ResetFrameGrabber; end; if item = InvertID then begin InvertVideo := not InvertVideo; SetVideoItem(InvertID, ord(InvertVideo)); if digitizing then ResetFrameGrabber; end; if item = HighlightID then begin HighlightSaturatedPixels := not HighlightSaturatedPixels; SetVideoItem(HighlightID, ord(HighlightSaturatedPixels)); if digitizing then begin if HighlightSaturatedPixels then HighlightPixels else LoadLUT(info^.ctable); end; end; if item = TriggerID then begin ExternalTrigger := not ExternalTrigger; case FrameGrabber of QuickCapture, ScionLG3, ScionAG5, ScionVG5f: begin WasDigitizing := digitizing; StopDigitizing; if ExternalTrigger and WasDigitizing then StartDigitizing; end; otherwise ExternalTrigger := false; end; SetVideoItem(TriggerID, ord(ExternalTrigger)); end; if item = SyncID then begin if SyncMode <> SeparateSync then SyncMode := SeparateSync else SyncMode := NormalSync; case FrameGrabber of ScionLG3, ScionAG5, ScionVG5f: if digitizing then ResetFrameGrabber; QuickCapture: begin PutError('Sync is not under program control on the QuickCapure card.'); SyncMode := NormalSync; AbortMacro; end; otherwise ; end; SetVideoItem(SyncID, ord(SyncMode = SeparateSync)); end; if (item >= OffsetUpID) and (item <= GainDownID) then begin if NoScion then exit(DoVideoControl); offset := DacLow; inc := 1; count := 0; repeat count := count + 1; if count > 2 then inc := 2; if count > 4 then inc := 5; if count > 8 then inc := 10; case item of OffsetUpID: begin offset := offset + inc; SetOffset(offset, gain); end; OffsetDownID: begin offset := offset - inc; SetOffset(offset, gain); end; GainUpID: begin gain := gain + inc; SetGain(offset, gain); end; GainDownID: begin gain := gain - inc; SetGain(offset, gain); end; end; {case} ShowOffsetAndGain(DacLow, gain); if Digitizing and (count > 1) then begin DacLowReg^ := DacLow; DacHighReg^ := DacHigh; CaptureAndDisplayFrame; if ContinuousHistogram then begin ShowContinuousHistogram; DrawHistogram end end else wait(5); until not button; end; if item = ResetID then begin if NoScion then exit(DoVideoControl); if FrameGrabber=ScionLG3 then begin DacLow := DefaultLG3DacLow; DacHigh := DefaultLG3DacHigh; end else if FrameGrabber = ScionAG5 then begin DacLow := DefaultAG5DacLow; DacHigh := DefaultAG5DacHigh; end else begin DacLow := DefaultVG5DacLow; DacHigh := DefaultVG5DacHigh; end; gain := 255 - (DacHigh - DacLow); ParamText(long2str(DacLow), long2str(gain), '', ''); ShowOffsetAndGain(DacLow, gain); end; if FramesToAverage < 2 then FramesToAverage := 2; if (FrameGrabber = ScionLG3) or (FrameGrabber=ScionAG5) or (FrameGrabber=ScionVG5f) then begin DacLowReg^ := DacLow; DacHighReg^ := DacHigh; end; end; end.