image.scm 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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. #:background-color "white"
  66. #:background-alpha 0.0)))
  67. (call-with-port (mkstemp "/tmp/guile-openai-XXXXXX")
  68. (lambda (port)
  69. (put-bytevector port png-rgba)
  70. (port-filename port))))))
  71. (define (make-image img)
  72. (let ((url (image-data-url img)))
  73. (%make-image (if (unspecified? url) #f url)
  74. (write-image-file img))))
  75. (define (print-image img port)
  76. (format port "#<Image: ~a>" (image-file img)))
  77. (set-record-type-printer! <Image> print-image)
  78. (define parse-image-size
  79. (match-lambda
  80. ((? unspecified? value) value)
  81. ((? string? value) value)
  82. ((or 256 '256x256) "256x256")
  83. ((or 512 '512x512) "512x512")
  84. ((or 1024 '1024x1024) "1024x1024")))
  85. (define parse-image-format
  86. (match-lambda
  87. ((? unspecified? value) value)
  88. ((? string? value) value)
  89. ('url "url")
  90. ('b64 "b64_json")))
  91. (define* (openai-image prompt #:key
  92. (n *unspecified*)
  93. (size (openai-default-image-size))
  94. (format (openai-default-image-format))
  95. (user (openai-default-user)))
  96. "Send an image generation request. Returns an image record.
  97. The PROMPT must be a string.
  98. The keyword arguments correspond to the request parameters described
  99. in the image generation request documentation:
  100. #:n - The number of images to generate, returned as multiple values.
  101. #:size - A number specifying the generated image size, either 256,
  102. 512, or 1024. The symbols `256x256', `512x512' and `1024x1024' are
  103. also supported.
  104. #:format - How the image data will be returned, either `url' (hosted
  105. on a cloud server), or `b64' (embedded in the response message, the
  106. default). In either case, the image data will be fetched and stored
  107. locally for display.
  108. #:user - An optional username to associate with this request."
  109. (let* ((size (parse-image-size size))
  110. (format (parse-image-format format))
  111. (request (make-image-request prompt n size format user))
  112. (response (send-image-request request)))
  113. (apply values
  114. (map make-image (image-response-data response)))))
  115. (define* (openai-image-edit image prompt #:key
  116. (mask *unspecified*)
  117. (n *unspecified*)
  118. (size (openai-default-image-size))
  119. (format (openai-default-image-format))
  120. (user (openai-default-user)))
  121. "Send an image edit request. Returns an image record.
  122. The IMAGE can be an image record or a string path to the image file to
  123. be varied. The API requires the image to be a valid PNG file, less
  124. than 4MB, and square.
  125. The PROMPT must be a string describing the changes to be made to the
  126. image.
  127. The keyword arguments correspond to the request parameters described
  128. in the image edit request documentation:
  129. #:mask - An image record or a string path to an image file to use as
  130. the edit mask. The model will only edit pixels which are fully
  131. transparent (ie. alpha 0) in the mask image. The mask must have the
  132. same dimensions as IMAGE. The default is fully opaque, so no pixels
  133. will be edited and a copy of the original image will be returned.
  134. #:n - The number of images to generate, returned as multiple values.
  135. #:size - A number specifying the generated image size, either 256,
  136. 512, or 1024. The symbols `256x256', `512x512' and `1024x1024' are
  137. also supported.
  138. #:format - How the image data will be returned, either `url' (hosted
  139. on a cloud server), or `b64' (embedded in the response message, the
  140. default). In either case, the image data will be fetched and stored
  141. locally for display.
  142. #:user - An optional username to associate with this request."
  143. (let* ((image (if (image? image) (image-file image) image))
  144. (mask (if (image? mask) (image-file mask) mask))
  145. (size (parse-image-size size))
  146. (format (parse-image-format format))
  147. (request (make-image-edit-request image mask prompt
  148. n size format user))
  149. (response (send-image-edit-request request)))
  150. (apply values
  151. (map make-image (image-response-data response)))))
  152. (define* (openai-image-variation image #:key
  153. (n *unspecified*)
  154. (size (openai-default-image-size))
  155. (format (openai-default-image-format))
  156. (user (openai-default-user)))
  157. "Send an image variation request. Returns an image record.
  158. The IMAGE can be an image record or a string path to the image file to
  159. be varied. The API requires the image to be a valid PNG file, less
  160. than 4MB, and square.
  161. The keyword arguments correspond to the request parameters described
  162. in the image variation request documentation:
  163. #:n - The number of images to generate, returned as multiple values.
  164. #:size - A number specifying the generated image size, either 256,
  165. 512, or 1024. The symbols `256x256', `512x512' and `1024x1024' are
  166. also supported.
  167. #:format - How the image data will be returned, either `url' (hosted
  168. on a cloud server), or `b64' (embedded in the response message, the
  169. default). In either case, the image data will be fetched and stored
  170. locally for display.
  171. #:user - An optional username to associate with this request."
  172. (let* ((image (if (image? image) (image-file image) image))
  173. (size (parse-image-size size))
  174. (format (parse-image-format format))
  175. (request (make-image-variation-request image n size format user))
  176. (response (send-image-variation-request request)))
  177. (apply values
  178. (map make-image (image-response-data response)))))