Is there a good Racket DSL alternative to Image Magick?

147 views
Skip to first unread message

Robert Haisfield

unread,
May 11, 2021, 9:26:01 AM5/11/21
to Racket Users
I have to do a bunch of .jpg and .png image resizings and was looking for a programmatic way to do it. Is their a Racket DSL for this?

Sage Gerard

unread,
May 11, 2021, 9:30:26 AM5/11/21
to racket...@googlegroups.com

I don't know of one off hand, but I believe RacketCon 2018 (?) included a presenter that showed a PostScript-like DSL for designers and artists.  If pict not cover your needs, maybe dig into the presentations?

Failing that, can you show what you'd hope the syntax would look like? That would probably help point you in the right direction,

On 5/11/21 9:26 AM, Robert Haisfield wrote:
I have to do a bunch of .jpg and .png image resizings and was looking for a programmatic way to do it. Is their a Racket DSL for this? --
You received this message because you are subscribed to the Google Groups "Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to racket-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/0d576c32-7d4d-4944-9cbc-c12f04406fccn%40googlegroups.com.
--
~slg

Bruce O'Neel

unread,
May 11, 2021, 10:20:19 AM5/11/21
to Sage Gerard, racket...@googlegroups.com
This might be it.

-- ~slg --
You received this message because you are subscribed to the Google Groups "Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to racket-users...@googlegroups.com.

Sage Gerard

unread,
May 11, 2021, 11:03:35 AM5/11/21
to racket...@googlegroups.com

I hope that has what Robert is looking for, but I don't recognize that speech. In fact, I have a false memory, because I'm not finding the speech I'm looking for on https://con.racket-lang.org/2018/#speakers

Robert Haisfield

unread,
May 11, 2021, 4:03:33 PM5/11/21
to Racket Users
Hmm does the video language work for image files? If so, then I think it might work.

Robert Haisfield

unread,
May 11, 2021, 4:09:07 PM5/11/21
to Racket Users
Alternatively, does the normal images function for Racket work? When I was looking through the documentation I saw a lot about creating shapes but not about using image files.

John Clements

unread,
May 11, 2021, 4:15:14 PM5/11/21
to Robert Haisfield, Racket Users
Racket has the ability to read a variety of different image files. I would go first to 2htdp/image’s “bitmap/file” to read images. “save-image” can write images (but only as png files). I believe there are also an array of lower-level image manipulation functions that are likely to have a less friendly interface :).

Apologies in advance for any misleading information I might be providing….

John
> To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/b6a89a78-c92c-41c1-960a-49d64d8a5366n%40googlegroups.com.

Jens Axel Søgaard

unread,
May 11, 2021, 5:06:20 PM5/11/21
to John Clements, Robert Haisfield, Racket Users
Hi Robert,

There are some bindings for Magick in the example folder for the FFI library.

https://github.com/racket/racket/tree/master/pkgs/racket-doc/ffi/examples

The bindings are in magic.rkt and examples of use are in use-magick.rkt (see the bottom).

/Jens Axel




--
--
Jens Axel Søgaard

Robert Haisfield

unread,
May 11, 2021, 6:42:43 PM5/11/21
to Racket Users
Okay, so I figured out how to do what I want on individual photos using bitmap/file and 2htdp/image, but I'm trying to find a more programmatic way to do it. Basically, I'd love to create a data structure that feeds into the file path, width, and height into the image-resizer function. Even better if I could point the function at a directory, it finds all of the images in it, and creates the data structure for me. Any ideas?

No, I'm not trying to just resize photos (this would be much easier if that's all I had to do), the thing I'm working with requires me to add whitespace to the sides of the images to have them display properly.

CleanShot 2021-05-11 at 16.31.23@2x.png

Robert Haisfield

unread,
May 11, 2021, 8:27:18 PM5/11/21
to Racket Users
More specifically, I'm trying to do what I talk about here. Can I do this in Racket? https://twitter.com/RobertHaisfield/status/1392272268022095872

Daniel Prager

unread,
May 11, 2021, 8:57:03 PM5/11/21
to Robert Haisfield, Racket Users
Hi Robert


Even better if I could point the function at a directory, it finds all of the images in it, and creates the data structure for me. Any ideas?

You can get the files in a directory using directory-list and filter for the image files using a regex.

(for ([f (directory-list "/some/path")] #:when (regexp-match? "\\.png$" f))
     (displayln f))


Dan

Martin DeMello

unread,
May 11, 2021, 9:31:15 PM5/11/21
to Robert Haisfield, Racket Users
So to clarify, you want something like the following (pseudocode)?

(define (process-image transform image-file-data)
   (let* ([path (get-full-path image-file-data)]
          [image (bitmap/file path)]
          [new-image (transform image)])
    (save-image new-image path))

(define (process-file path)
  (let ([image-file-data (extract-image-metadata path)])
     (process-image resize image-file-data)))

(map process-file (find-all-images directory))

One question - where do the new width and height of each image come from, if you are reading the list of filenames from a directory?

martin
             

Robert Haisfield

unread,
May 12, 2021, 9:06:02 AM5/12/21
to Racket Users
Dan, that's awesome. Thank you.

Martin, I would love some way to extract the image metadata from each of the files outputted by Dan's function. All my function does is take the current width and height and apply some math to return some new values, so right now I'm just manually going into finder, looking at the images width and height, and plugging those values into my function. 

kamist...@gmail.com

unread,
May 12, 2021, 7:04:41 PM5/12/21
to Racket Users
Personally I use pict and racket/draw instead of htdp/image, because save-image is png only.
Pict to transform or load the image, racket/draw's bitmap% to save and/or load the image
(sidenote: in general I find pict more pleasant to work with, but that may be subjective):

(require pict
         racket/draw)

;; this is a rough sketch of the image functions I use

;; make-object 2nd variant: https://docs.racket-lang.org/draw/bitmap_.html?q=bitmap%25#%28constructor._%28%28lib._racket%2Fdraw..rkt%29._bitmap~25%29%29
(define b (make-object bitmap% filename-or-port image-type-see-docs #f #t)) ;; background color #f, complain on failure #t
(define p (bitmap b)) ;; use bitmap% as pict
;; use pict functions to transform pict
(define p2 (scale-to-fit p ...))
;; other pict transforms
(define p3 (inset p2 30 0))
;; saving the transformed file
(send (pict->bitmap p3) save-file out 'png) ;; other file types possible

Robert Haisfield

unread,
May 12, 2021, 8:22:04 PM5/12/21
to Racket Users
Daniel, that's awesome. How would I filter down this list according to the regex?

(define list-of-files (map path->string (directory-list starting-path)))

Ben Greenman

unread,
May 12, 2021, 9:21:42 PM5/12/21
to Racket Users
On 5/12/21, Robert Haisfield <roberthh...@gmail.com> wrote:
> Daniel, that's awesome. How would I filter down this list according to the
> regex?
>
> (define list-of-files (map path->string (directory-list starting-path)))

You can wrap it in a filter:

(define list-of-files (filter (lambda (str) (regexp-match? "\\.png$"
str)) (map path->string (directory-list starting-path))))


You might also like the glob package:

(require file/glob)
(define list-of-files (map path->string (glob (build-path
starting-path "*.png"))))

kamist...@gmail.com

unread,
May 12, 2021, 9:28:45 PM5/12/21
to Racket Users
p.s: scale-to-fit might be interesting for you and if you don't care about extra options (for background/alpha etc.) you can construct the pict with (bitmap path) directly instead of (bitmap (make-object bitmap% ...))
p.p.s: I think there was also a way to convert/use images within picts or the other way around but I currently can't find it...  ...I did find it: pict/convert
a little bit more example code options / some might be useful for you or other people (I partly write this as a note to myself)

#lang racket

(require (only-in 2htdp/image circle)
         (except-in pict circle)
         pict/convert)

(module+ main
  (define c (circle 708 "solid" "blue"))
  (displayln "pict-convertible:")
  (pict-convertible? c)
  (define p (pict-convert c))
  (define p2 (frame (scale-to-fit p 1440 (pict-height p) #:mode 'inset)))
  (send (pict->bitmap p2) save-file "example.png" 'png)
  (send (pict->bitmap p) save-file "test.png" 'png)
 
  (make-directory* "./resized")
  (define resize (resizer "." "./resized" 1440))
  (resize 708 1100 "test.png"))

(define ((resizer source target max-width) width height filename)
  (define p (bitmap (build-path source filename)))
  ;; (pict-width p) (pict-height p) would give the dimmensions of the incoming image
  ;; maybe that could be used instead of the hardcoded arguments, if that is possible for the usecase
 
  ;; this is a pict based variant with scaling (maybe you don't need scaling)
  ;; maybe you need something a little bit different
  ;; maybe scale-to-fit with certain parameters may be enough, or you can keep using your 2htdp/image
  ;; version and you could convert it to a pict with pict-convert if you want to use pict or bitmap% functionality
  (define p2
    (cc-superimpose (filled-rectangle max-width height #:color "white" #:draw-border? #f)
                    (scale-to-fit p (- max-width width) height #:mode 'inset)))
  (send (pict->bitmap p2)
        save-file (build-path target filename) 'png))

;; -------------------------------------------------------------
;; everything below this is completely optional and I only do it because I wanted to try it
;; -------------------------------------------------------------
;; because this contract is defined on the module boundary it won't work within this module
;; [define/contract could be used to define it on the function boundary]
;; we could use a ->i contract to specify that width has to be <= max-width
(require racket/contract)
(define pathish/c (or/c path-string? path-for-some-system? 'up 'same))
(provide
 (contract-out
  [resizer (->i ([source pathish/c]
                 [target pathish/c]
                 [max-width exact-nonnegative-integer?])
                [result (max-width) (->i ([width (and/c exact-nonnegative-integer? (<=/c max-width))]
                                          [height exact-nonnegative-integer?]
                                          [filename string?])
                                         [result () boolean?])])]))

(module* contract-test racket
  (require (submod "..")) ;; require enclosing module

  ;; testing the contract from a submodule similar to requiring from a different file
  (define resize (resizer "." "./resized" 1440))
  (resize 708 1100 "test.png") ;; works
  (resize 1708 1100 "test.png")) ;; contract error

;; to execute contract-test submodule use this in drracket-repl:
;; (require (submod "." contract-test))

David Storrs

unread,
May 13, 2021, 12:15:23 PM5/13/21
to Ben Greenman, Racket Users
On Wed, May 12, 2021 at 9:21 PM Ben Greenman <benjamin...@gmail.com> wrote:
On 5/12/21, Robert Haisfield <roberthh...@gmail.com> wrote:
> Daniel, that's awesome. How would I filter down this list according to the
> regex?
>
> (define list-of-files (map path->string (directory-list starting-path)))

You can wrap it in a filter:

  (define list-of-files (filter (lambda (str) (regexp-match? "\\.png$"
str)) (map path->string (directory-list starting-path))))

Alternatively, if you would like to keep them as paths instead of strings then you can do this:

(filter (compose1 (curry regexp-match? "\\.png$") path->string) (directory-list "."))


Also, if you're going to be doing a lot of work with regexen, take a look at the at-exp reader so that you don't have to do so much backwhacking (obviously it's not important in this case where there's only one character to escape, but it gets helpful as you do more):

#lang at-exp racket
(filter (compose1 (curry regexp-match? @~a{\.png}) path->string) (directory-list "."))

James Platt

unread,
May 21, 2021, 8:03:06 AM5/21/21
to Racket Users
I was there and, as I remember it, there was a presentation on something else where this was mentioned. IIRC, the topic was Language Oriented Programming and there was a discussion about how much complexity is okay when you are programmatically generating DSL code. The presenter mentioned PostScript as an example of a DSL where very complex code is often machine generated. Then there was more discussion about postscript and related things you can do with Racket. Maybe that's what you were thinking about.

James
> To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/c911fc49-034e-7159-8a14-7fc5466122b0%40sagegerard.com.

Reply all
Reply to author
Forward
0 new messages