Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Readme

Gemini AI Object Detection and Bounding Box Visualizer

This application visualizes object detection results by drawing bounding boxes on images using the Google's Gemini 1.5 Pro AI model.

Try using this image of 8 bananas, with 1 row and 1/2/4 columns: https://f2.phage.directory/blogalog/bananas-8.png

Or this image of a phage plaque assay, with 3x3 grid and high contrast turned on: https://f2.phage.directory/blogalog/pae7.png

This app is an adaptation of Simon Willison's idea, which you can read more about here: https://simonwillison.net/2024/Aug/26/gemini-bounding-box-visualization/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { Hono } from "https://deno.land/x/hono@v3.11.7/mod.ts";
import { marked } from "npm:marked";
const intro = `
# Gemini AI Object Detection and Bounding Box Visualizer
This application visualizes object detection results by drawing bounding boxes on images using the [Google's Gemini 1.5 Pro AI model](https://ai.google.dev/).
Try using this image of 8 bananas, with 1 row and 1/2/4 columns: [https://f2.phage.directory/blogalog/bananas-8.png](https://f2.phage.directory/blogalog/bananas-8.png)
Or this image of a phage plaque assay, with 3x3 grid and high contrast turned on: [https://f2.phage.directory/blogalog/pae7.png](https://f2.phage.directory/blogalog/pae7.png)
This app is an adaptation of Simon Willison's idea, which you can read more about here: [https://simonwillison.net/2024/Aug/26/gemini-bounding-box-visualization/](https://simonwillison.net/2024/Aug/26/gemini-bounding-box-visualization/)
- API keys are only stored in your browser's local storage.
- Source code: [https://www.val.town/v/yawnxyz/geminiBbox/](https://www.val.town/v/yawnxyz/geminiBbox/)
- Original Source: https://github.com/simonw/tools/blob/main/gemini-bbox.html
`;
const markdownJsonRegex = /```json\n([\s\S]*?)```/;
export const module = `<script type="module">
import { GoogleGenerativeAI } from "https://esm.run/@google/generative-ai";
import { marked } from "https://esm.run/marked";
function getApiKey() {
let apiKey = localStorage.getItem("GEMINI_API_KEY");
if (!apiKey) {
apiKey = prompt("Please enter your Gemini API key:");
if (apiKey) {
localStorage.setItem("GEMINI_API_KEY", apiKey);
}
}
return apiKey;
}
async function getGenerativeModel(params) {
const API_KEY = getApiKey();
const genAI = new GoogleGenerativeAI(API_KEY);
return genAI.getGenerativeModel(params);
}
async function fileToGenerativePart(file) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => resolve({
inlineData: {
data: reader.result.split(",")[1],
mimeType: file.type
}
});
reader.readAsDataURL(file);
});
}
function applyHighContrast(canvas) {
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const darkThreshold = parseInt(document.getElementById('darkThresholdInput').value);
const lightThreshold = parseInt(document.getElementById('lightThresholdInput').value);
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
if (avg < darkThreshold) {
// Make dark gray things more black
data[i] = data[i + 1] = data[i + 2] = 0;
} else if (avg > lightThreshold) {
// Make light gray things go away (white)
data[i] = data[i + 1] = data[i + 2] = 255;
}
// Leave mid-range values unchanged
}
ctx.putImageData(imageData, 0, 0);
}
function resizeAndCompressImage(file, maxWidth = 1000) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = function(event) {
const img = new Image();
img.onload = function() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let width = img.width;
let height = img.height;
if (width > maxWidth) {
height = Math.round((height * maxWidth) / width);
width = maxWidth;
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
yawnxyz-geminibbox.web.val.run
September 5, 2024