user11205892
user11205892

Reputation:

How to build a interface that displays an image bitmap and it's histogram in Racket?

I'm trying to build a GUI where I have the possibility to load an image (and draw it on a canvas) and to make a histogram out of it's pixels when I press a button (I want to display the histogram on the GUI too, but only for the "red" pixels for simplicity).

Initially I put a black background for the image canvas and afterwards the canvas is updated based on what image is loaded (using the load-button). In the same time I also save the pixels into input-buffer.

I couldn't find a way to embedded the plot onto the GUI, so for now I only plot it in the REPL.

I assume that the images are 256x256 (but anything else is fine as it can be modified afterwards). Here's the code of what I've tried so far:

#lang racket/gui
(require plot)

(define SIZE 256)

(define frame
  (new frame%
       [label "BMP"]
       [x 0] [y 0]
       [width 1000] [height 500]))

(define main-panel
  (new horizontal-panel%
       [parent frame]))

;---------------FOR THE IMAGE---------------------

(define input-panel
  (new vertical-panel%
       [parent main-panel]))

;in order to put a black fundal for the bitmap initially
(define input-bitmap (make-bitmap SIZE SIZE))
(define input-dc (send input-bitmap make-dc))
(send input-dc set-background (make-color 0 0 0))
(send input-dc clear)

(define input-canvas
  (new canvas%
       [parent input-panel]
       [paint-callback
        (λ (canvas dc)
          (send dc draw-bitmap input-bitmap 0 0))]))

;update canvas with the new loaded image
;also save the pixels in the input-buffer
(define input-buffer (make-bytes (* SIZE SIZE 4)))
(define load-button
  (new button%
       [parent input-panel]
       [label "Load file"]
       [callback
        (λ (button event)
          (define path (get-file))
          (when path
            (set! input-bitmap (read-bitmap path))
            (send input-canvas on-paint)
            (send input-bitmap get-argb-pixels 0 0 SIZE SIZE input-buffer)))]))

;---------------FOR THE HISTOGRAM---------------------

;builds a list of SIZE elements holding the frequency of each pixels
;e.g.: '((0 100) (1 50) ... (255 75)) ->this is given to the histogram
(define (get-red-pixels buffer)
  (define red-pixels
    (for/list ([i (in-range 1 (* SIZE SIZE 4) 4)])
      (bytes-ref buffer i)))
  (map list (build-list SIZE values)
       (for/list ([i SIZE])
         (length (filter (λ(x) (= i x)) red-pixels)))))

(define (plot-histogram)
  (plot (discrete-histogram (get-red-pixels input-buffer)
                            #:y-max 100
                            #:color 1 #:line-color 1)))

(define histogram-panel
  (new vertical-panel%
       [parent main-panel]))

(define refresh-button
  (new button%
       [parent histogram-panel]
       [label "Refresh"]
       [callback
        (λ (button event)
          ;not plotting when pressed..
          ;also I want the plot to be on the interface
          (plot-histogram))]))

(send frame show #t)

In order for the histogram to be plotted I must explicit call (plot-button) as the refresh-button doesn't plot it. Anyway I am trying to put the plot inside the interface not in a new window, however I'm stuck. Please let me know if anything is unclear.

Upvotes: 0

Views: 157

Answers (1)

Martin Půda
Martin Půda

Reputation: 7576

I made some changes in your old code and this seems to work:

(check load-button and get-red-pixels, rest of code should be the same)

#lang racket/gui
(require plot)

(define SIZE 256)

(define frame
  (new frame%
       [label "BMP"]
       [x 0] [y 0]
       [width 1000] [height 500]))

(define main-panel
  (new horizontal-panel%
       [parent frame]))

;---------------FOR THE IMAGE---------------------

(define input-panel
  (new vertical-panel%
       [parent main-panel]))

;in order to put a black fundal for the bitmap initially
(define input-bitmap (make-bitmap SIZE SIZE))
(define input-dc (send input-bitmap make-dc))
(send input-dc set-background (make-color 0 0 0))
(send input-dc clear)

(define input-canvas
  (new canvas%
       [parent input-panel]
       [paint-callback
        (λ (canvas dc)
          (send dc draw-bitmap input-bitmap 0 0))]))

;update canvas with the new loaded image
;also save the pixels in the input-buffer
(define input-buffer (make-bytes (* SIZE SIZE 4)))

(define load-button
  (new button%
       [parent input-panel]
       [label "Load file"]
       [callback
        (λ (button event)
          (let ((path (get-file)))
            (when path
              (set! input-bitmap (make-object bitmap% path))    
              (send input-canvas on-paint)
              (send input-bitmap get-argb-pixels 0 0 SIZE SIZE input-buffer)
              (plot/dc (discrete-histogram (get-red-pixels input-buffer)
                                           #:y-max SIZE
                                           #:color 1
                                           #:line-color 1)
                       (send histogram-canvas get-dc)
                       0 0
                       SIZE SIZE))))]))
 

;---------------FOR THE HISTOGRAM---------------------
 

;builds a list of SIZE elements holding the frequency of each pixels
;e.g.: '((0 100) (1 50) ... (255 75)) ->this is given to the histogram
(define (get-red-pixels buffer)
  (let ((red-pixels
         (for/list ([i (in-range 1 (* SIZE SIZE 4) 4)])
           (bytes-ref buffer i))))
    (map list
         (build-list SIZE values)
         (for/list ([i SIZE])
           (count (λ(x) (= i x)) red-pixels)))))

(define histogram-panel
  (new vertical-panel%
       [parent main-panel]))

(define histogram-canvas
  (new canvas%
       [parent histogram-panel]))

(send frame show #t)

Upvotes: 0

Related Questions