;+
; Project     : SOHO - CDS     
;                   
; Name        : CONNECT
;               
; Purpose     : Find objects of connected pixels in a binary image
;               
; Explanation : Counts the groups of 8-connected pixels in a
;               binary image.  Object pixels must have value 1, 
;               zero otherwise.
;               
; Use         : IDL> connect, array [, nobj, npix, /debug] 
;    
; Inputs      : array - binary image array
;               
; Opt. Inputs : None
;               
; Outputs     : None
;               
; Opt. Outputs: nobj - the number of objects found
;               npix - the total number of object pixels
;               
; Keywords    : DEBUG - prints out the details of the pixel runs
;                       associated with each object and displays
;                       the array with one pixel in each detected object 
;                       highlighted
;
; Calls       : None
;
; Common      : None
;               
; Restrictions: Uses an intermediate text file 'Connect_run_data',
;               user must have privilege to write this file.
;               
; Side effects: Sometimes has trouble if objects occur on the edges of the 
;               array
;               
; Category    : Image processing
;               
; Prev. Hist. : Based on algorithm by Thanisch et al (1984)
;               Image and Vision Computing, Vol 2, No 4, Pg 191
;
; Written     : C D Pike, RAL, 28-Jun-00
;               
; Modified    : 
;
; Version     : Version 1, 28-Jun-00
;-            


pro prep_image,arr,tr

s = size(arr)

nx = s(1)
ny = s(2)

openw,lun,'Connect_run_data',/get_lun


tr = 0

for i=0,ny-1 do begin
   x = arr(*,i)
   lhs = where(x-shift(x,1) eq 1,cl)
   rhs = where(x-shift(x,-1) eq 1,cr)
   n = n_elements(lhs)
   printf,lun,i+1,cl
   if cl gt 0 then begin
      for kk=0,cl-1 do begin
         printf,lun,lhs(kk),' ',rhs(kk)
      endfor
   endif
   tr = tr + cl
endfor

free_lun,lun

end


function touching, ptr1, ptr2

common alldata, run, obj, component, stack, $
                oldrhead, newrhead, newrun, oldrun, rear, $
                count, objavail, runavail, $
                mergecount, prevobj, objhead, objptr, $
                lun_data, temp2, temp3

common arrsize, totalcolumns, totalruns

if (run(ptr1).lhs le run(ptr2).rhs+1) and $
       (run(ptr1).rhs ge run(ptr2).lhs-1) then begin
   return,1
endif else return,0

end

pro getrun, ptr

common alldata, run, obj, component, stack, $
                oldrhead, newrhead, newrun, oldrun, rear, $
                count, objavail, runavail, $
                mergecount, prevobj, objhead, objptr, $
                lun_data, temp2, temp3

common arrsize, totalcolumns, totalruns

ptr = runavail
runavail = run(runavail).rnext
run(ptr).object = 0
run(ptr).rnext = 0

end

pro getobj, ptr

common alldata, run, obj, component, stack, $
                oldrhead, newrhead, newrun, oldrun, rear, $
                count, objavail, runavail, $
                mergecount, prevobj, objhead, objptr, $
                lun_data, temp2, temp3

common arrsize, totalcolumns, totalruns

ptr = objavail
objavail = obj(objavail).obnext
obj(ptr).first = 0
obj(ptr).last = 0
obj(ptr).newlabel = ptr
obj(ptr).obnext = 0

end


pro freeobj, ptr

common alldata, run, obj, component, stack, $
                oldrhead, newrhead, newrun, oldrun, rear, $
                count, objavail, runavail, $
                mergecount, prevobj, objhead, objptr, $
                lun_data, temp2, temp3

common arrsize, totalcolumns, totalruns

obj(ptr).obnext = objavail
objavail = ptr

end


pro getscan

common alldata, run, obj, component, stack, $
                oldrhead, newrhead, newrun, oldrun, rear, $
                count, objavail, runavail, $
                mergecount, prevobj, objhead, objptr, $
                lun_data, temp2, temp3

common arrsize, totalcolumns, totalruns

readf,lun_data,n,k
newrun = newrhead

;print,'n,k ',n,k
for i=1,k do begin
   getrun, temp
   run(newrun).rnext = temp
   newrun = temp
   readf, lun_data, a,b
   run(newrun).lhs = a
   run(newrun).rhs = b
   run(newrun).scan = n
endfor

run(newrun).rnext = rear

end


pro findtouches

common alldata, run, obj, component, stack, $
                oldrhead, newrhead, newrun, oldrun, rear, $
                count, objavail, runavail, $
                mergecount, prevobj, objhead, objptr, $
                lun_data, temp2, temp3

common arrsize, totalcolumns, totalruns

newrun = run(newrhead).rnext
mergecount = 0

while run(oldrhead).rnext ne rear do begin
   oldrun = run(oldrhead).rnext
   while run(newrun).rhs le run(oldrun).rhs do begin
      if touching(newrun,oldrun) then begin
         obj(run(oldrun).object).istouched = 1
         run(newrun).object = obj(run(oldrun).object).newlabel
      endif
      newrun = run(newrun).rnext
   endwhile
   
   if touching(newrun,oldrun) then begin
      obj(run(oldrun).object).istouched = 1
      run(newrun).object = obj(run(oldrun).object).newlabel
      if touching(newrun,run(oldrun).rnext) then begin
         mergecount = mergecount + 1
         stack(mergecount,1) = run(oldrun).object
         stack(mergecount,2) = run(run(oldrun).rnext).object
         updatelabels, run(oldrun).object, run(run(oldrun).rnext).object
      endif
   endif
   run(oldrhead).rnext = run(oldrun).rnext
   run(oldrun).rnext = obj(run(oldrun).object).last
   obj(run(oldrun).object).last = oldrun
  
   if obj(run(oldrun).object).first eq 0 then begin
      obj(run(oldrun).object).first = oldrun
   endif

endwhile

end


pro findcomponents

common alldata, run, obj, component, stack, $
                oldrhead, newrhead, newrun, oldrun, rear, $
                count, objavail, runavail, $
                mergecount, prevobj, objhead, objptr, $
                lun_data, temp2, temp3

common arrsize, totalcolumns, totalruns

prevobj = objhead

while obj(prevobj).obnext ne 0 do begin
   objptr = obj(prevobj).obnext
   obj(objptr).left = 0
  
   if obj(objptr).istouched then begin
      prevobj = objptr
      obj(objptr).istouched = 0
   endif else begin
      count = count + 1
      component(count) = obj(objptr).last
      obj(prevobj).obnext = obj(objptr).obnext
      freeobj, objptr
   endelse
endwhile

end

pro relabel

common alldata, run, obj, component, stack, $
                oldrhead, newrhead, newrun, oldrun, rear, $
                count, objavail, runavail, $
                mergecount, prevobj, objhead, objptr, $
                lun_data, temp2, temp3

common arrsize, totalcolumns, totalruns

for ptr = mergecount, 1, -1 do begin
   updatelabels, stack(ptr,1), stack(ptr,2)
endfor

newrun = run(newrhead).rnext

while newrun ne rear do begin
   if run(newrun).object eq 0 then begin
      getobj, objptr
      run(newrun).object = objptr
      obj(objptr).left = run(newrun).lhs
      obj(objptr).obnext = obj(objhead).obnext
      obj(objhead).obnext = objptr
   endif else begin
      run(newrun).object = obj(run(newrun).object).newlabel
      if obj(run(newrun).object).left eq 0 then begin
         obj(run(newrun).object).left = run(newrun).lhs
      endif
   endelse
 
   newrun = run(newrun).rnext
endwhile

end


pro mergestore

common alldata, run, obj, component, stack, $
                oldrhead, newrhead, newrun, oldrun, rear, $
                count, objavail, runavail, $
                mergecount, prevobj, objhead, objptr, $
                lun_data, temp2, temp3

common arrsize, totalcolumns, totalruns

objptr = obj(objhead).obnext
prevobj = objhead

while objptr ne 0 do begin
   obj(objptr).istouched = 0
   if objptr eq obj(objptr).newlabel then begin
      prevobj = objptr
   endif else begin
      run(obj(objptr).first).rnext = obj(obj(objptr).newlabel).last
      obj(obj(objptr).newlabel).last = obj(objptr).last
      obj(prevobj).obnext = obj(objptr).obnext
      freeobj,objptr
   endelse

   objptr = obj(prevobj).obnext
endwhile


end




pro updatelabels, obj1, obj2

common alldata, run, obj, component, stack, $
                oldrhead, newrhead, newrun, oldrun, rear, $
                count, objavail, runavail, $
                mergecount, prevobj, objhead, objptr, $
                lun_data, temp2, temp3

common arrsize, totalcolumns, totalruns

if obj(run(obj1).object).left lt obj(run(obj2).object).left then begin
   obj(obj2).newlabel = obj(obj1).newlabel
   obj(obj(obj2).newlabel).newlabel = obj(obj1).newlabel
endif else begin
   obj(obj1).newlabel = obj(obj2).newlabel
   obj(obj(obj1).newlabel).newlabel = obj(obj2).newlabel
endelse

end




pro initialise

common alldata, run, obj, component, stack, $
                oldrhead, newrhead, newrun, oldrun, rear, $
                count, objavail, runavail, $
                mergecount, prevobj, objhead, objptr, $
                lun_data, temp2, temp3

common arrsize, totalcolumns, totalruns

;
;  initialise
;
count = 0
objavail = 1 
runavail = 1

for i=totalruns+3,1,-1 do begin
   obj(i).obnext = i+1
   run(i).rnext = i+1
endfor

getobj, objhead
getrun, oldrhead
getrun, rear

run(rear).lhs = totalcolumns+2
run(rear).rhs = totalcolumns+2
run(oldrhead).rnext = rear

getrun, newrhead
run(newrhead).rnext = rear



end




;******************************
;  main routine
;******************************

pro connect, array, ncray, npix, debug=debug

common alldata, run, obj, component, stack, $
                oldrhead, newrhead, newrun, oldrun, rear, $
                count, objavail, runavail, $
                mergecount, prevobj, objhead, objptr, $
                lun_data, temp2, temp3
common arrsize, totalcolumns, totalruns

s = size(array)
totalcolumns = s(1)

if max(array) gt 1 or min(array) lt 0 then begin
   bell
   print,'Array must only have binary (0/1) values.'
   return
endif


prep_image,array,tr
totalruns = tr

openr,lun_data,'Connect_run_data',/get_lun


rec1 = {rec1a, lhs:0, rhs:0, object:0, scan:0, rnext:0}
run = replicate(rec1,totalruns+5)

rec2 = {rec2a, left:0, first:0, last:0, newlabel:0, obnext:0, istouched:0b}
obj = replicate(rec2,totalruns+5)

component = intarr(totalruns+1)
stack = intarr(totalcolumns+1,3)


initialise

; data file records

while not eof(lun_data) do begin 
   getscan
   findtouches
   findcomponents
   relabel
   mergestore
   run(oldrhead).rnext = run(newrhead).rnext
   run(newrhead).rnext = rear
endwhile

ncray = count
nn = where(array gt 0,npix)

if keyword_set(debug) then begin
   temp = array
   for i=1,count do begin
      print,'Runs in connected component',i
      kk = component(i)
      print,run(kk).lhs,run(kk).rhs,run(kk).scan-1
      temp(run(kk).lhs,run(kk).scan-1) = 2
      while run(kk).rnext gt 0 do begin
         kk = run(kk).rnext
         print,run(kk).lhs,run(kk).rhs,run(kk).scan-1
      endwhile
      print,'========='
   endfor
   print,' '
   print,'Number of objects: ',ncray
   print,'Number of affected pixels: ',npix
   print,' '
   plot_image,temp
endif

free_lun,lun_data



end

