如何在 Racket 中构建一个显示图像位图及其直方图的界面?

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

我正在尝试构建一个 GUI,我可以在其中加载图像(并将其绘制在 canvas 上)并在按下按钮时用它的像素制作直方图(我也想在 GUI 上显示直方图,但为了简单起见,只显示“红色”像素。

最初我为图像 canvas 设置了黑色背景,之后 canvas 会根据加载的图像(使用加载按钮)进行更新。同时我还将像素保存到输入缓冲区中。

我找不到将绘图嵌入 GUI 的方法,所以现在我只在 REPL 中绘制它。

我假设图像是 256x256(但其他任何都可以,因为之后可以修改)。这是我到目前为止尝试过的代码:

#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)

为了绘制直方图,我必须显式调用(绘图按钮),因为刷新按钮不会绘制它。无论如何,我试图将绘图放在界面内而不是新的 window 中,但我被卡住了。如果有任何不清楚的地方,请告诉我。

我对您的旧代码进行了一些更改,这似乎有效:

(检查 load-buttonget-red-pixels,其余代码应该相同)

#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)