Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add image generattion parameters to jpeg exif metadata #21

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"jszip": "^3.10.1",
"marked": "^4.1.1",
"pako": "^2.1.0",
"piexifjs": "^1.0.6",
"pinia": "^2.0.21",
"rxjs": "^7.8.0",
"sanitize-html": "^2.7.2",
Expand Down
2 changes: 1 addition & 1 deletion src/components/ImageActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ const ratingDialog = ref(false);

<template>
<el-button @click="confirmDelete" type="danger" :icon="Delete" plain>Delete</el-button>
<el-button @click="downloadImage(imageData.image, `${imageData.seed}-${imageData.prompt}`)" type="success" :icon="Download" plain>Download</el-button>
<el-button @click="downloadImage(imageData.image, `${imageData.seed}-${imageData.prompt}`, imageData)" type="success" :icon="Download" plain>Download</el-button>
<el-button v-if="!imageData.starred" @click="outputStore.toggleStarred(imageData.id)" type="warning" :icon="Star" plain>Favourite</el-button>
<el-button v-if="imageData.starred" @click="outputStore.toggleStarred(imageData.id)" type="warning" :icon="StarFilled" plain>Unfavourite</el-button>
<el-button @click="store.generateText2Img(imageData)" type="success" :icon="Refresh" plain>Text2img</el-button>
Expand Down
27 changes: 22 additions & 5 deletions src/utils/base64.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import piexif from 'piexifjs';

/**
* Converts base64 data into to another data type
Expand All @@ -23,22 +24,38 @@ export async function convertBase64ToDataType(base64Image: string, contentType:
/**
* Converts base64 data into a Uint8Array
*/
export async function convertBase64ToUint8Array(base64Image: string, contentType?: string) {
export async function convertBase64ToUint8Array(base64Image: string, contentType?: string, userComment?: string) {
// Split into two parts
const parts = base64Image.split(';base64,');

// Hold the content type
const imageType = contentType ?? parts[0].split(':')[1];

// Decode Base64 string
const decodedData =
let decodedData =
window.atob(
imageType === parts[0].split(':')[1] ?
parts[1] :
(await convertBase64ToDataType(base64Image, imageType)).split(',')[1]
);


// Add exif data if filetype is jpeg, and userComment is set
if (imageType == "image/jpeg" && userComment) {
const zeroth = {
[piexif.ImageIFD.Software]: "Stable UI - Create images with Stable Diffusion using the AI Horde"
};
const exif = userComment ? {
[piexif.ExifIFD.UserComment]: `ASCII\0\0\0${userComment}`
} : undefined;
const exifObj = {
"0th": zeroth,
"Exif": exif
};
const exifbytes = piexif.dump(exifObj);
decodedData = piexif.insert(exifbytes, decodedData);
}

// Create UNIT8ARRAY of size same as row data length
const uInt8Array = new Uint8Array(decodedData.length);

Expand All @@ -53,12 +70,12 @@ export async function convertBase64ToUint8Array(base64Image: string, contentType
/**
* Converts base64 data into a blob
*/
export async function convertBase64ToBlob(base64Image: string, contentType?: string) {
export async function convertBase64ToBlob(base64Image: string, contentType?: string, userComment?: string) {
// Hold the content type
const imageType = contentType ?? base64Image.split(';base64,')[0].split(':')[1];

// Get Uint8Array
const uInt8Array = await convertBase64ToUint8Array(base64Image, contentType)
const uInt8Array = await convertBase64ToUint8Array(base64Image, contentType, userComment)

// Return BLOB image after conversion
return new Blob([uInt8Array], { type: imageType });
Expand Down
18 changes: 15 additions & 3 deletions src/utils/download.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { convertBase64ToBlob } from './base64';
import { downloadZip, type InputWithSizeMeta } from 'client-zip';
import { db } from '@/utils/db';
import type { IndexableType } from 'dexie';
import { type ImageData } from '@/stores/outputs';

export async function downloadMultipleImages(outputKeys: IndexableType[], showMessage = true, onProcess?: () => void) {
const optionsStore = useOptionsStore();
Expand Down Expand Up @@ -32,7 +33,7 @@ export async function downloadMultipleImages(outputKeys: IndexableType[], showMe
} else if (optionsStore.imageDownloadType === "JPG") {
toDownload.push({
name: fileName + ".jpg",
input: await convertBase64ToBlob(image, "image/jpeg"),
input: await convertBase64ToBlob(image, "image/jpeg", getMetaData(output)),
})
} else {
toDownload.push({
Expand All @@ -57,7 +58,18 @@ export async function downloadMultipleImages(outputKeys: IndexableType[], showMe
downloadLink.click();
}

export async function downloadImage(base64Data: string, fileName: string) {
// Create a string with the image params for the file
function getMetaData(imageData: ImageData) : string{
const prompts = imageData.prompt.split("###");
const prompt = prompts[0].trim();
const negativePrompt = prompts.length > 1 ? prompts[1].trim() : null;
return `${prompt}\n` +
((negativePrompt) ? `Negative prompt: ${negativePrompt}\n` : ``) +
`Steps: ${imageData.steps}, Sampler: ${imageData.sampler_name}, CFG scale: ${imageData.cfg_scale}, Seed: ${imageData.seed},` +
`Size: ${imageData.width}x${imageData.height}, model: ${imageData.modelName}`
}

export async function downloadImage(base64Data: string, fileName: string, imageData: ImageData) {
const optionsStore = useOptionsStore();

const downloadLink = document.createElement("a");
Expand All @@ -68,7 +80,7 @@ export async function downloadImage(base64Data: string, fileName: string) {
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = fileName.replace(/[/\\:*?"<>]/g, "").substring(0, 128).trimEnd() + ".png"; // Only get first 128 characters so we don't break the max file name limit
} else if (optionsStore.imageDownloadType === "JPG") {
blob = await convertBase64ToBlob(base64Data, "image/jpeg");
blob = await convertBase64ToBlob(base64Data, "image/jpeg", getMetaData(imageData));
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = fileName.replace(/[/\\:*?"<>]/g, "").substring(0, 128).trimEnd() + ".jpg";
} else {
Expand Down