1
by Michael Hanke
Import upstream version 0.20100725.1~dfsg.1 |
1 |
unit autoroi; |
2 |
||
3 |
interface
|
|
4 |
||
5 |
uses
|
|
6 |
{$IFNDEF FPC}
|
|
7 |
RXSpin,capmenu, |
|
8 |
{$ELSE}
|
|
9 |
Spin,lResources, |
|
10 |
{$ENDIF}
|
|
11 |
{$IFNDEF Unix} Windows,{$ENDIF} |
|
12 |
SysUtils, Classes, Graphics, Controls, Forms, Dialogs, |
|
13 |
Buttons, StdCtrls, define_types, ExtCtrls, nifti_img,nifti_img_view; |
|
14 |
||
15 |
type
|
|
16 |
||
17 |
{ TAutoROIForm }
|
|
18 |
||
19 |
TAutoROIForm = class(TForm) |
|
20 |
OriginLabel: TLabel; |
|
21 |
OriginBtn: TSpeedButton; |
|
22 |
EdgeEdit: TSpinEdit; |
|
23 |
RadiusEdit: TSpinEdit; |
|
24 |
ErodeEdit: TSpinEdit; |
|
25 |
ROIconstraint: TComboBox; |
|
26 |
VarianceEdit: TSpinEdit; |
|
27 |
DiffLabel: TLabel; |
|
28 |
Label1: TLabel; |
|
29 |
Label2: TLabel; |
|
30 |
Label3: TLabel; |
|
31 |
AutoROIBtn: TSpeedButton; |
|
32 |
CancelBtn: TSpeedButton; |
|
33 |
Timer1: TTimer; |
|
34 |
Label4: TLabel; |
|
35 |
ExcludeBlackCheck: TCheckBox; |
|
36 |
procedure OriginBtnClick(Sender: TObject); |
|
37 |
procedure PreviewBtnClick(Sender: TObject); |
|
38 |
procedure FormShow(Sender: TObject); |
|
39 |
procedure FormCreate(Sender: TObject); |
|
40 |
procedure FormHide(Sender: TObject); |
|
41 |
procedure AutoROIBtnClick(Sender: TObject); |
|
42 |
procedure CancelBtnClick(Sender: TObject); |
|
43 |
procedure AutoROIchange(Sender: TObject); |
|
44 |
procedure Timer1Timer(Sender: TObject); |
|
45 |
procedure FormDestroy(Sender: TObject); |
|
46 |
private
|
|
47 |
{ Private declarations }
|
|
48 |
public
|
|
49 |
{ Public declarations }
|
|
50 |
end; |
|
51 |
||
52 |
procedure ROICluster ({lInROIBuf: bytep;} lXdim, lYDim, lZDim,lXOriginIn,lYOrigin,lZOrigin: integer; lDeleteNotFill: boolean); |
|
53 |
var
|
|
54 |
AutoROIForm: TAutoROIForm; |
|
55 |
gOriginX,gOriginY,gOriginZ: integer; |
|
56 |
implementation
|
|
57 |
||
58 |
{$IFNDEF FPC}
|
|
59 |
{$R *.DFM}
|
|
60 |
{$ENDIF}
|
|
61 |
||
62 |
||
63 |
procedure TAutoROIForm.OriginBtnClick(Sender: TObject); |
|
64 |
begin
|
|
65 |
gOriginX := ImgForm.XViewEdit.value; |
|
66 |
gOriginY := ImgForm.YViewEdit.value; |
|
67 |
gOriginZ := ImgForm.ZViewEdit.value; |
|
68 |
OriginLabel.Caption := 'Origin: '+inttostr(gOriginX)+'x'+inttostr(gOriginY)+'x'+inttostr(gOriginZ); |
|
69 |
PreviewBtnClick(sender); |
|
70 |
end; |
|
71 |
||
72 |
procedure TAutoROIForm.PreviewBtnClick(Sender: TObject); |
|
73 |
var
|
|
74 |
lXmm,lYmm,lZmm,lSqrRadius: single; |
|
75 |
lExcludeBlackIfZero,//lX,lY,lZ, //abba |
|
76 |
{lMaxROISz,}lEdge,lOriginPos,lROISz,lOriginIntensity,lVariance,lXdim, lYDim, lZDim: integer; |
|
77 |
lErodeCycles,lQTail,lQHead,lSliceSz,lQSz,lInc,lVolSz{,lX,lY,lZ}: integer; |
|
78 |
lROIConstrain,lReadFilteredData: boolean; |
|
79 |
lQra: LongIntP; |
|
80 |
lSourceBuffer,lBuff,lPreErodeBuff: ByteP; |
|
81 |
const
|
|
82 |
kFillValue = -2; |
|
83 |
Procedure IncQra(var lVal, lQSz: integer); |
|
84 |
begin
|
|
85 |
inc(lVal); |
|
86 |
if lVal >= lQSz then |
|
87 |
lVal := 1; |
|
88 |
end; |
|
89 |
function UnsmoothedIntensity(lPixel: integer): integer; //1381 |
|
90 |
begin
|
|
91 |
if lReadFilteredData then |
|
92 |
result := lBuff^[lPixel] |
|
93 |
else
|
|
94 |
Result :=lSourceBuffer^[lPixel]; |
|
95 |
end; |
|
96 |
||
97 |
function MeanIntensity(lPixel: integer): integer; |
|
98 |
var lV: integer; |
|
99 |
begin
|
|
100 |
if lReadFilteredData then |
|
101 |
result := lBuff^[lPixel] |
|
102 |
else if ((lPixel-lSliceSz) > 0) and ((lPixel+lSliceSz) <= lVolSz) then begin |
|
103 |
lV :=lSourceBuffer^[lPixel]+lSourceBuffer^[lPixel+1]+lSourceBuffer^[lPixel-1] //L/R |
|
104 |
+lSourceBuffer^[lPixel+lXdim]+lSourceBuffer^[lPixel-lXdim] //Anterior/Posterior |
|
105 |
+lSourceBuffer^[lPixel+lSliceSz]+lSourceBuffer^[lPixel-lSliceSz]; //Dorsal/Ventral |
|
106 |
result := lV div 7; |
|
107 |
end else result := lSourceBuffer^[lPixel];//1401 gImageBackupBuffer[lPixel] |
|
108 |
end; |
|
109 |
procedure Check(lPixel,lIntensity: integer); |
|
110 |
var lSmoothInten :integer; |
|
111 |
begin
|
|
112 |
lSmoothInten := MeanIntensity(lPixel); |
|
113 |
if (lROIConstrain) and (gBGImg.VOIUndoVol^[lPixel] > 0) then //1410 |
|
114 |
//constrain
|
|
115 |
else if (lBuff^[lPixel]<> 255) and (UnsmoothedIntensity(lPixel) > lExcludeBlackIfZero {1381}) and (abs(lSmoothInten-lIntensity)<=lEdge) and(abs(lSmoothInten-lOriginIntensity)<=lVariance) {}then begin//add item |
|
116 |
incQra(lQHead,lQSz); |
|
117 |
inc(lROISz); |
|
118 |
lBuff^[lPixel] := 255; |
|
119 |
lQra^[lQHead] := lPixel; |
|
120 |
end; |
|
121 |
end; |
|
122 |
||
123 |
PROCEDURE RetirePixel; //FIFO cleanup |
|
124 |
function WithinRadius(lXs,lYs,lZs:integer): boolean; |
|
125 |
begin
|
|
126 |
if (sqr((lXs-gOriginX)*lXmm)+sqr((lYs-gOriginY)*lYmm)+sqr((lZs-gOriginZ)*lZmm)) > lSqrRadius then |
|
127 |
result := false |
|
128 |
else
|
|
129 |
result := true; |
|
130 |
end; |
|
131 |
VAR
|
|
132 |
lVal,lXPos,lYPos,lZPos,lIntensity: integer; |
|
133 |
BEGIN
|
|
134 |
lVal := lQra^[lQTail]; |
|
135 |
lXpos := lVal mod lXdim; |
|
136 |
if lXpos = 0 then lXPos := lXdim; |
|
137 |
||
138 |
lYpos := (1+((lVal-1) div lXdim)) mod lYDim; |
|
139 |
if lYPos = 0 then lYPos := lYdim; |
|
140 |
||
141 |
lZpos := ((lVal-1) div lSliceSz)+1; |
|
142 |
if lReadFilteredData then |
|
143 |
lIntensity := 128 |
|
144 |
else
|
|
145 |
lIntensity := lSourceBuffer^[lVal];//1401 gImageBackupBuffer[lVal]; |
|
146 |
if (lXpos > 1) and WithinRadius(lXpos-1,lYpos,lZpos) then Check(lVal -1,lIntensity);//check to left |
|
147 |
if (lXPos < lXDim) and (WithinRadius(lXpos+1,lYpos,lZpos)) then Check(lVal + 1,lIntensity); //check to right |
|
148 |
if (lYpos > 1) and (WithinRadius(lXpos,lYpos-1,lZpos)) then Check(lVal -lXdim,lIntensity);//check previous line |
|
149 |
if (lYPos < lYDim) and (WithinRadius(lXpos,lYpos+1,lZpos)) then Check(lVal + lXdim,lIntensity); //check next line |
|
150 |
if (lZpos > 1) and (WithinRadius(lXpos,lYpos,lZpos-1)) then Check(lVal -lSliceSz,lIntensity);//check previous slice |
|
151 |
if (lZPos < lZDim) and (WithinRadius(lXpos,lYpos,lZpos+1)) then Check(lVal + lSliceSz,lIntensity); //check next slice |
|
152 |
incQra(lQTail,lQSz); //done with this pixel |
|
153 |
END; |
|
154 |
||
155 |
procedure FillStart (lPt: integer); {FIFO algorithm: keep memory VERY low} |
|
156 |
var lI: integer; |
|
157 |
begin
|
|
158 |
for lI := 1 to lQsz do |
|
159 |
lQra^[lI] := 0; |
|
160 |
lQHead := 0; |
|
161 |
lQTail := 1; |
|
162 |
lROISz := 0; |
|
163 |
Check(lPt,lOriginIntensity); |
|
164 |
RetirePixel; |
|
165 |
while ((lQHead+1) <> lQTail) do begin//complete until all voxels in buffer have been tested |
|
166 |
RetirePixel; |
|
167 |
if (lQHead = lQSz) and (lQTail = 1) then |
|
168 |
exit; //break condition: avoids possible infinite loop where QTail is being incremented but QHead is stuck at maximum value |
|
169 |
end; |
|
170 |
end; |
|
171 |
||
172 |
function ROIOnEdge (lVal: integer): boolean; |
|
173 |
BEGIN
|
|
174 |
result := false; |
|
175 |
if lBuff^[lVal] <> 255 then exit; //not ROI - is not boundary |
|
176 |
//Find
|
|
177 |
if ((lVal-lSliceSz) > 0) and ((lVal+lSliceSz) <= lVolSz) then begin |
|
178 |
if lBuff^[lVal+1] = 0 then result := true; |
|
179 |
if lBuff^[lVal-1] = 0 then result := true; |
|
180 |
if lBuff^[lVal+lXdim] = 0 then result := true; |
|
181 |
if lBuff^[lVal-lXdim] = 0 then result := true; |
|
182 |
if lBuff^[lVal+lSliceSz] = 0 then result := true; |
|
183 |
if lBuff^[lVal-lSliceSz] = 0 then result := true; |
|
184 |
end; |
|
185 |
end; |
|
186 |
||
187 |
function ZeroOnEdge (lVal: integer): boolean; |
|
188 |
BEGIN
|
|
189 |
result := false; |
|
190 |
if lBuff^[lVal] <> 0 then exit; //not ROI - is not boundary |
|
191 |
//Find
|
|
192 |
if ((lVal-lSliceSz) > 0) and ((lVal+lSliceSz) <= lVolSz) then begin |
|
193 |
if lBuff^[lVal+1] = 255 then result := true; |
|
194 |
if lBuff^[lVal-1] = 255 then result := true; |
|
195 |
if lBuff^[lVal+lXdim] = 255 then result := true; |
|
196 |
if lBuff^[lVal-lXdim] = 255 then result := true; |
|
197 |
if lBuff^[lVal+lSliceSz] = 255 then result := true; |
|
198 |
if lBuff^[lVal-lSliceSz] = 255 then result := true; |
|
199 |
end; |
|
200 |
end; |
|
201 |
||
202 |
begin //alfa666 |
|
203 |
if (gMRIcroOverlay[kBGOverlayNum].ScrnBufferItems<1) or (gMRIcroOverlay[kBGOverlayNum].ScrnBufferItems<>gBGImg.VOIUndoVolItems) then exit; |
|
204 |
//if gImageBackupSz <> gMRIcroOverlay[kBGOverlayNum].ScrnBufferItems then
|
|
205 |
//UpdateBackupBuffer;
|
|
206 |
lXdim := gBGImg.ScrnDim[1]; |
|
207 |
lYDim := gBGImg.ScrnDim[2]; |
|
208 |
lZDim := gBGImg.ScrnDim[3]; |
|
209 |
if (gBGImg.Scrnmm[1] = 0) or (gBGImg.Scrnmm[2]=0) or (gBGImg.Scrnmm[3]=0) then begin |
|
210 |
lXmm := 1; |
|
211 |
lYmm := 1; |
|
212 |
lZmm := 1; |
|
213 |
end else begin |
|
214 |
lXmm := gBGImg.Scrnmm[1]; |
|
215 |
lYmm := gBGImg.Scrnmm[2]; |
|
216 |
lZmm := gBGImg.Scrnmm[3]; |
|
217 |
end; |
|
218 |
lSliceSz := lXdim * lYdim; |
|
219 |
lVolSz := lSliceSz*lZdim; |
|
220 |
//lMaxROISz := round(PctImg.Value/100 * lVolSz);
|
|
221 |
lOriginPos := gOriginX + ((gOriginY-1)*lXdim) + ((gOriginZ-1)*lSliceSz); |
|
222 |
if (lOriginPos < 1) or (lVolSz <> gMRIcroOverlay[kBGOverlayNum].ScrnBufferItems) or (lOriginPos > lVolSz) or (gMRIcroOverlay[kBGOverlayNum].ScrnBufferItems <> gBGImg.VOIUndoVolItems) then |
|
223 |
exit; |
|
224 |
{$IFNDEF FPC}
|
|
225 |
lVariance := AutoROIForm.VarianceEdit.asinteger; //asinteger; |
|
226 |
lEdge := AutoROIForm.EdgeEdit.asinteger; |
|
227 |
lSqrRadius := sqr(AutoROIForm.RadiusEdit.asinteger); |
|
228 |
{$ELSE}
|
|
229 |
lVariance := AutoROIForm.VarianceEdit.value; //asinteger; |
|
230 |
lEdge := AutoROIForm.EdgeEdit.value; |
|
231 |
lSqrRadius := sqr(AutoROIForm.RadiusEdit.value); |
|
232 |
{$ENDIF}
|
|
233 |
if (lXDim < 4) or (lYDim < 4) or (lZDim < 4) or (lVolSz < 1) then exit; |
|
234 |
lSourceBuffer := gMRIcroOverlay[kBGOverlayNum].ScrnBuffer;//gBuffer; |
|
235 |
//Next - START count cluster size
|
|
236 |
lQSz := (lVolSz div 4)+8; |
|
237 |
GetMem(lQra,lQsz * sizeof(longint) ); |
|
238 |
//check positive clusters....
|
|
239 |
Getmem(lBuff,lVolSz); |
|
240 |
FillChar(lBuff^,lVolSz, 0); |
|
241 |
//Move(gImageBackupBuffer^,lBuff^,lVolSz);
|
|
242 |
if ExcludeBlackCheck.checked then //1381 |
|
243 |
lExcludeBlackIfZero := 0 //0 |
|
244 |
else
|
|
245 |
lExcludeBlackIfZero := -1;//impossible 8-bit value: do not use this feature |
|
246 |
lOriginIntensity := lSourceBuffer^[lOriginPos]; //1401 gImageBackupBuffer[lOriginPos]; |
|
247 |
lReadFilteredData := false; |
|
248 |
//ROIconstrainCheck.enabled := (gROIBupSz > 1); //1410: next 3 lines
|
|
249 |
ROIconstraint.enabled := (gMRIcroOverlay[kVOIOverlayNum].ScrnBufferItems > 1); //1410: next 3 lines |
|
250 |
if (ROIconstraint.ItemIndex = 2) and (ROIconstraint.enabled) then |
|
251 |
lROIConstrain := true |
|
252 |
else
|
|
253 |
lROIconstrain := false; |
|
254 |
FillStart(lOriginPos); |
|
255 |
lROIConstrain := false;//1410 |
|
256 |
//START: ERODE/DILATE CYCLES
|
|
257 |
{$IFNDEF FPC}
|
|
258 |
lErodeCycles := AutoROIForm.ErodeEdit.asinteger; |
|
259 |
{$ELSE}
|
|
260 |
lErodeCycles := AutoROIForm.ErodeEdit.value; |
|
261 |
{$ENDIF}
|
|
262 |
if lErodeCycles > 0 then begin |
|
263 |
Getmem(lPreErodeBuff,lVolSz); |
|
264 |
Move(lBuff^,lPreErodeBuff^,lVolSz); |
|
265 |
for lQHead := 1 to lErodeCycles do begin//ERODE |
|
266 |
for lInc := 1 to lVolSz do |
|
267 |
if ROIonEdge(lInc) then |
|
268 |
lBuff^[lInc] :=254; |
|
269 |
for lInc := 1 to lVolSz do |
|
270 |
if lBuff^[lInc]=254 then |
|
271 |
lBuff^[lInc] := 0; //erode |
|
272 |
end;//for ErodeCycles = ERODE |
|
273 |
//SET ALL VOXELS THAT HAVE SURVIVED EROSION TO 128, WE THEN GROW THE ORIGIN
|
|
274 |
for lInc := 1 to lVolSz do |
|
275 |
if lBuff^[lInc] =255 then lBuff^[lInc] := 128; |
|
276 |
//NOW - ONLY PRESERVE STUFF CONNECTED TO ORIGIN
|
|
277 |
lBuff^[lOriginPos] := 128; |
|
278 |
lOriginIntensity := 128; |
|
279 |
lVariance := 2; |
|
280 |
lEdge := 2; |
|
281 |
lReadFilteredData := true; |
|
282 |
FillStart(lOriginPos); |
|
283 |
//SWITCH OFF ALL UNCONNECTED BLOBS
|
|
284 |
for lInc := 1 to lVolSz do |
|
285 |
if lBuff^[lInc] =128 then lBuff^[lInc] := 0; |
|
286 |
//for lInc := 1 to lVolSz do
|
|
287 |
// if lBuff[lInc] > 0 then showmessage(inttostr(lBuff[lInc]));// := 0;
|
|
288 |
||
289 |
for lQHead := 1 to lErodeCycles do begin//DILATE |
|
290 |
for lInc := 1 to lVolSz do |
|
291 |
if (lPreErodeBuff^[lInc] = 255) and (ZeroonEdge(lInc)) then |
|
292 |
lBuff^[lInc] :=254; |
|
293 |
for lInc := 1 to lVolSz do |
|
294 |
if lBuff^[lInc]=254 then |
|
295 |
lBuff^[lInc] := 255; //erode |
|
296 |
end;//for ErodeCycles = DILATE |
|
297 |
Freemem(lPreErodeBuff); |
|
298 |
{}
|
|
299 |
end; //ERODE cycles > 0 |
|
300 |
//END: ERODE/DILATE
|
|
301 |
Freemem(lQra); |
|
302 |
ROIconstraint.enabled := (gMRIcroOverlay[kVOIOverlayNum].ScrnBufferItems > 1); //1410: next 3 lines |
|
303 |
if (ROIconstraint.ItemIndex = 1) and (ROIconstraint.enabled) then begin //delete ROI |
|
304 |
for lInc := 1 to gMRIcroOverlay[kVOIOverlayNum].ScrnBufferItems do //gROIBup |
|
305 |
if (lBuff^[lInc] = 255) then |
|
306 |
lBuff^[lInc] := 0 |
|
307 |
else
|
|
308 |
lBuff^[lInc] := gBGImg.VOIUndoVol^[lInc]; |
|
309 |
end else (*if true {alfa (gDynSz > 1) and (gROIBupsz > 1) {and (gImageBackupSz = gDynSz){} then begin |
|
310 |
for lInc := 1 to gMRIcroOverlay[kVOIOverlayNum].ScrnBufferItems do
|
|
311 |
if lBuff[lInc] = 255 then
|
|
312 |
else if gImageBackupBuffer[lInc] = 255 then
|
|
313 |
lBuff[lInc] := 255//255;
|
|
314 |
else lBuff[lInc] := lSourceBuffer[lInc];
|
|
315 |
||
316 |
end else *)
|
|
317 |
for lInc := 1 to lVolSz do |
|
318 |
if lBuff^[lInc] <> 255 then |
|
319 |
lBuff^[lInc] := gBGImg.VOIUndoVol^[lInc] |
|
320 |
else
|
|
321 |
lBuff^[lInc] := kVOI8bit;//1401 gImageBackupBuffer[lInc]; |
|
322 |
Move(lBuff^,gMRIcroOverlay[kVOIOverlayNum].ScrnBuffer^,lVolSz); |
|
323 |
Freemem(lBuff); |
|
324 |
//END check clusters
|
|
325 |
ImgForm.RefreshImagesTimer.Enabled := true; |
|
326 |
end; |
|
327 |
||
328 |
procedure TAutoROIForm.FormShow(Sender: TObject); |
|
329 |
begin
|
|
330 |
EnsureVOIOpen; |
|
331 |
CreateUndoVol; |
|
332 |
AutoROIForm.ModalResult := mrCancel; |
|
333 |
ROIconstraint.Enabled := (gMRIcroOverlay[kVOIOverlayNum].ScrnBufferItems > 1); |
|
334 |
OriginBtn.OnClick(sender); |
|
335 |
//DeleteCheck.enabled := (gROIBupSz > 1);
|
|
336 |
//ROIConstrainCheck.enabled := (gROIBupSz > 1);
|
|
337 |
end; |
|
338 |
||
339 |
procedure TAutoROIForm.FormCreate(Sender: TObject); |
|
340 |
begin
|
|
341 |
{$IFNDEF FPC}
|
|
342 |
ROIconstraint.SetItemIndex(0);//1410 |
|
343 |
{$ELSE}
|
|
344 |
ROIconstraint.ItemIndex := (0);//1410 |
|
345 |
{$ENDIF}
|
|
346 |
end; |
|
347 |
||
348 |
procedure TAutoROIForm.FormHide(Sender: TObject); |
|
349 |
begin
|
|
350 |
// if (AutoROIForm.ModalResult = mrCancel) and (gBGImg.VOIUndoVolItems > 1) and (gBGImg.VOIUndoVolItems = gMRIcroOverlay[kVOIOverlayNum].ScrnBufferItems) then
|
|
351 |
// Move(gImageBackupBuffer^,gMRIcroOverlay[kVOIOverlayNum].ScrnBuffer^,gImageBackupSz);
|
|
352 |
if (AutoROIForm.ModalResult = mrCancel) then |
|
353 |
UndoVolVOI; |
|
354 |
if not (AutoROIForm.ModalResult = mrCancel) then |
|
355 |
gBGImg.VOIchanged := true; |
|
356 |
//if gImageBackupSz <> 0 then Freemem(gImageBackupBuffer);
|
|
357 |
//gImageBackupSz := 0;
|
|
358 |
ImgForm.Fill3DBtn.Down := false; |
|
359 |
ImgForm.RefreshImagesTimer.Enabled := true; |
|
360 |
end; |
|
361 |
||
362 |
//Previous: create 3D ROI
|
|
363 |
//Below fill bubbles in 3D ROIS
|
|
364 |
//ROIcluster Follows
|
|
365 |
(***********************************************************88
|
|
366 |
************************************************************
|
|
367 |
**********************************************************)
|
|
368 |
procedure ROICluster (lXdim, lYDim, lZDim,lXOriginIn,lYOrigin,lZOrigin: integer; lDeleteNotFill: boolean); |
|
369 |
var
|
|
370 |
lVariability,lOrigin,lClusterInputValue,lClusterOutputValue, lClusterSz,lQTail, |
|
371 |
lXOrigin,lQHead,lSliceSz,lQSz,lInc,lVolSz: integer; |
|
372 |
lXInc,lYInc,lZInc,lSlicePos,lYPos, |
|
373 |
lMinX,lMaxX,lMinY,lMaxY,lMinZ,lMaxZ, |
|
374 |
lMinXBound,lMaxXBound,lMinYBound,lMaxYBound,lMinZBound,lMaxZBound: integer; |
|
375 |
lAtEdge: boolean; |
|
376 |
lROIBuf: bytep; |
|
377 |
lQra: LongIntP; |
|
378 |
const
|
|
379 |
kFillValue = -2; |
|
380 |
Procedure IncQra(var lVal, lQSz: integer); |
|
381 |
begin
|
|
382 |
inc(lVal); |
|
383 |
if lVal >= lQSz then |
|
384 |
lVal := 1; |
|
385 |
end; |
|
386 |
||
387 |
procedure Check(lPixel: integer); |
|
388 |
begin
|
|
389 |
if (abs(lROIBuf^[lPixel] - lClusterInputValue)) <= lVariability then begin//add item |
|
390 |
incQra(lQHead,lQSz); |
|
391 |
inc(lClusterSz); |
|
392 |
lROIBuf^[lPixel] := lClusterOutputValue; |
|
393 |
lQra^[lQHead] := lPixel; |
|
394 |
end; |
|
395 |
end; |
|
396 |
||
397 |
PROCEDURE RetirePixel; //FIFO cleanup |
|
398 |
VAR
|
|
399 |
lVal,lXPos,lYPos,lZPos: integer; |
|
400 |
BEGIN
|
|
401 |
lVal := lQra^[lQTail]; |
|
402 |
lXpos := lVal mod lXdim; |
|
403 |
if lXpos = 0 then lXPos := lXdim; |
|
404 |
||
405 |
lYpos := (1+((lVal-1) div lXdim)) mod lYDim; |
|
406 |
if lYPos = 0 then lYPos := lYdim; |
|
407 |
||
408 |
lZpos := ((lVal-1) div lSliceSz)+1; |
|
409 |
||
410 |
if lXPos < lMinX then lMinX := lXPos; |
|
411 |
if lXPos > lMaxX then lMaxX := lXPos; |
|
412 |
if lXpos > lMinXBound then Check(lVal -1);//check to left |
|
413 |
if lXPos < lMaxXBound then Check(lVal + 1); //check to right |
|
414 |
||
415 |
if lYPos < lMinY then lMinY := lYPos; |
|
416 |
if lYPos > lMaxY then lMaxY := lYPos; |
|
417 |
if lYpos > lMinYBound then Check(lVal -lXdim);//check previous line |
|
418 |
if lYPos < lMaxYBound then Check(lVal + lXdim); //check next line |
|
419 |
||
420 |
if lZPos < lMinZ then lMinZ := lZPos; |
|
421 |
if lZPos > lMaxZ then lMaxZ := lZPos; |
|
422 |
if lZpos > lMinZBound then Check(lVal -lSliceSz);//check previous slice |
|
423 |
if lZPos < lMaxZBound then Check(lVal + lSliceSz); //check next slice |
|
424 |
||
425 |
incQra(lQTail,lQSz); //done with this pixel |
|
426 |
END; |
|
427 |
||
428 |
procedure FillStart (lPt: integer); {FIFO algorithm: keep memory VERY low} |
|
429 |
var lI: integer; |
|
430 |
begin
|
|
431 |
//1414 follows
|
|
432 |
for lI := 1 to lQsz do |
|
433 |
lQra^[lI] := 0; |
|
434 |
lQHead := 0; |
|
435 |
lQTail := 1; |
|
436 |
Check(lPt); |
|
437 |
RetirePixel; |
|
438 |
while ((lQHead+1) <> lQTail) do begin//complete until all voxels in buffer have been tested |
|
439 |
RetirePixel; |
|
440 |
if (lQHead = lQSz) and (lQTail = 1) then |
|
441 |
exit; //break condition: avoids possible infinite loop where QTail is being incremented but QHead is stuck at maximum value |
|
442 |
end; |
|
443 |
end; |
|
444 |
||
445 |
procedure SelectClusters (lInput,lOutput: integer); |
|
446 |
begin
|
|
447 |
lClusterSz := 0; |
|
448 |
lClusterInputValue := lInput; |
|
449 |
lClusterOutputValue := lOutput; |
|
450 |
FillStart(lOrigin); |
|
451 |
end; |
|
452 |
||
453 |
function Lo (lVolumeEdge,lObjectEdge: integer): integer; |
|
454 |
begin
|
|
455 |
if lVolumeEdge > lObjectEdge then |
|
456 |
result := lObjectEdge |
|
457 |
else begin |
|
458 |
lAtEdge := true; |
|
459 |
result := lVolumeEdge; |
|
460 |
end; |
|
461 |
end; |
|
462 |
||
463 |
function Hi (lVolumeEdge,lObjectEdge: integer): integer; |
|
464 |
begin
|
|
465 |
if lVolumeEdge < lObjectEdge then |
|
466 |
result := lObjectEdge |
|
467 |
else begin |
|
468 |
lAtEdge := true; |
|
469 |
result := lVolumeEdge; |
|
470 |
end; |
|
471 |
end; |
|
472 |
||
473 |
begin
|
|
474 |
lXOrigin := lXOriginIn; |
|
475 |
lVolSz := lXdim*lYdim*lZdim; |
|
476 |
if gMRIcroOverlay[kVOIOverlayNum].ScrnBufferItems <> lVolSz then begin |
|
477 |
showmessage('You need to draw or load a VOI in order to use the 3D bubble tool.'); |
|
478 |
exit; |
|
479 |
end; |
|
480 |
CreateUndoVol; |
|
481 |
lSliceSz := lXdim * lYdim; |
|
482 |
lMinX:=lXOrigin; |
|
483 |
lMaxX:=lXOrigin; |
|
484 |
lMinY:=lYOrigin; |
|
485 |
lMaxY:=lYOrigin; |
|
486 |
lMinZ:=lZOrigin; |
|
487 |
lMaxZ:=lZOrigin; |
|
488 |
lMinXBound := 1; |
|
489 |
lMaxXBound := lXDim; |
|
490 |
lMinYBound := 1; |
|
491 |
lMaxYBound := lYDim; |
|
492 |
lMinZBound := 1; |
|
493 |
lMaxZBound := lZDim; |
|
494 |
lOrigin := lXOrigin + ((lYOrigin-1)*lXdim)+((lZOrigin-1)*lSliceSz); |
|
495 |
if (lOrigin > lVolSz) or (lXDim < 4) or (lYDim < 4) or (lZDim < 4) or (lVolSz < 1) {or (gROIBupSz <> lVolSz )} then exit; |
|
496 |
if (gMRIcroOverlay[kVOIOverlayNum].ScrnBuffer^[lOrigin] = 0) then begin |
|
497 |
showmessage('You must click directly on a ROI to select it. The 3D ROI bubble tool will not work unless you choose the ROI you wish to fill/delete.'); |
|
498 |
exit; |
|
499 |
end; |
|
500 |
GetMem(lROIBuf, lVolSz); |
|
501 |
for lInc := 1 to lVolSz do |
|
502 |
if gMRIcroOverlay[kVOIOverlayNum].ScrnBuffer^[lInc] > 0 then//ROI |
|
503 |
lROIBuf^[lInc] := 1 |
|
504 |
else
|
|
505 |
lROIBuf^[lInc] := 0; |
|
506 |
//BEGIN: define selected ROI contiguous cluster
|
|
507 |
lQSz := (lVolSz div 4)+8; |
|
508 |
GetMem(lQra,lQsz * sizeof(longint) ); |
|
509 |
lVariability := 0; //only convert images that are exactly 1 |
|
510 |
SelectClusters(1,255); //selected 3D ROI is 255, other ROI = 1, nonROI 0 |
|
511 |
//END: define selected roi
|
|
512 |
//BEGIN: either delete selected ROI, _OR_ fill bubbles in selected ROI
|
|
513 |
if lDeleteNotFill then begin |
|
514 |
for lInc := 1 to lVolSz do |
|
515 |
if lROIBuf^[lInc] = 1 then //alfa |
|
516 |
lROIBuf^[lInc] := gMRIcroOverlay[kVOIOverlayNum].ScrnBuffer^[lInc] //a different ROI |
|
517 |
else
|
|
518 |
lROIBuf^[lInc] := 0;//gMRIcroOverlay[kVOIOverlayNum].ScrnBuffer[lInc]; //1402 selected ROI or non-ROI |
|
519 |
end else begin //fill bubbles in selected ROI |
|
520 |
//FindROIbounds;
|
|
521 |
lMinXBound := Hi(1,lMinX-1); |
|
522 |
lMaxXBound := Lo(lXDim,lMaxX+1); |
|
523 |
lMinYBound := Hi(1,lMinY-1); |
|
524 |
lMaxYBound := Lo(lYDim,lMaxY+1); |
|
525 |
lMinZBound := Hi(1,lMinZ-1); |
|
526 |
lMaxZBound := Lo(lZDim,lMaxZ+1); |
|
527 |
lOrigin := (lMinXBound) + ((lMinYBound-1)*lXdim)+((lMinZBound-1)*lSliceSz); |
|
528 |
lVariability := 2;//convert voxels that are either 0 or 1 to 1 |
|
529 |
SelectClusters(1,128); |
|
530 |
//now bubbles trapped in volume are set to zero
|
|
531 |
//we next need to distinguish bubbles from unmarked voxels outside the searched object boundary
|
|
532 |
for lZInc := lMinZBound to lMaxZBound do begin |
|
533 |
lSlicePos := (lZInc-1) * lSliceSz; |
|
534 |
for lYInc := lMinYBound to lMaxYBound do begin |
|
535 |
lYPos := (lYInc-1) * lXDim; |
|
536 |
for lXInc := lMinXBound to lMaxXBound do begin |
|
537 |
lInc := lXInc + lYPos + lSlicePos; |
|
538 |
if lROIBuf^[lInc] = 0 then lROIBuf^[lInc] := 33; |
|
539 |
end; //for X |
|
540 |
end; //for Y |
|
541 |
end; //for Z |
|
542 |
||
543 |
for lInc := 1 to lVolSz do |
|
544 |
if lROIBuf^[lInc] = 33 then |
|
545 |
lROIBuf^[lInc] := kVOI8bit //bubble in selected ROI |
|
546 |
else
|
|
547 |
lROIBuf^[lInc] := gMRIcroOverlay[kVOIOverlayNum].ScrnBuffer^[lInc]; |
|
548 |
end; |
|
549 |
Freemem(lQra); |
|
550 |
//BEGIN: CREATE 3D UNDO BUFFER
|
|
551 |
(*if (gDynSz > 1) and (gDynSz = gImageBackupSz) then begin
|
|
552 |
if (gUndoBufSz > 0) then freemem(gUndoBuffer);
|
|
553 |
gUndoBufSz := gDynSz;
|
|
554 |
getmem(gUndoBuffer,gDynSz);
|
|
555 |
Move(gImageBackupBuffer^,gUndoBuffer^,gImageBackupSz);
|
|
556 |
gSaveUndoBuf := true;
|
|
557 |
end; (**)
|
|
558 |
//END: CREATE 3D UNDO BUFFER
|
|
559 |
//BEGIN: mopping up: prepare data for viewing, report ROI change
|
|
560 |
Move(lROIBuf^,gMRIcroOverlay[kVOIOverlayNum].ScrnBuffer^,lVolSz); |
|
561 |
Freemem(lROIBuf); {} |
|
562 |
gBGImg.VOIchanged := true; |
|
563 |
//END: mopping up
|
|
564 |
ImgForm.RefreshImagesTimer.enabled := true; |
|
565 |
end; (**) |
|
566 |
||
567 |
procedure TAutoROIForm.AutoROIBtnClick(Sender: TObject); |
|
568 |
begin
|
|
569 |
AutoROIForm.ModalResult := mrOK; |
|
570 |
AutoROIForm.close; |
|
571 |
end; |
|
572 |
||
573 |
procedure TAutoROIForm.CancelBtnClick(Sender: TObject); |
|
574 |
begin
|
|
575 |
AutoROIForm.close; |
|
576 |
end; |
|
577 |
||
578 |
procedure TAutoROIForm.AutoROIchange(Sender: TObject); |
|
579 |
begin
|
|
580 |
if not AutoROIForm.visible then exit; |
|
581 |
Timer1.Enabled := true; |
|
582 |
end; |
|
583 |
||
584 |
procedure TAutoROIForm.Timer1Timer(Sender: TObject); |
|
585 |
begin
|
|
586 |
Timer1.Enabled := false; |
|
587 |
PreviewBtnClick(sender); |
|
588 |
end; |
|
589 |
||
590 |
procedure TAutoROIForm.FormDestroy(Sender: TObject); |
|
591 |
begin
|
|
592 |
//if gImageBackupSz <> 0 then Freemem(gImageBackupBuffer);
|
|
593 |
//gImageBackupSz := 0;
|
|
594 |
end; |
|
595 |
||
596 |
{$IFDEF FPC}
|
|
597 |
initialization
|
|
598 |
{$I autoroi.lrs}
|
|
599 |
{$ENDIF}
|
|
600 |
||
601 |
end. |