blog updates. more demo assets, SURF-attempts and more
This commit is contained in:
+1304
-196
File diff suppressed because it is too large
Load Diff
+1176
-74
File diff suppressed because it is too large
Load Diff
+675
-149
@@ -2,19 +2,33 @@ body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: var(--foreground);
|
||||
background: var(--background) url('bg.jpeg');
|
||||
background-size: cover;
|
||||
background: var(--background);
|
||||
font-family: sans-serif;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
/* Fixed background layer, same construction as the landing page */
|
||||
|
||||
#bg {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: -1;
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
#bg img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: 50% 50%;
|
||||
opacity: 0;
|
||||
transition: opacity 3s linear;
|
||||
}
|
||||
|
||||
#bg img.loaded {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
@@ -25,7 +39,7 @@ h1, h2, h3, h4, h5, h6 {
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
font-size: 24px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@@ -34,45 +48,98 @@ h2 {
|
||||
|
||||
p {
|
||||
margin-top: 2px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color2);
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
strong {
|
||||
color: var(--color4);
|
||||
a:hover {
|
||||
color: var(--color3);
|
||||
}
|
||||
|
||||
em {
|
||||
color: var(--color3);
|
||||
}
|
||||
|
||||
sup.fn {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
sup.fn a {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
margin: 8px 0;
|
||||
max-width: 640px;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
border: 1px solid;
|
||||
border: 1px solid color-mix(in srgb, var(--foreground) 25%, transparent);
|
||||
display: block;
|
||||
padding: 8px 12px;
|
||||
overflow-x: auto;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
article {
|
||||
max-width: 1024px;
|
||||
min-height: 128px;
|
||||
margin: 25px auto;
|
||||
padding: 20px;
|
||||
background: color-mix(in srgb, var(--background) 90%, transparent);
|
||||
box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
code.block {
|
||||
display: block;
|
||||
@media (max-width: 1064px) {
|
||||
article {
|
||||
margin: 15px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
math[display="block"] {
|
||||
margin: 12px 0 12px 24px;
|
||||
font-size: 17px;
|
||||
color: var(--color4);
|
||||
}
|
||||
|
||||
aside.explainer {
|
||||
border-left: 3px solid var(--color4);
|
||||
background: color-mix(in srgb, var(--foreground) 5%, transparent);
|
||||
padding: 8px 12px;
|
||||
margin: 12px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.rewrite-target {
|
||||
text-decoration: underline dashed var(--color3);
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
|
||||
.rewrite-target::after {
|
||||
content: ' ✎';
|
||||
color: var(--color3);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.attribution {
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.big-number {
|
||||
font-size: 26px;
|
||||
color: var(--color1);
|
||||
margin: 8px 0 4px 0;
|
||||
}
|
||||
|
||||
/* Demo */
|
||||
|
||||
.needs-image {
|
||||
@@ -88,135 +155,107 @@ img {
|
||||
min-width: 256px;
|
||||
}
|
||||
|
||||
/* Resize demo */
|
||||
.sample {
|
||||
height: 48px;
|
||||
margin: 0 4px 4px 0;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#resize {
|
||||
.sample:hover {
|
||||
outline: 2px solid var(--color1);
|
||||
}
|
||||
|
||||
/* Comparison slider */
|
||||
|
||||
.compare {
|
||||
position: relative;
|
||||
height: 512px;
|
||||
max-width: 512px;
|
||||
overflow: hidden;
|
||||
touch-action: none;
|
||||
cursor: ew-resize;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.compare img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
transition: height 2s linear;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#resize.resize {
|
||||
height: 256px;
|
||||
}
|
||||
|
||||
#resize .image-original {
|
||||
/* The top image stretches to the base image's box, so differently
|
||||
sized results (8x8 thumbs, crops) stay comparable under the divider */
|
||||
.compare .compare-b {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 512px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
object-fit: fill;
|
||||
clip-path: inset(0 0 0 50%);
|
||||
}
|
||||
|
||||
#resize .image-original img {
|
||||
max-height: 50vh;
|
||||
min-height: 257px;
|
||||
max-width: 100%;
|
||||
min-width: 257px;
|
||||
/* height: 512px; */
|
||||
}
|
||||
|
||||
#resize-original {
|
||||
position: relative;
|
||||
height: 512px;
|
||||
}
|
||||
|
||||
#resize-original img {
|
||||
.compare .compare-divider {
|
||||
position: absolute;
|
||||
max-width: 90vw;
|
||||
max-height: 512px;
|
||||
min-width: 257px;
|
||||
min-height: 257px;
|
||||
opacity: 1;
|
||||
transition:
|
||||
width 2s linear,
|
||||
height 2s linear,
|
||||
filter 3s linear 2s,
|
||||
opacity 3s ease-in 5s;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 2px;
|
||||
margin-left: -1px;
|
||||
background: var(--color1);
|
||||
}
|
||||
|
||||
#resize.resize #resize-original img {
|
||||
width: 256px !important;
|
||||
height: 256px !important;
|
||||
filter: grayscale(1) blur(10px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#resize-original img.grayscale {
|
||||
filter: grayscale(1) blur(10px);
|
||||
}
|
||||
|
||||
#resize-original img.fade {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#image-resize img {
|
||||
width: 256px;
|
||||
height: 256px;
|
||||
image-rendering: pixelated;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
}
|
||||
|
||||
#image-dct {
|
||||
margin-left: 32px;
|
||||
display:grid;
|
||||
grid-template-columns: repeat(8, 32px);
|
||||
}
|
||||
|
||||
#image-dct > div {
|
||||
height: 32px;
|
||||
line-height:32px;
|
||||
font-size: 11px;
|
||||
.compare .compare-handle {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
border: 1px solid var(--color1);
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
background: var(--color1);
|
||||
color: var(--background);
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.similarity-example {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: flex-end;
|
||||
.compare .compare-label {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
padding: 2px 6px;
|
||||
font-size: 11px;
|
||||
background: color-mix(in srgb, var(--background) 75%, transparent);
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 10px;
|
||||
width: 50%;
|
||||
.compare .compare-label.a {
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
/* Loader */
|
||||
|
||||
#loader.show {
|
||||
border-radius: 50%;
|
||||
width: 256px;
|
||||
height: 256px;
|
||||
font-size: 10px;
|
||||
position: fixed;
|
||||
z-index: 99;
|
||||
top: calc(50vh - 128px);
|
||||
left: calc(50vw - 128px);
|
||||
border: 16px solid transparent;
|
||||
border-top: 16px solid darkred;
|
||||
transform: translateZ(0);
|
||||
animation: loader 2s infinite ease-in-out;
|
||||
.compare .compare-label.b {
|
||||
right: 6px;
|
||||
}
|
||||
|
||||
@keyframes loader {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
50% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
/* Skeleton */
|
||||
/* Busy indicator while the worker renders a mutation */
|
||||
|
||||
.todo {
|
||||
opacity: 0.55;
|
||||
font-style: italic;
|
||||
border-left: 3px solid var(--color3);
|
||||
padding-left: 8px;
|
||||
.compare.busy::after {
|
||||
content: 'mutating…';
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
bottom: 8px;
|
||||
padding: 3px 9px;
|
||||
background: color-mix(in srgb, var(--background) 80%, transparent);
|
||||
color: var(--color1);
|
||||
font-size: 12px;
|
||||
animation: busy-pulse 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.distances.busy td {
|
||||
opacity: 0.35;
|
||||
}
|
||||
|
||||
@keyframes busy-pulse {
|
||||
50% { opacity: 0.35; }
|
||||
}
|
||||
|
||||
/* Hash visualisation */
|
||||
@@ -233,6 +272,14 @@ figure {
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.hash-value.small {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
letter-spacing: 1px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.bit-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 20px);
|
||||
@@ -248,6 +295,14 @@ figure {
|
||||
border: 1px solid var(--background);
|
||||
}
|
||||
|
||||
.bit-grid.small {
|
||||
grid-template-columns: repeat(8, 13px);
|
||||
}
|
||||
|
||||
.bit-grid.small .bit {
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
.bit-grid .bit.on {
|
||||
background: var(--foreground);
|
||||
}
|
||||
@@ -256,6 +311,20 @@ figure {
|
||||
background: color-mix(in srgb, var(--foreground) 12%, transparent);
|
||||
}
|
||||
|
||||
/* Changed bits keep their on/off brightness and get a red tint + border,
|
||||
so light-changed and dark-changed cells stay distinguishable */
|
||||
.bit-grid .bit.diff {
|
||||
border-color: hsl(4 70% 55%);
|
||||
}
|
||||
|
||||
.bit-grid .bit.on.diff {
|
||||
background: color-mix(in srgb, var(--foreground) 55%, hsl(4 70% 50%));
|
||||
}
|
||||
|
||||
.bit-grid .bit.off.diff {
|
||||
background: hsl(4 60% 50% / 0.25);
|
||||
}
|
||||
|
||||
.heat-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 32px);
|
||||
@@ -266,7 +335,15 @@ figure {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.pixelated, #image-resize img, #phash-resize img {
|
||||
.heat-grid.mini {
|
||||
grid-template-columns: repeat(8, 22px);
|
||||
}
|
||||
|
||||
.heat-grid.mini > div {
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.pixelated, #phash-resize img, .input-thumb img {
|
||||
image-rendering: pixelated;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
}
|
||||
@@ -276,71 +353,520 @@ figure {
|
||||
height: 128px;
|
||||
}
|
||||
|
||||
.sample {
|
||||
height: 48px;
|
||||
margin: 0 4px;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
.input-thumb img {
|
||||
width: 176px;
|
||||
height: 176px;
|
||||
}
|
||||
|
||||
/* Mutation compare */
|
||||
/* DCT extras */
|
||||
|
||||
.compare {
|
||||
.grid-overlay {
|
||||
position: relative;
|
||||
max-width: 640px;
|
||||
width: 256px;
|
||||
}
|
||||
|
||||
.compare img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
.grid-overlay.mini {
|
||||
width: 176px;
|
||||
}
|
||||
|
||||
.compare #compare-b {
|
||||
#dct-basis-wrap {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.basis-hover {
|
||||
display: none;
|
||||
position: absolute;
|
||||
outline: 2px solid var(--color1);
|
||||
outline-offset: -1px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#dct-basis {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
#dct-formula .formula-result {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#dct-formula.live .formula-result {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#dct-formula.live .var-u,
|
||||
#dct-formula.live .var-v,
|
||||
#dct-formula.live .formula-value {
|
||||
color: var(--color1);
|
||||
}
|
||||
|
||||
#image-dct .hover-cell {
|
||||
outline: 2px solid var(--color1);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
#dct-zigzag {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
clip-path: inset(0 0 0 50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#compare-slider, #mutator-amount {
|
||||
width: 320px;
|
||||
#dct-zigzag polyline {
|
||||
stroke: var(--color1);
|
||||
stroke-width: 3;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
#dct-zigzag polyline.play {
|
||||
transition: stroke-dashoffset 2.5s ease-in-out;
|
||||
}
|
||||
|
||||
#dct-basis {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
/* Reconstruction demos */
|
||||
|
||||
.recon-row {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
gap: 24px;
|
||||
align-items: flex-start;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.recon-row figure {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.recon-row figcaption {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.recon-row canvas {
|
||||
width: 176px;
|
||||
height: 176px;
|
||||
image-rendering: pixelated;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
}
|
||||
|
||||
#recon-k {
|
||||
width: 256px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.recon-note {
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
#recon-zigzag {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#recon-zigzag polyline {
|
||||
stroke-width: 2.5;
|
||||
stroke-linejoin: round;
|
||||
transition: stroke-dashoffset 0.12s linear;
|
||||
}
|
||||
|
||||
#recon-zigzag .zig-head {
|
||||
stroke: var(--color1);
|
||||
}
|
||||
|
||||
#recon-zigzag .zig-tail {
|
||||
stroke: color-mix(in srgb, var(--foreground) 45%, transparent);
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
|
||||
table.facts {
|
||||
border-collapse: collapse;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
table.facts th, table.facts td {
|
||||
text-align: left;
|
||||
padding: 4px 14px 4px 0;
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--foreground) 20%, transparent);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
table.facts th {
|
||||
color: var(--color1);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.distances td {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
min-width: 64px;
|
||||
}
|
||||
|
||||
.distances td.match {
|
||||
color: hsl(135 50% 45%);
|
||||
}
|
||||
|
||||
.distances td.nomatch {
|
||||
color: hsl(4 70% 55%);
|
||||
}
|
||||
|
||||
/* LSH demo */
|
||||
|
||||
.lsh-thumb img {
|
||||
height: 110px;
|
||||
margin-bottom: 6px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.lsh-distance {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.lsh-distance .match {
|
||||
font-size: 22px;
|
||||
color: hsl(135 50% 45%);
|
||||
}
|
||||
|
||||
.lsh-distance .nomatch {
|
||||
font-size: 22px;
|
||||
color: hsl(4 70% 55%);
|
||||
}
|
||||
|
||||
/* Flip demo */
|
||||
|
||||
#flip-stage img {
|
||||
max-width: 256px;
|
||||
max-height: 256px;
|
||||
transition: transform 0.7s ease-in-out;
|
||||
}
|
||||
|
||||
#flip-stage img.flipped {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
#ghost-canvas {
|
||||
transition: transform 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
#ghost-canvas.flipped {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.flip-table th {
|
||||
color: var(--color1);
|
||||
font-weight: normal;
|
||||
text-align: left;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.flip-table td {
|
||||
vertical-align: top;
|
||||
padding: 6px 12px 6px 0;
|
||||
}
|
||||
|
||||
.flip-distance {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.flip-distance.match {
|
||||
color: hsl(135 50% 45%);
|
||||
}
|
||||
|
||||
.flip-distance.nomatch {
|
||||
color: hsl(4 70% 55%);
|
||||
}
|
||||
|
||||
/* Ranking */
|
||||
|
||||
.thumb-grid {
|
||||
.pool {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
|
||||
gap: 4px;
|
||||
max-height: 330px;
|
||||
overflow-y: auto;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.pool img {
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
object-fit: cover;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pool img:hover {
|
||||
outline: 2px solid var(--color1);
|
||||
}
|
||||
|
||||
.pool img.query {
|
||||
outline: 3px solid var(--color1);
|
||||
}
|
||||
|
||||
.ranking-results h3 {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.thumb-row {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.thumb-grid figure {
|
||||
width: 96px;
|
||||
.thumb-row figure {
|
||||
width: 84px;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.thumb-grid figure.query {
|
||||
outline: 2px solid var(--color1);
|
||||
.thumb-row figure.hit {
|
||||
outline: 2px solid hsl(135 50% 45%);
|
||||
}
|
||||
|
||||
.thumb-grid img {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
.thumb-row img {
|
||||
width: 84px;
|
||||
height: 84px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.thumb-grid figcaption {
|
||||
.thumb-row figcaption {
|
||||
font-size: 11px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Precision-recall charts */
|
||||
|
||||
.pr-controls {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
#pr-legend {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
gap: 4px 14px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.pr-group {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.pr-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
border: 1px solid color-mix(in srgb, var(--foreground) 20%, transparent);
|
||||
background: none;
|
||||
color: var(--foreground);
|
||||
font-size: 11px;
|
||||
padding: 2px 7px;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.pr-chip.dim {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.pr-chip.pinned {
|
||||
border-color: var(--color1);
|
||||
}
|
||||
|
||||
.pr-slider {
|
||||
display: inline-block;
|
||||
margin: 4px 0;
|
||||
color: var(--color4);
|
||||
}
|
||||
|
||||
.pr-slider input {
|
||||
width: 220px;
|
||||
vertical-align: middle;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.pr-charts {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.pr-charts figure {
|
||||
margin: 0;
|
||||
flex: 1 1 300px;
|
||||
max-width: 340px;
|
||||
}
|
||||
|
||||
.pr-charts figcaption {
|
||||
color: var(--color1);
|
||||
font-size: 14px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.pr-svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
background: color-mix(in srgb, var(--foreground) 3%, transparent);
|
||||
}
|
||||
|
||||
.pr-grid {
|
||||
stroke: color-mix(in srgb, var(--foreground) 12%, transparent);
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.pr-tick {
|
||||
fill: color-mix(in srgb, var(--foreground) 55%, transparent);
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.pr-axis {
|
||||
fill: color-mix(in srgb, var(--foreground) 70%, transparent);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.pr-line {
|
||||
fill: none;
|
||||
stroke-width: 1.8;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.pr-dot {
|
||||
stroke: var(--background);
|
||||
stroke-width: 2;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.pr-line.faded, .pr-dot.faded {
|
||||
opacity: 0.12;
|
||||
}
|
||||
|
||||
#pr-tooltip {
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
background: color-mix(in srgb, var(--background) 92%, transparent);
|
||||
border: 1px solid var(--color1);
|
||||
color: var(--foreground);
|
||||
font-size: 12px;
|
||||
padding: 4px 8px;
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Retrieval demos */
|
||||
|
||||
.dist-figure, .toy-figure {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.dist-figure figcaption, .toy-figure figcaption {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.dist-svg {
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
.dist-bar {
|
||||
fill: color-mix(in srgb, var(--foreground) 45%, transparent);
|
||||
}
|
||||
|
||||
.dist-copy {
|
||||
fill: hsl(135 50% 45%);
|
||||
stroke: var(--background);
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.dist-region {
|
||||
fill: color-mix(in srgb, var(--color1) 10%, transparent);
|
||||
}
|
||||
|
||||
.dist-thresh {
|
||||
stroke: var(--color1);
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.dist-readout, .toy-stats {
|
||||
margin-left: 12px;
|
||||
color: var(--color4);
|
||||
}
|
||||
|
||||
.toy-node {
|
||||
fill: color-mix(in srgb, var(--foreground) 30%, transparent);
|
||||
transition: opacity 0.3s, fill 0.3s;
|
||||
}
|
||||
|
||||
.toy-node.seen {
|
||||
fill: color-mix(in srgb, var(--foreground) 10%, var(--background));
|
||||
stroke: var(--foreground);
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.toy-node.match {
|
||||
fill: hsl(135 50% 45%);
|
||||
}
|
||||
|
||||
.toy-node.pruned {
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
.toy-edge {
|
||||
stroke: color-mix(in srgb, var(--foreground) 45%, transparent);
|
||||
stroke-width: 1.5;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.toy-edge-label {
|
||||
fill: color-mix(in srgb, var(--foreground) 70%, transparent);
|
||||
font-size: 9px;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.toy-edge.pruned, .toy-edge-label.pruned {
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
.big-stats .stat-note {
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
margin: 0;
|
||||
max-width: 220px;
|
||||
}
|
||||
|
||||
.big-curve-svg {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.big-curve-line {
|
||||
stroke: var(--color2);
|
||||
}
|
||||
|
||||
.big-curve-dot {
|
||||
fill: var(--color1);
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
|
||||
ul.loose-ends li, ol.references li {
|
||||
margin-bottom: 6px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
ol.references li:target {
|
||||
outline: 1px dashed var(--color1);
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
+71
-5
@@ -1,15 +1,32 @@
|
||||
import init, { hash_all, resize_preview, dct_coefficients, dct_masks, phash_lowfreq, mutate } from './pkg/image_similarity.js';
|
||||
import init, { hash_all, resize_preview, dct_coefficients, dct_masks, phash_lowfreq, mutate, suite_hashes, suite_labels, BkIndex } from './pkg/image_similarity.js';
|
||||
|
||||
const ready = init();
|
||||
|
||||
// The retrieval demos keep their trees in wasm memory between calls:
|
||||
// a small one that gets visualized and the full experiment index.
|
||||
let toyIndex = null;
|
||||
let bigIndex = null;
|
||||
|
||||
// Average runtime of fn, sampled until enough wall time passed to
|
||||
// outrun the clamped performance.now resolution
|
||||
function time(fn) {
|
||||
const start = performance.now();
|
||||
let reps = 0;
|
||||
do {
|
||||
fn();
|
||||
reps++;
|
||||
} while (performance.now() - start < 20);
|
||||
return (performance.now() - start) / reps;
|
||||
}
|
||||
|
||||
onmessage = async (e) => {
|
||||
const { id, op, args } = e.data;
|
||||
await ready;
|
||||
try {
|
||||
const bytes = new Uint8Array(args.buffer);
|
||||
let result;
|
||||
switch (op) {
|
||||
case 'pipeline':
|
||||
case 'pipeline': {
|
||||
const bytes = new Uint8Array(args.buffer);
|
||||
result = {
|
||||
resize8: resize_preview(bytes, 8),
|
||||
resize32: resize_preview(bytes, 32),
|
||||
@@ -19,14 +36,63 @@ onmessage = async (e) => {
|
||||
hashes: hash_all(bytes),
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'hashes':
|
||||
result = { hashes: hash_all(bytes) };
|
||||
result = { hashes: hash_all(new Uint8Array(args.buffer)) };
|
||||
break;
|
||||
case 'mutate': {
|
||||
const png = mutate(bytes, args.kind, args.amount);
|
||||
const png = mutate(new Uint8Array(args.buffer), args.kind, args.amount);
|
||||
result = { png, hashes: hash_all(png) };
|
||||
break;
|
||||
}
|
||||
case 'suite':
|
||||
result = {
|
||||
hashes: suite_hashes(new Uint8Array(args.buffer)),
|
||||
labels: suite_labels().split('\n'),
|
||||
};
|
||||
break;
|
||||
case 'toy_build': {
|
||||
toyIndex?.free();
|
||||
toyIndex = new BkIndex();
|
||||
for (const key of args.keys) toyIndex.insert(key);
|
||||
result = { structure: toyIndex.structure(), size: toyIndex.len() };
|
||||
break;
|
||||
}
|
||||
case 'toy_query':
|
||||
result = {
|
||||
found: toyIndex.find(args.key, args.radius),
|
||||
visited: toyIndex.trace(args.key, args.radius),
|
||||
};
|
||||
break;
|
||||
case 'big_build': {
|
||||
bigIndex?.free();
|
||||
bigIndex = new BkIndex();
|
||||
bigIndex.insert_bytes(new Uint8Array(args.buffer));
|
||||
result = { size: bigIndex.len() };
|
||||
break;
|
||||
}
|
||||
case 'big_query': {
|
||||
const found = bigIndex.find(args.key, args.radius);
|
||||
result = {
|
||||
found,
|
||||
compared: bigIndex.find_compared(args.key, args.radius),
|
||||
treeMs: time(() => bigIndex.find(args.key, args.radius)),
|
||||
scanMs: time(() => bigIndex.scan(args.key, args.radius)),
|
||||
};
|
||||
break;
|
||||
}
|
||||
// compared counts and found counts for every radius up to max,
|
||||
// so the radius slider works without a round trip per step
|
||||
case 'big_profile': {
|
||||
const compared = [];
|
||||
const found = [];
|
||||
for (let t = 0; t <= args.max; t++) {
|
||||
compared.push(bigIndex.find_compared(args.key, t));
|
||||
found.push(bigIndex.find(args.key, t).length / 2);
|
||||
}
|
||||
result = { compared, found };
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(`unknown op ${op}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user