image.scm 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. ;;; guile-openai --- An OpenAI API client for Guile
  2. ;;; Copyright © 2023 Andrew Whatson <whatson@tailcall.au>
  3. ;;;
  4. ;;; This file is part of guile-openai.
  5. ;;;
  6. ;;; guile-openai is free software: you can redistribute it and/or modify
  7. ;;; it under the terms of the GNU Affero General Public License as
  8. ;;; published by the Free Software Foundation, either version 3 of the
  9. ;;; License, or (at your option) any later version.
  10. ;;;
  11. ;;; guile-openai is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. ;;; Affero General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU Affero General Public
  17. ;;; License along with guile-openai. If not, see
  18. ;;; <https://www.gnu.org/licenses/>.
  19. (define-module (openai image)
  20. #:use-module (openai api image)
  21. #:use-module (openai client)
  22. #:use-module (openai utils magick)
  23. #:use-module (ice-9 binary-ports)
  24. #:use-module (ice-9 match)
  25. #:use-module (srfi srfi-8)
  26. #:use-module (srfi srfi-9)
  27. #:use-module (srfi srfi-9 gnu)
  28. #:export (openai-default-image-size
  29. openai-default-image-format
  30. image?
  31. image-url
  32. image-file
  33. openai-image
  34. openai-image-edit
  35. openai-image-variation))
  36. (define-once openai-default-image-size
  37. (make-parameter 512))
  38. (define-once openai-default-image-format
  39. (make-parameter 'b64))
  40. (define-record-type <Image>
  41. (%make-image url file)
  42. image?
  43. (url image-url)
  44. (file image-file))
  45. (define (write-image-file img)
  46. (let* ((raw-data (fetch-image-data img))
  47. (png-rgba (magick-convert-image raw-data
  48. #:format "PNG"
  49. #:alpha-channel 'Activate)))
  50. (call-with-port (mkstemp "/tmp/guile-openai-XXXXXX")
  51. (lambda (port)
  52. (put-bytevector port png-rgba)
  53. (port-filename port)))))
  54. (define (write-transparent-mask size)
  55. (receive (width height)
  56. (cond ((string=? size "256x256")
  57. (values 256 256))
  58. ((string=? size "512x512")
  59. (values 512 512))
  60. ((string=? size "1024x1024")))
  61. (let ((png-rgba (magick-create-image #:width width
  62. #:height height
  63. #:format "PNG"
  64. #:alpha-channel 'Activate)))
  65. (call-with-port (mkstemp "/tmp/guile-openai-XXXXXX")
  66. (lambda (port)
  67. (put-bytevector port png-rgba)
  68. (port-filename port))))))
  69. (define (make-image img)
  70. (let ((url (image-data-url img)))
  71. (%make-image (if (unspecified? url) #f url)
  72. (write-image-file img))))
  73. (define (print-image img port)
  74. (format port "#<Image: ~a>" (image-file img)))
  75. (set-record-type-printer! <Image> print-image)
  76. (define parse-image-size
  77. (match-lambda
  78. ((? unspecified? value) value)
  79. ((? string? value) value)
  80. ((or 256 '256x256) "256x256")
  81. ((or 512 '512x512) "512x512")
  82. ((or 1024 '1024x1024) "1024x1024")))
  83. (define parse-image-format
  84. (match-lambda
  85. ((? unspecified? value) value)
  86. ((? string? value) value)
  87. ('url "url")
  88. ('b64 "b64_json")))
  89. (define* (openai-image prompt #:key
  90. (n *unspecified*)
  91. (size (openai-default-image-size))
  92. (format (openai-default-image-format))
  93. (user (openai-default-user)))
  94. "Send an image generation request. Returns an *image* record.
  95. The PROMPT must be a string.
  96. The keyword arguments correspond to the request parameters described in
  97. the image generation request documentation:
  98. #:n - The number of images to generate, returned as multiple values.
  99. #:size - A number specifying the generated image size, either 256, 512,
  100. or 1024. The symbols `256x256', `512x512' and `1024x1024' are also
  101. supported.
  102. #:format - How the image data will be returned, either `url' (hosted on
  103. a cloud server), or `b64' (embedded in the response message, the
  104. default). In either case, the image data will be fetched and stored
  105. locally for display.
  106. #:user - An optional username to associate with this request."
  107. (let* ((size (parse-image-size size))
  108. (format (parse-image-format format))
  109. (request (make-image-request prompt n size format user))
  110. (response (send-image-request request)))
  111. (apply values
  112. (map make-image (image-response-data response)))))
  113. (define* (openai-image-edit image prompt #:key
  114. (mask *unspecified*)
  115. (n *unspecified*)
  116. (size (openai-default-image-size))
  117. (format (openai-default-image-format))
  118. (user (openai-default-user)))
  119. "Send an image edit request. Returns an *image* record.
  120. The IMAGE can be an *image* record or a string path to the image file to
  121. be varied. The API requires the image to be a valid PNG file, less than
  122. 4MB, and square.
  123. The PROMPT must be a string describing the changes to be made to the
  124. image.
  125. The keyword arguments correspond to the request parameters described in
  126. the image variation request documentation:
  127. #:mask - An *image* record or a string path to an image file to use as
  128. the edit mask. The model will only edit pixels which are fully
  129. transparent (ie. alpha 0) in the mask image. The mask must have the
  130. same dimensions as IMAGE.
  131. #:n - The number of images to generate, returned as multiple values.
  132. #:size - A number specifying the generated image size, either 256, 512,
  133. or 1024. The symbols `256x256', `512x512' and `1024x1024' are also
  134. supported.
  135. #:format - How the image data will be returned, either `url' (hosted on
  136. a cloud server), or `b64' (embedded in the response message, the
  137. default). In either case, the image data will be fetched and stored
  138. locally for display.
  139. #:user - An optional username to associate with this request."
  140. (let* ((image (if (image? image) (image-file image) image))
  141. (mask (if (image? mask) (image-file mask) mask))
  142. (size (parse-image-size size))
  143. (mask (if (unspecified? mask) (write-transparent-mask size) mask))
  144. (format (parse-image-format format))
  145. (request (make-image-edit-request image mask prompt
  146. n size format user))
  147. (response (send-image-edit-request request)))
  148. (apply values
  149. (map make-image (image-response-data response)))))
  150. (define* (openai-image-variation image #:key
  151. (n *unspecified*)
  152. (size (openai-default-image-size))
  153. (format (openai-default-image-format))
  154. (user (openai-default-user)))
  155. "Send an image variation request. Returns an *image* record.
  156. The IMAGE can be an *image* record or a string path to the image file to
  157. be varied. The API requires the image to be a valid PNG file, less than
  158. 4MB, and square.
  159. The keyword arguments correspond to the request parameters described in
  160. the image variation request documentation:
  161. #:n - The number of images to generate, returned as multiple values.
  162. #:size - A number specifying the generated image size, either 256, 512,
  163. or 1024. The symbols `256x256', `512x512' and `1024x1024' are also
  164. supported.
  165. #:format - How the image data will be returned, either `url' (hosted on
  166. a cloud server), or `b64' (embedded in the response message, the
  167. default). In either case, the image data will be fetched and stored
  168. locally for display.
  169. #:user - An optional username to associate with this request."
  170. (let* ((image (if (image? image) (image-file image) image))
  171. (size (parse-image-size size))
  172. (format (parse-image-format format))
  173. (request (make-image-variation-request image n size format user))
  174. (response (send-image-variation-request request)))
  175. (apply values
  176. (map make-image (image-response-data response)))))