How To Create OpenAI DALL·E Mask Images

If you are starting to work with the OpenAI image edit API, you will need some tools to create the mask image. This mask image contains…

How To Create OpenAI DALL·E Mask Images

If you are starting to work with the OpenAI image edit API, you will need some tools to create the mask image. This mask image contains erased pixels where the AI should draw new content.

Open AI Image Requirements

  1. Image must have transparent masking added
  2. Less than 4MB in size
  3. Aspect ratio 1:1 (square)
  4. PNG image format

Creating the mask image

First start with an image you want to edit and an idea of what you want to change about the image. In this example we will be adding a baseball cap to the image. This image was generated using text to image with Open AI and is about 527x527 pixels.

Next we want to remove pixels from the image where we want to AI to draw the baseball cap. Many tools can do this work, but to speed up the process, I created a simple website. Visit the site below to create your mask image.

https://ai-image-editor.netlify.app/

Choose the image you want to edit using the Choose File button.

Erase the part of the photo you want to be changed.

Save both the original and the mask image.

Now that you have the images needed for you image editing, we can come up with the prompt we will use to let the AI know what we want it to draw.

Draw baseball cap

Add baseball cap to image

Put front facing baseball cap on head of person in image

These are a few examples of prompts that could be used to generate an image edit. Now we can send the API request to get our AI generated edit back.

response = openai.Image.create_edit(
image=open("original.png", "rb"),
mask=open("mask.png", "rb"),
prompt="Put front facing baseball cap on head of person in image",
n=1,
size="1024x1024"
)
image_url = response['data'][0]['url']

You will get something like this returned from the API.

Now you can change the prompt and see what other types of edits you can generate.

Mask Editor Code

Interested in making your own AI image editor? Here is the code used to remove pixels from an image to get you started.

import { useState } from 'react'
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, Button } from 'react-native';
const Separator = () => <View style={styles.separator} />;

export default function App() {
const [originalImage, setOriginalImage] = useState('')

const fileSelected = (event) => {
const file = event.target.files[0]
const img = new Image()
img.src = URL.createObjectURL(file)
img.onload = () => {
if (img.width !== img.height) {
const minVal = Math.min(img.width, img.height)
setup(img, 0, 0, minVal, minVal)
} else {
setup(img, 0, 0, img.width, img.height)
}
}
}

const setup = (img, x, y, width, height) => {
const node = document.getElementById("PictureLayer");
if (node && node.parentNode) {
node.parentNode.removeChild(node);
}

var can = document.createElement('canvas');
can.id = "PictureLayer"
can.width = window.innerWidth * 0.45
can.height = window.innerWidth * 0.45
can.style = 'margin:auto;'
const outerCanvas = document.getElementById('outer-canvas')
outerCanvas.appendChild(can)

var ctx = can.getContext("2d")
ctx.drawImage(img, x, y, width, height, 0, 0, can.width, can.height);
ctx.lineCap = "round";
ctx.lineWidth = 25;
ctx.globalCompositeOperation = 'destination-out'
let isDrawing = false

const startDrawing = (event) => {
isDrawing = true
const pos = getPos(event)
console.log('start erasing', pos)
points.setStart(pos.x, pos.y)
}
const stopDrawing = () => {
console.log('stop erasing')
isDrawing = false
}
const draw = (event) => {
if (!isDrawing) return
const pos = getPos(event)
points.newPoint(pos.x, pos.y)
}

var points = function() {
var queue = [], qi = 0;

function clear() {
queue = [];
qi = 0;
}

function setStart(x, y) {
clear();
newPoint(x, y);
}

function newPoint(x, y) {
queue.push([x, y]);
}

function tick() {
var k = 20; // adjust to limit points drawn per cycle
if (queue.length - qi > 1) {
ctx.beginPath();
if (qi === 0)
ctx.moveTo(queue[0][0], queue[0][1]);
else
ctx.moveTo(queue[qi - 1][0], queue[qi - 1][1]);

for (++qi; --k >= 0 && qi < queue.length; ++qi) {
ctx.lineTo(queue[qi][0], queue[qi][1]);
}
ctx.stroke();
}
}

setInterval(tick, 50); // adjust cycle time

return {
setStart: setStart,
newPoint: newPoint
};
}()

window.addEventListener("touchstart", startDrawing)
window.addEventListener("mouseup", stopDrawing)
can.addEventListener("touchend", stopDrawing)
can.addEventListener("mousedown", startDrawing)
can.addEventListener("mousemove", draw)
can.addEventListener("touchmove", draw)

function getPos(e) {
var rect = can.getBoundingClientRect();
if (e.touches) {
return { x: e.touches[0].clientX - rect.left, y: e.touches[0].clientY - rect.top }
}
return { x: e.clientX - rect.left, y: e.clientY - rect.top }
}
setOriginalImage(can.toDataURL())
}

const downloadOriginal = () => {
console.log('download original')
const node = document.getElementById("PictureLayer")
if (!node) return
var link = document.createElement('a')
link.download = 'original.png'
link.href = originalImage
link.click()
}

const downloadMask = () => {
console.log('download mask')
const node = document.getElementById("PictureLayer")
if (!node) return
var link = document.createElement('a')
link.download = 'mask.png'
link.href = node.toDataURL()
link.click()
}

return (
<View style={styles.container}>
<h2 style={styles.title}>DALL-E 2 Image Mask Editor</h2>
<Text style={styles.title}>1. <input type="file" accept="image/*" onChange={fileSelected} /></Text>
<View>
<Text style={styles.title}>2. Use mouse to erase parts of the photo that should be edited by AI</Text>
<div style={styles.outerCanvas} id='outer-canvas' />
</View>
<View>
<Text style={styles.title}>3. Download Images</Text>
<View style={styles.buttonContainer}>
<Button style={{ margin: 10 }} title='Download Original' onPress={downloadOriginal} />
</View>
<View style={styles.buttonContainer}>
<Button style={{ margin: 10 }} title='Download Mask' onPress={downloadMask} />
</View>
</View>
<StatusBar style="auto" />
</View>

);
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
separator: {
marginVertical: 8,
borderBottomColor: '#737373',
borderBottomWidth: StyleSheet.hairlineWidth,
},
title: {
textAlign: 'center',
marginVertical: 8,
},
fixToText: {
flexDirection: 'row',
justifyContent: 'space-between',
},
outerCanvas: {
width: window.innerWidth * 0.45,
height: window.innerWidth * 0.45,
borderStyle: 'solid'
},
buttonContainer: {
marginVertical: 8
}
});

Thank you for reading! Stay tuned for more.

Contact

Open for contract projects as a Project Leader or Individual Contributor. Let’s chat!

LinkedIn: https://www.linkedin.com/in/davidrichards5/
Email: david.richards.tech (@) gmail.com