Skip to content
Draft
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ helix-importer-ui
.DS_Store
*.bak
.idea

# for now ensure ML models are not accidentally published
models/
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,17 @@ npm run lint
1. Install the [AEM CLI](https://github.com/adobe/helix-cli): `npm install -g @adobe/aem-cli`
1. Start AEM Proxy: `aem up` (opens your browser at `http://localhost:3000`)
1. Open the `helix-labs-website` directory in your favorite IDE and start coding :)

### Image Identity

`Image identity` features require ML models that are not part of this repository as they cannot be published on the internet (i.e. commited to an Helix repo). To demo one has to run helix-labs locally with the models present on the local machine.


1. Check out this repo
1. Create a `models/` directory in the root of the project
1. Get the model files in onnx format. These are Adobe-internal and distributed outside this repo.
1. Copy the model files into the `models/` directory
1. Run `aem up`
1. Go to
- <http://localhost:3000/tools/asset-identity/>
- <http://localhost:3000/tools/image-audit/> (enable `Advanced Image Identity`)
1 change: 1 addition & 0 deletions scripts/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export function buildModal() {
close.innerHTML = '<i class="symbol symbol-close"></i>';
close.addEventListener('click', closeModal);
const body = document.createElement('div');
body.classList.add('modal-body');
dialog.append(close, body);
return [dialog, body];
}
Expand Down
41 changes: 41 additions & 0 deletions tools/asset-identity/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Asset Identity</title>
<script nonce="aem" src="/scripts/aem.js" type="module"></script>
<script nonce="aem" src="/scripts/scripts.js" type="module"></script>
<link rel="stylesheet" href="/styles/styles.css" />
<script nonce="aem" src="/tools/asset-identity/scripts.js" type="module"></script>
<link rel="stylesheet" href="/tools/asset-identity/styles.css" />
</head>

<body class="image-audit">
<header></header>
<main>
<div>
<h1>Asset Identity</h1>
<p>
Playground for bringing asset identity AI into the browser.
</p>
</div>

<div class="asset-identity">
<form>
<div class="viewbox" data-status="upload">
<label for="upload">
<span class="glyph glyph-upload"></span>
<p>Click to upload an image or drag-and-drop one here</p>
</label>
<input type="file" accept="image/*" name="upload">
</div>
</form>
</div>
</main>

<footer></footer>
</body>

</html>
57 changes: 57 additions & 0 deletions tools/asset-identity/scripts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { getImageFingerprint } from '../../utils/identity.js';

function disableForm() {
const viewbox = document.querySelector('.viewbox');
if (viewbox) {
viewbox.classList.remove('hover');
viewbox.dataset.status = 'preview';
}
const label = document.querySelector('label[for="upload"]');
if (label) {
label.setAttribute('aria-hidden', true);
}
const upload = document.querySelector('input[name="upload"]');
if (upload) {
upload.disabled = true;
}
}

async function initUpload() {
// get input element with name "upload"
const upload = document.querySelector('input[name="upload"]');
const viewbox = document.querySelector('.viewbox');
upload.addEventListener('change', () => {
const file = upload.files[0];
if (file) {
const reader = new FileReader();
reader.addEventListener('load', async (e) => {
// show image
const imgWrapper = document.createElement('div');
imgWrapper.classList.add('preview');
imgWrapper.innerHTML = `<img src="${e.target.result}" alt="${file.name}">`;
viewbox.appendChild(imgWrapper);
disableForm();

const img = imgWrapper.querySelector('img');
try {
const fingerprint = await getImageFingerprint(img, file.name);
console.debug('Fingerprint:', fingerprint);

Check warning on line 38 in tools/asset-identity/scripts.js

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
} catch (error) {
console.error('Error getting fingerprint:', error);

Check warning on line 40 in tools/asset-identity/scripts.js

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
}
});
reader.readAsDataURL(file);
}
});
}

async function init() {
initUpload();

// await initIdentity();
// load test image for debugging
// const fingerprint = await getImageFingerprint('/tools/asset-identity/samples/earth.png');
// console.debug('Fingerprint:', fingerprint);
}

init();
188 changes: 188 additions & 0 deletions tools/asset-identity/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/* form */
.asset-identity {
--color-checkerboard-bg-fill: #fefefe;
--color-checkerboard-bg-border: rgb(214 213 213 / 40%);
--checkerboard-background: linear-gradient( var(--color-checkerboard-bg-border) 1px, transparent 1px ), linear-gradient( 90deg, var(--color-checkerboard-bg-border) 1px, transparent 1px ), linear-gradient( 45deg, var(--color-checkerboard-bg-fill) 50%, var(--color-checkerboard-bg-fill) 51% );
--checkerboard-background-size: clamp(15px, 3vw, 25px) clamp(15px, 3vw, 25px);
}

.asset-identity.block {
text-align: left;
}

.asset-identity form {
position: relative;
}

.asset-identity form input[name='upload'] {
display: block;
cursor: pointer;
position: absolute;
inset: 0;
opacity: 0;
}

.asset-identity form input[name='upload']:disabled,
.asset-identity form label[for='upload'][aria-hidden="true"] {
display: none;
visibility: hidden;
}

.asset-identity form label {
transition: color .3s;
}

.asset-identity form .viewbox[data-mode='dark'] label {
color: white;
}

.asset-identity form label p {
color: inherit;
text-align: center;
}

.asset-identity form label span + p {
margin-top: .5em;
}

.asset-identity .form .form-number-wrapper,
.asset-identity .form label[for="width"], .asset-identity .form input#width,
.asset-identity .form label[for="height"], .asset-identity .form input#height {
visibility: hidden;
display: none;
}

/* viewbox */
.asset-identity .viewbox {
display: flex;
align-items: center;
justify-content: center;

/* width: 100%; */
height: 100vw;
outline: 2px solid transparent;
border: 1px solid var(--color-checkerboard-bg-border);
padding: 1rem;
background-image: var(--checkerboard-background);
background-size: var(--checkerboard-background-size);
background-position: center;
}

.asset-identity .viewbox,
.asset-identity .viewbox [data-mode='light'] {
--color-checkerboard-bg-fill: #fefefe;
--color-checkerboard-bg-border: rgb(214 213 213 / 40%);
--checkerboard-background: linear-gradient(
var(--color-checkerboard-bg-border) 1px,
transparent 1px
),
linear-gradient(
90deg,
var(--color-checkerboard-bg-border) 1px,
transparent 1px
),
linear-gradient(
45deg,
var(--color-checkerboard-bg-fill) 50%,
var(--color-checkerboard-bg-fill) 51%
);
}

.asset-identity .viewbox[data-mode='dark'] {
--color-checkerboard-bg-border: rgb(41 42 42 / 40%);
--color-checkerboard-bg-fill: #010101;
}

.asset-identity .viewbox[data-status='upload'] {
transition: background-color .3s, outline .3s;
}

.asset-identity .viewbox[data-status='upload'].hover,
.asset-identity .viewbox[data-status='upload']:hover {
--color-checkerboard-bg-fill: var(--gray-100);

outline: 2px solid var(--link-hover-color);
}

.asset-identity .viewbox[data-status='upload'][data-mode='dark'].hover,
.asset-identity .viewbox[data-status='upload'][data-mode='dark']:hover {
--color-checkerboard-bg-fill: #070707;
}

.asset-identity .viewbox svg {
width: 100%;
height: auto;
max-height: calc(100vw - 3rem);
}

@media (width >= 900px) {
.asset-identity .viewbox {
height: 860px;
}

.asset-identity .viewbox svg {
max-height: calc(860px - 3rem);
}
}

/* icons/glyphs */
.asset-identity .glyph,
.asset-identity .glyph::before,
.asset-identity .glyph::after {
box-sizing: border-box;
display: block;
position: relative;
color: var(--color-text);
}

.asset-identity .glyph::before,
.asset-identity .glyph::after {
content: '';
position: absolute;
}

.asset-identity .glyph-upload {
width: 20px;
height: 10px;
margin: auto;
margin-top: 12px;
border: 2px solid;
border-top: 0;
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}

.asset-identity .glyph-upload::after {
left: 3px;
bottom: 7px;
width: 10px;
height: 10px;
border-left: 2px solid;
border-top: 2px solid;
transform: rotate(45deg);
}

.asset-identity .glyph-upload::before {
left: 7px;
bottom: 5px;
width: 2px;
height: 12px;
background: currentcolor;
}

/* preview */
.asset-identity .viewbox .preview {
width: 100%;
height: 100%;
color: black;
}

.asset-identity .viewbox .preview img {
width: 100%;
height: 100%;
object-fit: contain;
}

.asset-identity .viewbox[data-mode='dark'] .preview {
color: white;
}
16 changes: 14 additions & 2 deletions tools/image-audit/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<head>
<meta
http-equiv="Content-Security-Policy"
content="script-src 'nonce-aem' 'strict-dynamic'; base-uri 'self'; object-src 'none';"
content="script-src 'nonce-aem' 'strict-dynamic' 'wasm-unsafe-eval'; base-uri 'self'; object-src 'none';"
move-to-http-header="true"
>
<meta charset="UTF-8">
Expand Down Expand Up @@ -37,13 +37,22 @@ <h1>Image Audit</h1>
</section>

<div class="field-group">

<div class="form-field text-field path-filter-field">
<label for="path">Path Filter</label>
<input name="path" id="path" data-default-value="/" placeholder="/" />
</div>
</div>

<div class="field-group">
<div class="form-field checkbox-field">
<label>
<input type="checkbox" name="identity" id="identity">
<span>Advanced Image Identity</span>
</label>
</div>
</div>

<p class="button-wrapper">
<button type="submit" class="button">Start Audit</button>
<button type="reset" id="form-reset" class="button outline">Reset</button>
Expand All @@ -57,6 +66,9 @@ <h1>Image Audit</h1>
<p class="button-wrapper">
<button type="button" class="button" id="download-report" disabled>Download Audit Report</button>
</p>
<p class="button-wrapper" hidden>
<button type="button" class="button" id="visualize-image-clusters">Visualize Image Clusters</button>
</p>
</div>
<div class="canvas" id="canvas">
<div class="action-bar">
Expand Down
Loading
Loading