X

NV5 Geospatial Blog

Each month, NV5 Geospatial posts new blog content across a variety of categories. Browse our latest posts below to learn about important geospatial information or use the search bar to find a specific topic or author. Stay informed of the latest blog posts, events, and technologies by joining our email list!



10035 Rate this article:
2.3

Hough Transform Demo GUI

Anonym

The following code demonstrates the Hough transform. It provides one interactive display for moving points in an image domain and a second display which displays the resulting Hough transform.

 

To run the application:

  1. Save the code that follows to a file named hough_demo.pro
  2. Open and compile the file in the IDLDE
  3. Execute the following at the IDL command prompt

hough_demo

 

The optional keyword NUM_POINTS may be used to set the number of points in the image-domain display.  This number must be in the range [2,100].

A brief description of the graphical user interface (Launched using the command hough_demo, NUM_POINTS=10).

The points in the image-domain (left) can be moved by clicking and dragging.  The image in the Hough domain (right) will update as a point moves.  When moving the mouse over the Hough domain a representative line for that point will appear in the image domain.  For instance, hovering over the point in the Hough domain where many of the waves intersect will draw a line that crosses several points in the image domain (Sorry you can’t see the cursor I the image)

Additionally, two line equations are displayed:

  1. Line Equation – The equation of the line between the first two points (red) in order from left to right in the image domain.
  2. Hough Line Equation – The equation of the line defined by a point in the Hough domain.  This will update as the cursor moves the mouse cursor in the Hough-domain window.

 

The source code:

;------------------------------------------------------------------------------

;+

; Main event handler

;

; :Params:

;   sEvent: in, required, type="structure"

;     An IDL widget event structure

;

;-

pro hough_demo_event, sEvent

  compile_opt idl2, logical_predicate

  widget_control, sEvent.top, GET_UVALUE=oHoughDemo

  oHoughDemo->Event, sEvent

end

 

;------------------------------------------------------------------------------

;+

; This method is called when the Hough demo GUI has been realized

;

; :Params:

;   tlb: in, required, type="long"

;     The widget ID of the top-level-base

;-

pro hough_demo_realize, tlb

  compile_opt idl2, logical_predicate

  widget_control, tlb, GET_UVALUE=oHoughDemo

  oHoughDemo->NotifyRealize

end

 

;------------------------------------------------------------------------------

;+

; Lifecycle method called when the object is destroyed vis OBJ_DESTROY.

;

;-

pro hough_demo::Cleanup

  compile_opt idl2, logical_predicate

  self->Destruct

end

 

;------------------------------------------------------------------------------

;+

; Constructs the GUI for the demo application

;-

pro hough_demo::ConstructGUI

  compile_opt idl2, logical_predicate

  self.tlb = widget_base(EVENT_PRO='hough_demo_event', MAP=0, $

    NOTIFY_REALIZE='hough_demo_realize', /ROW, TITLE='Hough Demo', $

    /TLB_KILL_REQUEST_EVENTS)

  wBase = widget_base(self.tlb, /COLUMN)

  wDraw = widget_draw(wBase, /BUTTON_EVENTS, GRAPHICS_LEVEL=2, /MOTION_EVENTS, $

    UNAME='draw_points', XSIZE=300, YSIZE=300)

  wLabel = widget_label(wBase, VALUE='Line Equation')

  wLabel = widget_label(wBase, /DYNAMIC_RESIZE, UNAME='label_line', VALUE='')

  wLabel = widget_label(wBase, VALUE='Hough Line Equation')

  wLabel = widget_label(wBase, /DYNAMIC_RESIZE, UNAME='label_hough', VALUE='')

  wBase = widget_base(self.tlb, /COLUMN)

  wDraw = widget_draw(wBase, GRAPHICS_LEVEL=2, /MOTION_EVENTS, $

    /TRACKING_EVENTS, UNAME='draw_hough', XSIZE=500, YSIZE=400)

  widget_control, self.tlb, /REALIZE, SET_UVALUE=self

end

 

;------------------------------------------------------------------------------

;+

; Cleans up any heap member variables

;-

pro hough_demo::Destruct

  compile_opt idl2, logical_predicate

  if widget_info(self.tlb, /VALID_ID) then begin

    void = self->GetObject('model/points', WINDOW=oWindow)

    obj_destroy, oWindow

    void = self->GetObject('model/image', /HOUGH, WINDOW=oWindow)

    obj_destroy, oWindow

    widget_control, self.tlb, /DESTROY

  endif

  ptr_free, [self.pRho, self.pTheta]

end

 

;------------------------------------------------------------------------------

;+

; Main entry point for widget events

;

; :Params:

;   sEvent: in, required, type="structure"

;     An IDL widget event structure

;-

pro hough_demo::Event, sEvent

  compile_opt idl2, logical_predicate

  case tag_names(sEvent, /STRUCTURE_NAME) of

    'WIDGET_DRAW': self->EventDraw, sEvent

    'WIDGET_KILL_REQUEST': self->Destruct

    'WIDGET_SLIDER': self->EventSlider, sEvent

    'WIDGET_TRACKING': self->EventTracking, sEvent

    else: help, sEvent

  endcase

end

 

;------------------------------------------------------------------------------

;+

; This method handles events from draw widgets.  It simply passes the event

; to the event handler for the specific draw widget.

;

; :Params:

;   sEvent: in, required, type="structure"

;     An IDL {WIDGET_DRAW} structure

;-

pro hough_demo::EventDraw, sEvent

  compile_opt idl2, logical_predicate

  case widget_info(sEvent.id, /UNAME) of

    'draw_hough': self->EventDrawHough, sEvent

    'draw_points': self->EventDrawPoints, sEvent

    else:

  endcase

end

 

;------------------------------------------------------------------------------

;+

; This method handles events from the draw widget displaying the Hough domain.

;

; :Params:

;   sEvent: in, required, type="structure"

;     An IDL {WIDGET_DRAW} structure

;-

pro hough_demo::EventDrawHough, sEvent

  compile_opt idl2, logical_predicate

  case sEvent.type of

    2: self->UpdateLinePlot, [sEvent.x,sEvent.y]

    else:

  endcase

end

 

;------------------------------------------------------------------------------

;+

; This method handles events from the draw widget displaying the image-domain

; points.

;

; :Params:

;   sEvent: in, required, type="structure"

;     An IDL {WIDGET_DRAW} structure

;-

pro hough_demo::EventDrawPoints, sEvent

  compile_opt idl2, logical_predicate

  widget_control, sEvent.id, GET_VALUE=oWindow

  case sEvent.type of

    0: begin

    ; Press event

      oWindow->GetProperty, GRAPHICS_TREE=oView

      oSelect = oWindow->Select(oVIew, [sEvent.x,sEvent.y])

      if obj_valid(oSelect[0]) then begin

        self.oSelect = oSelect[0]

        self.oSelect->GetProperty, DATA=pts

        void = min(abs(sEvent.x-pts[0,*]) + abs(sEvent.y-pts[1,*]), index)

        self.oSelect->SetProperty, UVALUE=index

      endif

    end

    1: begin

    ; Release event

      self.oSelect = obj_new()

      return

    end

    2: begin

    ; Motion event

      if obj_valid(self.oSelect) then begin

        widget_control, sEvent.id, GET_VALUE=oWindow

        oWindow->GetProperty, DIMENSIONS=dims

        if (sEvent.x LT 0) || (sEvent.y LT 0) $

        || (sEvent.x GT dims[0]) || (sEvent.y GT dims[1]) then begin

          return

        endif

        self.oSelect->GetProperty, DATA=pts, UVALUE=index

        pts[*,index] = [sEvent.x,sEvent.y]

        self.oSelect->SetProperty, DATA=pts

        self->UpdateHough

      endif

    end

    else: return

  endcase

  oWindow->Draw

end

 

;------------------------------------------------------------------------------

;+

; This method handles traking events

;

; :Params:

;   sEvent: in, required, type="structure"

;     An IDL {WIDGET_TRACKING} structure

;-

pro hough_demo::EventTracking, sEvent

  compile_opt idl2, logical_predicate

  case widget_info(sEvent.id, /UNAME) of

    'draw_hough': begin

      if ~sEvent.enter then begin

        oPlot = self->GetObject('model/plot', WINDOW=oWindow)

        oPlot->SetProperty, HIDE=1

        oWindow->Draw

        widget_control, self->GetWID('label_hough'), SET_VALUE=''

      endif

    end

    else:

  endcase

end

 

;------------------------------------------------------------------------------

;+

; Calculates the default MINX and MINY values used by the HOUGH algorithm

;

; :Keywords:

;   X: out, optional, type="float"

;     Set this keyword to a named variable to retrieve the default MINX value

;     used by HOUGH

;   Y: out, optional, type="float"

;     Set this keyword to a named variable to retrieve the default MINY value

;     used by HOUGH

;-

pro hough_demo::GetMinXY, $

  X=minX, Y=minY

  compile_opt idl2, logical_predicate

  void = self->GetObject('model/points', WINDOW=oWindow)

  oWindow->GetProperty, DIMENSIONS=dims

  minX = -(dims[0]-1)/2

  minY = -(dims[1]-1)/2

end

 

;------------------------------------------------------------------------------

;+

; This method is for accessing objects in the object-graphics tree

;

; :Returns:

;   A reference to the object with the spacified name.  If no object contains

;   a match a null object will be returned.

;

; :Params:

;   name: in, required, type="string"

;     The name of the object to be retrieved.

;    

; :Keywords:

;   HOUGH: in, optional, type="boolean"

;     Set this keyword to have the object retrieved from the Hough-domain

;     graphics window.  By default, the object will be retrieved from the

;     image-domian graphics window.

;   VIEW: out, optional, type="objref"

;     Set this keyword to a named variable to retrieve a reference to the

;     IDLGRVIEW object from the graphics window.

;   WINDOW: out, optional, type="objref"

;     Set this keyword to a named variable to retrieve a reference to the

;     IDLGRWINDOW object

;-

function hough_demo::GetObject, name, $

  HOUGH=hough, $

  VIEW=oView, $

  WINDOW=oWindow

  uName = keyword_set(hough) ? 'draw_hough' : 'draw_points'

  widget_control, self->GetWID(uName), GET_VALUE=oWindow

  oWindow->GetProperty, GRAPHICS_TREE=oView

  return, oView->GetByName(name)

end

 

;------------------------------------------------------------------------------

;+

; Calculates the slope and y-intercept of a line in either of the graphics

; windows

;

; :Returns:

;   A tow element, floating-point vector containing, in order, the slope and

;   y-intercept of the line

;  

; :Params:

;   xy: in, optional, type="float"

;     Set this to the (x,y)-postition of the cursor in the HOUGH window.  This

;     information is only used when the HOUGH keyword is set.

;    

; :Keywords:

;   HOUGH: in, optional, type="boolean"

;     Set this keyword to have the slope and intercept calculated from a point

;     in the Hough domain.  By default, the first two points in the image-

;     domain are used.

;-

function hough_demo::GetSlopeIntercept, xy, $

  HOUGH=hough

  mb = fltarr(2)

  if keyword_set(hough) then begin

    rho = (*self.pRho)[xy[1]]

    theta = (*self.pTheta)[xy[0]]

    self->GetMinXY, X=xMin, Y=yMin

    mb[0] = -1.0/tan(theta)

    mb[1] = (rho - xMin*cos(theta) - yMin*sin(theta)) / sin(theta)

  endif else begin

    oPoints = self->GetObject('model/points')

    oPoints->GetProperty, DATA=pts

    mb[0] = (pts[1,1]-pts[1,0])/(pts[0,1]-pts[0,0])

    mb[1] = pts[1,0] - mb[0]*pts[0,0]

  endelse

  return, mb

end

 

;------------------------------------------------------------------------------

;+

; This method the ID of the widget that uses the specified name as its UNAME.

;

; :Returns:

;   The ID of the widget using the specified UNAME.  If no widget is using the

;   UNAME then 0 is returned

;

; :Params:

;   name: in, required, type="string"

;       The UNAME of the widget whose ID is to be returned

;

; :Keywords:

;   PARENT: in, optional, type="integer"

;       The widget ID of the parent widget.  If not set, self.tlb will be used.

;-

function hough_demo::GetWID, name, $

  PARENT=wParent

  compile_opt idl2, logical_predicate

  if ~n_elements(wParent) then wParent = self.tlb

  if ~widget_info(wParent, /VALID_ID) then return, 0

  return, widget_info(wParent, FIND_BY_UNAME=name)

end

 

;------------------------------------------------------------------------------

;+

; Lifecycle method for initializing an instance of the object via OBJ_NEW()

;

; :Returns:

;   1 if the object is successfully initialized

;  

; :Params:

;   NUM_POINTS: in, optional, type="integer"

;     Sets the numner of points to be displayed in the image-domain display.

;     The default is 2.  If more than 2 points are displayed the first two will

;     be red and the rest will be blue.  This is because the slope/intercept

;     calculation for this window is done using the first two points.

;-

function hough_demo::Init, $

  NUM_POINTS=nPoints

  compile_opt idl2, logical_predicate

  self.pRho = ptr_new(/ALLOCATE_HEAP)

  self.pTheta = ptr_new(/ALLOCATE_HEAP)

  self.nPoints = n_elements(nPoints) ? nPoints : 2

  return, 1

end

 

;------------------------------------------------------------------------------

;+

; This method initializes the object graphics used by the Hough demo

;-

pro hough_demo::InitializeGraphics

  compile_opt idl2, logical_predicate

; Image domain (Points)

  widget_control, self->GetWID('draw_points'), GET_VALUE=oWindowPoint

  oWindowPoint->GetProperty, DIMENSIONS=dims

  oSymbol = obj_new('IDLgrSymbol', 1, THICK=2, SIZE=5)

  x = fix(randomu(s,self.nPoints)*dims[0])

  y = fix(randomu(s,self.nPoints)*dims[1])

  colors = bytarr(3,self.nPoints)

  colors[0,0:1] = 255

  if (self.nPoints GT 2) then colors[2,2:self.nPoints-1] = 255

  oPoints = obj_new('IDLgrPolyline', x, y, LINESTYLE=6, NAME='points', $

    SYMBOL=oSymbol, VERT_COLORS=colors)

  oPlot = obj_new('IDLgrPlot', HIDE=1, NAME='plot')

  oModelPoints = obj_new('IDLgrModel', NAME='model')

  oModelPoints->Add, [oPoints, oPlot]

  oViewPoints = obj_new('IDLgrView', COLOR=[255,255,255], VIEWPLANE_RECT=[0,0,dims])

  oViewPoints->Add, oModelPoints

  oWindowPoint->Setproperty, GRAPHICS_TREE=oViewPoints

  oWindowPoint->Draw

; Hough domain

  widget_control, self->GetWID('draw_hough'), GET_VALUE=oWindowHough

  oImage = obj_new('IDLgrImage', NAME='image')

  oModelHough = obj_new('IDLgrModel', NAME='model')

  oModelHough->Add, oImage

  oViewHough = obj_new('IDLgrView', COLOR=[255,255,255])

  oViewHough->Add, oModelHough

  oWindowHough->SetProperty, GRAPHICS_TREE=oViewHough

  oWindowHough->Draw

end

 

;------------------------------------------------------------------------------

;+

; This method is called when the GUI is realized.  It finishes up some

; initialization and starts XMANAGER.

;-

pro hough_demo::NotifyRealize

  compile_opt idl2, logical_predicate

  ss = get_screen_size()

  self->InitializeGraphics

  self->UpdateHough, /RESIZE_WINDOW

  wGeom = widget_info(self.tlb, /GEOMETRY)

  widget_control, self.tlb, XOFFSET=(ss[0]-wGeom.scr_xSize)/2, YOFFSET=(ss[1]-wGeom.scr_ySize)/2

  widget_control, self.tlb, MAP=1

  xmanager, 'hough_demo', self.tlb, /NO_BLOCK

end

 

;------------------------------------------------------------------------------

;+

; This method updates the image in the hough domain according to the points

; in the image doimain.

;

; :Keywords:

;   RESIZE_WINDOW: in, optional, type="boolean"

;     Set this keyword to have the Hough-domain display resized to the size

;     of the output from HOUGH.  This is only called the first time a Hough

;     image is calculated.

;-

pro hough_demo::UpdateHough, $

  RESIZE_WINDOW=resize

  compile_opt idl2, logical_predicate

  oPoints = self->GetObject('model/points', WINDOW=oWindowPoints)

  oWindowPoints->GetProperty, DIMENSIONS=dims

  oPoints->GetProperty, DATA=pts

  temp = make_array(dims, /FLOAT, VALUE=0.0)

  pts = transpose(pts)

  temp[pts[*,0],pts[*,1]] = 1.0

  self->GetMinXY, X=xMin, Y=yMin

  imgHough = hough(temporary(temp), RHO=rho, THETA=theta)

  *self.pRho = rho

  *self.pTheta = theta

  dimHough = size(imgHough, /DIMENSIONS)

  oImage = self->GetObject('model/image', /HOUGH, VIEW=oViewHough, WINDOW=oWindowHough)

  if keyword_set(resize) then begin

    widget_control, self->GetWID('draw_hough'), XSIZE=dimHough[0], YSIZE=dimHough[1]

    oViewHough->SetProperty, VIEWPLANE_RECT=[0,0,dimHough]

  endif

  oImage->SetProperty, DATA=255-bytscl(imgHough)

  oWindowHough->Draw

  self->UpdateLineEquation

end

 

;------------------------------------------------------------------------------

;+

; This method updates the Hough line-equation on the GUI.

;

; :Params:

;   xy: in, required, type="float"

;     A 2-element vector containing the xy-location in the Hough domain for

;     which the line will be calculated.

;-

pro hough_demo::UpdateHoughEquation, xy

  compile_opt idl2, logical_predicate

  mb = self->GetSlopeIntercept(xy, /HOUGH)

  str = 'y = '+string(mb[0], FORMAT='(f0.2)')+' * x + '+string(mb[1], FORMAT='(f0.2)')

  widget_control, self->GetWID('label_hough'), SET_VALUE=str

end

 

;------------------------------------------------------------------------------

;+

; This method updates the image-domain line equation on the GUI according to

; the first two points.

;-

pro hough_demo::UpdateLineEquation

  compile_opt idl2, logical_predicate

  mb = self->GetSlopeIntercept()

  str = 'y = '+string(mb[0], FORMAT='(f0.2)')+' * x + '+string(mb[1], FORMAT='(f0.2)')

  widget_control, self->GetWID('label_line'), SET_VALUE=str

end

 

;------------------------------------------------------------------------------

;+

; This method updates the line plot in the image-domain display using a point

; in the Hough domain.

;

; :Params:

;   xy: in, required, type="long"

;     A two-element vector containing the xy-location in the Hough domain for

;     which the line will be drawn in the image-domain display

;-

pro hough_demo::UpdateLinePlot, xy

  compile_opt idl2, logical_predicate

  oPlot = self->GetObject('model/plot', WINDOW=oWindow)

  oWindow->GetProperty, DIMENSIONS=dims

  rho = (*self.pRho)[xy[1]]

  theta = (*self.pTheta)[xy[0]]

  self->GetMinXY, X=xMin, Y=yMin

  m = -1.0/tan(theta)

  b = (rho - xMin*cos(theta) - yMin*sin(theta)) / sin(theta)

  x = findgen(dims[0])

  y = m*x+b

  oPlot->SetProperty, DATAX=x, DATAY=y, HIDE=0

  oWindow->Draw

  self->UpdateHoughEquation, xy

end

;------------------------------------------------------------------------------

;+

; Class structure definition

;

; :Fields:

;   nPoints: The number of points in the image-domain display

;   oSelect: A reference to the selected graphics object.  This is used for

;     moving points in the image-domain display

;   pRho: Holds the rho values for the hough transform

;   pTheta: Holds the theta values for the hough transform

;   tlb: The widget ID of the top-level-base of the GUI

;-

pro hough_demo__define

  compile_opt idl2, logical_predicate

  void = {hough_demo     $

 

    ,nPoints : 0         $

    ,oSelect : obj_new() $

    ,pRho    : ptr_new() $

    ,pTheta  : ptr_new() $   

    ,tlb     : 0L        $

 

    }

end

 

;------------------------------------------------------------------------------

;+

; This routine starts the Hough demo

;

; :Keywords:

;   NUM_POINTS: in, optional, type="integer"

;     Sets the numner of points to be displayed in the image-domain display.

;     The allowed range is [2,100].  The default is 2.  If more than 2 points

;     are displayed the first two will be red and the rest will be blue.  This

;     is because the slope/intercept calculation for this window is done using

;     the first two points.

;-

pro hough_demo, NUM_POINTS=nPoints

  compile_opt idl2, logical_predicate

  if n_elements(nPoints) && ((nPoints LT 2) || (nPoints GT 100)) then begin

    void = dialog_message('The number ofpoints must be in [2,100]', /ERROR)

    return

  endif

  oHoughDemo = obj_new('hough_demo', NUM_POINTS=nPoints)

  oHoughDemo->ConstructGUI

end

 

Please login or register to post comments.
«April 2025»
SunMonTueWedThuFriSat
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910
Digital Number, Radiance, and...

Number of views (168830)

Push Broom and Whisk Broom Sensors

Number of views (149899)

The Many Band Combinations of Landsat 8

Number of views (118043)

What Type of Loop Should I Use?

Number of views (78297)

Mapping Coastal Erosion Using LiDAR

Number of views (58296)