Add image generation, editing, and variations (#63)

https://beta.openai.com/docs/api-reference/images
This commit is contained in:
Theo Kanning 2023-01-03 16:33:52 -06:00 committed by GitHub
parent 3f8f02f104
commit e68a098b87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 333 additions and 1 deletions

View File

@ -6,7 +6,7 @@ import lombok.Data;
import java.util.List; import java.util.List;
/** /**
* An object describing an fine-tuned model. Returned by multiple fine-tune requests. * An object describing a fine-tuned model. Returned by multiple fine-tune requests.
* *
* https://beta.openai.com/docs/api-reference/fine-tunes * https://beta.openai.com/docs/api-reference/fine-tunes
*/ */

View File

@ -0,0 +1,42 @@
package com.theokanning.openai.image;
import lombok.*;
/**
* A request for OpenAi to edit an image based on a prompt
* All fields except prompt are optional
*
* https://beta.openai.com/docs/api-reference/images/create-edit
*/
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class CreateImageEditRequest {
/**
* A text description of the desired image(s). The maximum length in 1000 characters.
*/
@NonNull
String prompt;
/**
* The number of images to generate. Must be between 1 and 10. Defaults to 1.
*/
Integer n;
/**
* The size of the generated images. Must be one of "256x256", "512x512", or "1024x1024". Defaults to "1024x1024".
*/
String size;
/**
* The format in which the generated images are returned. Must be one of url or b64_json. Defaults to url.
*/
String responseFormat;
/**
* A unique identifier representing your end-user, which will help OpenAI to monitor and detect abuse.
*/
String user;
}

View File

@ -0,0 +1,42 @@
package com.theokanning.openai.image;
import lombok.*;
/**
* A request for OpenAi to create an image based on a prompt
* All fields except prompt are optional
*
* https://beta.openai.com/docs/api-reference/images/create
*/
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class CreateImageRequest {
/**
* A text description of the desired image(s). The maximum length in 1000 characters.
*/
@NonNull
String prompt;
/**
* The number of images to generate. Must be between 1 and 10. Defaults to 1.
*/
Integer n;
/**
* The size of the generated images. Must be one of "256x256", "512x512", or "1024x1024". Defaults to "1024x1024".
*/
String size;
/**
* The format in which the generated images are returned. Must be one of url or b64_json. Defaults to url.
*/
String responseFormat;
/**
* A unique identifier representing your end-user, which will help OpenAI to monitor and detect abuse.
*/
String user;
}

View File

@ -0,0 +1,36 @@
package com.theokanning.openai.image;
import lombok.*;
/**
* A request for OpenAi to create a variation of an image
* All fields are optional
*
* https://beta.openai.com/docs/api-reference/images/create-variation
*/
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class CreateImageVariationRequest {
/**
* The number of images to generate. Must be between 1 and 10. Defaults to 1.
*/
Integer n;
/**
* The size of the generated images. Must be one of "256x256", "512x512", or "1024x1024". Defaults to "1024x1024".
*/
String size;
/**
* The format in which the generated images are returned. Must be one of url or b64_json. Defaults to url.
*/
String responseFormat;
/**
* A unique identifier representing your end-user, which will help OpenAI to monitor and detect abuse.
*/
String user;
}

View File

@ -0,0 +1,24 @@
package com.theokanning.openai.image;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* An object containing either a URL or a base 64 encoded image.
*
* https://beta.openai.com/docs/api-reference/images
*/
@Data
public class Image {
/**
* The URL where the image can be accessed.
*/
String url;
/**
* Base64 encoded image string.
*/
@JsonProperty("b64_json")
String b64Json;
}

View File

@ -0,0 +1,24 @@
package com.theokanning.openai.image;
import lombok.Data;
import java.util.List;
/**
* An object with a list of image results.
*
* https://beta.openai.com/docs/api-reference/images
*/
@Data
public class ImageResult {
/**
* The creation time in epoch seconds.
*/
Long createdAt;
/**
* List of image results.
*/
List<Image> data;
}

View File

@ -11,6 +11,9 @@ import com.theokanning.openai.file.File;
import com.theokanning.openai.finetune.FineTuneEvent; import com.theokanning.openai.finetune.FineTuneEvent;
import com.theokanning.openai.finetune.FineTuneRequest; import com.theokanning.openai.finetune.FineTuneRequest;
import com.theokanning.openai.finetune.FineTuneResult; import com.theokanning.openai.finetune.FineTuneResult;
import com.theokanning.openai.image.CreateImageEditRequest;
import com.theokanning.openai.image.CreateImageRequest;
import com.theokanning.openai.image.ImageResult;
import com.theokanning.openai.model.Model; import com.theokanning.openai.model.Model;
import com.theokanning.openai.moderation.ModerationRequest; import com.theokanning.openai.moderation.ModerationRequest;
import com.theokanning.openai.moderation.ModerationResult; import com.theokanning.openai.moderation.ModerationResult;
@ -82,6 +85,15 @@ public interface OpenAiApi {
@DELETE("/v1/models/{fine_tune_id}") @DELETE("/v1/models/{fine_tune_id}")
Single<DeleteResult> deleteFineTune(@Path("fine_tune_id") String fineTuneId); Single<DeleteResult> deleteFineTune(@Path("fine_tune_id") String fineTuneId);
@POST("/v1/images/generations")
Single<ImageResult> createImage(@Body CreateImageRequest request);
@POST("/v1/images/edits")
Single<ImageResult> createImageEdit(@Body RequestBody requestBody);
@POST("/v1/images/variations")
Single<ImageResult> createImageVariation(@Body RequestBody requestBody);
@POST("/v1/moderations") @POST("/v1/moderations")
Single<ModerationResult> createModeration(@Body ModerationRequest request); Single<ModerationResult> createModeration(@Body ModerationRequest request);

View File

@ -15,6 +15,7 @@ import com.theokanning.openai.file.File;
import com.theokanning.openai.finetune.FineTuneEvent; import com.theokanning.openai.finetune.FineTuneEvent;
import com.theokanning.openai.finetune.FineTuneRequest; import com.theokanning.openai.finetune.FineTuneRequest;
import com.theokanning.openai.finetune.FineTuneResult; import com.theokanning.openai.finetune.FineTuneResult;
import com.theokanning.openai.image.*;
import com.theokanning.openai.model.Model; import com.theokanning.openai.model.Model;
import com.theokanning.openai.moderation.ModerationRequest; import com.theokanning.openai.moderation.ModerationRequest;
import com.theokanning.openai.moderation.ModerationResult; import com.theokanning.openai.moderation.ModerationResult;
@ -169,6 +170,62 @@ public class OpenAiService {
return api.deleteFineTune(fineTuneId).blockingGet(); return api.deleteFineTune(fineTuneId).blockingGet();
} }
public ImageResult createImage(CreateImageRequest request) {
return api.createImage(request).blockingGet();
}
public ImageResult createImageEdit(CreateImageEditRequest request, String imagePath, String maskPath) {
java.io.File image = new java.io.File(imagePath);
java.io.File mask = null;
if (maskPath != null) {
mask = new java.io.File(maskPath);
}
return createImageEdit(request, image, mask);
}
public ImageResult createImageEdit(CreateImageEditRequest request, java.io.File image, java.io.File mask) {
RequestBody imageBody = RequestBody.create(MediaType.parse("image"), image);
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MediaType.get("multipart/form-data"))
.addFormDataPart("prompt", request.getPrompt())
.addFormDataPart("size", request.getSize())
.addFormDataPart("response_format", request.getResponseFormat())
.addFormDataPart("image", "image", imageBody);
if (request.getN() != null) {
builder.addFormDataPart("n", request.getN().toString());
}
if (mask != null) {
RequestBody maskBody = RequestBody.create(MediaType.parse("image"), mask);
builder.addFormDataPart("mask", "mask", maskBody);
}
return api.createImageEdit(builder.build()).blockingGet();
}
public ImageResult createImageVariation(CreateImageVariationRequest request, String imagePath) {
java.io.File image = new java.io.File(imagePath);
return createImageVariation(request, image);
}
public ImageResult createImageVariation(CreateImageVariationRequest request, java.io.File image) {
RequestBody imageBody = RequestBody.create(MediaType.parse("image"), image);
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MediaType.get("multipart/form-data"))
.addFormDataPart("size", request.getSize())
.addFormDataPart("response_format", request.getResponseFormat())
.addFormDataPart("image", "image", imageBody);
if (request.getN() != null) {
builder.addFormDataPart("n", request.getN().toString());
}
return api.createImageVariation(builder.build()).blockingGet();
}
public ModerationResult createModeration(ModerationRequest request) { public ModerationResult createModeration(ModerationRequest request) {
return api.createModeration(request).blockingGet(); return api.createModeration(request).blockingGet();
} }

View File

@ -0,0 +1,95 @@
package com.theokanning.openai;
import com.theokanning.openai.image.CreateImageEditRequest;
import com.theokanning.openai.image.CreateImageRequest;
import com.theokanning.openai.image.CreateImageVariationRequest;
import com.theokanning.openai.image.Image;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class ImageTest {
static String filePath = "src/test/resources/penguin.png";
static String fileWithAlphaPath = "src/test/resources/penguin_with_alpha.png";
static String maskPath = "src/test/resources/mask.png";
String token = System.getenv("OPENAI_TOKEN");
OpenAiService service = new OpenAiService(token, 30);
@Test
void createImageUrl() {
CreateImageRequest createImageRequest = CreateImageRequest.builder()
.prompt("penguin")
.n(3)
.size("256x256")
.user("testing")
.build();
List<Image> images = service.createImage(createImageRequest).getData();
assertEquals(3, images.size());
assertNotNull(images.get(0).getUrl());
}
@Test
void createImageBase64() {
CreateImageRequest createImageRequest = CreateImageRequest.builder()
.prompt("penguin")
.responseFormat("b64_json")
.user("testing")
.build();
List<Image> images = service.createImage(createImageRequest).getData();
assertEquals(1, images.size());
assertNotNull(images.get(0).getB64Json());
}
@Test
void createImageEdit() {
CreateImageEditRequest createImageRequest = CreateImageEditRequest.builder()
.prompt("a penguin with a red background")
.responseFormat("url")
.size("256x256")
.user("testing")
.n(2)
.build();
List<Image> images = service.createImageEdit(createImageRequest, fileWithAlphaPath, null).getData();
assertEquals(2, images.size());
assertNotNull(images.get(0).getUrl());
}
@Test
void createImageEditWithMask() {
CreateImageEditRequest createImageRequest = CreateImageEditRequest.builder()
.prompt("a penguin with a red hat")
.responseFormat("url")
.size("256x256")
.user("testing")
.n(2)
.build();
List<Image> images = service.createImageEdit(createImageRequest, filePath, maskPath).getData();
assertEquals(2, images.size());
assertNotNull(images.get(0).getUrl());
}
@Test
void createImageVariation() {
CreateImageVariationRequest createImageVariationRequest = CreateImageVariationRequest.builder()
.responseFormat("url")
.size("256x256")
.user("testing")
.n(2)
.build();
List<Image> images = service.createImageVariation(createImageVariationRequest, filePath).getData();
assertEquals(2, images.size());
assertNotNull(images.get(0).getUrl());
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB