Skip to content
Snippets Groups Projects
Verified Commit 4ecc0cb7 authored by Rasmus Thomsen's avatar Rasmus Thomsen
Browse files

backend: add API endpoint for comparing images

parent e27d1e72
No related branches found
No related tags found
No related merge requests found
Pipeline #3444 passed
web: deno run --allow-net src/index.ts --port=${PORT} web: deno run --allow-write --allow-read --allow-net src/index.ts --port=${PORT}
import {
readAll,
readerFromStreamReader,
} from "https://deno.land/std@0.95.0/io/mod.ts";
import { decode, Image } from "https://deno.land/x/jpegts@1.1/mod.ts";
import { resize } from "https://deno.land/x/deno_image@v0.0.3/mod.ts";
export const compareMunicipalRibbon = async (
ribbonUrl: string,
imageToCompareTo: Image,
): Promise<number> => {
const ribbon = (await fetch(ribbonUrl))?.body?.getReader();
if (ribbon) {
const reader = readerFromStreamReader(ribbon);
const rawImage = await readAll(reader);
const picture = decode(
await resize(rawImage, {
height: imageToCompareTo.height,
width: imageToCompareTo.width,
aspectRatio: false,
}),
);
return compareImages(picture, imageToCompareTo);
}
throw new Error("Couldn't fetch remote Image!");
};
const compareImages = (
firstImage: Image,
secondImage: Image,
): number => {
if (
firstImage.width != secondImage.width ||
firstImage.height != secondImage.height
) {
throw new Error(
"Can't compare images of different sizes. " + firstImage.width + "x" +
firstImage.height + " vs " + secondImage.width + "x" +
secondImage.height,
);
}
let diff = 0;
for (let i = 0; i < firstImage.data.length / 5; i++) {
diff += Math.abs(firstImage.data[4 * i + 0] - secondImage.data[4 * i + 0]) /
255;
diff += Math.abs(firstImage.data[4 * i + 1] - secondImage.data[4 * i + 1]) /
255;
diff += Math.abs(firstImage.data[4 * i + 2] - secondImage.data[4 * i + 2]) /
255;
}
return 100 - 100 * diff / (firstImage.width * firstImage.height * 3);
};
import { Application, Router } from "https://deno.land/x/oak@v7.3.0/mod.ts"; import { Application, Router } from "https://deno.land/x/oak@v7.3.0/mod.ts";
import { parse } from "https://deno.land/std@0.95.0/flags/mod.ts"; import { parse } from "https://deno.land/std@0.95.0/flags/mod.ts";
import logger from "https://deno.land/x/oak_logger@1.0.0/mod.ts";
import { getAllRibbons, getRibbon, Ribbon } from "./ribbon.ts"; import { compareRibbon, getAllRibbons, getRibbon, Ribbon } from "./ribbon.ts";
const { args } = Deno; const { args } = Deno;
const DEFAULT_PORT = 8000; const DEFAULT_PORT = 8000;
...@@ -14,10 +15,17 @@ const state: Ribbon[] = await (await fetch( ...@@ -14,10 +15,17 @@ const state: Ribbon[] = await (await fetch(
const app = new Application({ state }); const app = new Application({ state });
app.use(logger.logger);
app.use(logger.responseTime);
const router = new Router(); const router = new Router();
router.get("/ribbons", getAllRibbons); router.get("/ribbons", getAllRibbons);
router.get("/ribbons/:municipality", getRibbon); router.get("/ribbons/:municipality", getRibbon);
router.post(
"/ribbons/compare/:municipality",
compareRibbon,
);
app.use(router.routes()); app.use(router.routes());
......
import { helpers, RouterContext } from "https://deno.land/x/oak@v7.3.0/mod.ts"; import { helpers, RouterContext } from "https://deno.land/x/oak@v7.3.0/mod.ts";
import { compareMunicipalRibbon } from "./image.ts";
import { decode, Image } from "https://deno.land/x/jpegts@1.1/mod.ts";
export interface Ribbon { export interface Ribbon {
acceptance: string; acceptance: string;
...@@ -42,6 +44,7 @@ export const getAllRibbons = (ctx: RouterContext) => { ...@@ -42,6 +44,7 @@ export const getAllRibbons = (ctx: RouterContext) => {
export const getRibbon = ({ params, response, state }: RouterContext) => { export const getRibbon = ({ params, response, state }: RouterContext) => {
if (params?.municipality === undefined) { if (params?.municipality === undefined) {
response.status = 400; response.status = 400;
return;
} }
const municipality = params?.municipality; const municipality = params?.municipality;
const ribbon = state.find( const ribbon = state.find(
...@@ -56,3 +59,59 @@ export const getRibbon = ({ params, response, state }: RouterContext) => { ...@@ -56,3 +59,59 @@ export const getRibbon = ({ params, response, state }: RouterContext) => {
response.status = 200; response.status = 200;
response.body = ribbon; response.body = ribbon;
}; };
export const compareRibbon = async (
{ params, request, response, state }: RouterContext,
) => {
if (params?.municipality === undefined) {
response.status = 400;
return;
}
const municipality = params?.municipality;
const ribbon: Ribbon = state.find(
(ribbon: Ribbon) => ribbon.municipality.endsWith(`/${municipality}`),
);
if (ribbon === undefined) {
response.status = 404;
response.body = "Couldn't find municipality " + municipality;
return;
}
const { value } = request.body({ type: "form-data" });
const { files } = await value.read();
if (!files) {
response.status = 400;
return;
}
const filename = files[0].filename;
if (!filename) {
response.status = 400;
return;
}
let img: Image | undefined = undefined;
try {
img = decode(
await Deno.readFile(filename),
);
} catch (e) {
response.status = 500;
response.body = "" + e;
return;
}
let matchPercentage = 0;
try {
matchPercentage = await compareMunicipalRibbon(
ribbon.img,
img,
);
} catch (e) {
response.status = 500;
response.body = "" + e;
return;
}
response.body = { matchPercentage: matchPercentage };
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment