Esteves Enzo commited on
Commit
96d9078
1 Parent(s): 68b3088

update style + method to send file

Browse files
app/api/check-hair-color/route.ts CHANGED
@@ -1,22 +1,18 @@
1
  import fs from 'fs';
2
 
3
  import PipelineSingleton from './pipeline';
 
4
 
5
  export async function POST(request: Request) {
6
  const res = await request.formData();
7
- const file = res.get('file') as File;
8
 
9
  const classifier: any = await PipelineSingleton.getInstance();
10
  if (!classifier) {
11
  return Response.json({ success: false })
12
  }
13
-
14
- // convert file object to buffer
15
- const fileBuffer = await file.arrayBuffer();
16
- // convert buffer to url data
17
- const fileData = Buffer.from(fileBuffer).toString('base64');
18
- const fileUrl = `data:${file.type};base64,${fileData}`;
19
- console.log(fileUrl);
20
- const result = await classifier(fileUrl, { topk: 4 });
21
  return Response.json({ data: result })
22
  }
 
1
  import fs from 'fs';
2
 
3
  import PipelineSingleton from './pipeline';
4
+ import { RawImage } from '@xenova/transformers';
5
 
6
  export async function POST(request: Request) {
7
  const res = await request.formData();
8
+ const file = res.get('file') as Blob;
9
 
10
  const classifier: any = await PipelineSingleton.getInstance();
11
  if (!classifier) {
12
  return Response.json({ success: false })
13
  }
14
+ // read blob as data url
15
+ const file_url = await RawImage.fromBlob(file)
16
+ const result = await classifier(file_url, { topk: 4 });
 
 
 
 
 
17
  return Response.json({ data: result })
18
  }
components/form/hook.ts CHANGED
@@ -9,7 +9,9 @@ export const useClassifier = () => {
9
  setLoading(true);
10
  const formData = new FormData();
11
 
12
- formData.append("file", file);
 
 
13
  const res = await fetch("/api/check-hair-color", {
14
  method: "POST",
15
  body: formData,
@@ -20,9 +22,12 @@ export const useClassifier = () => {
20
  setLoading(false);
21
  }
22
 
 
 
23
  return {
24
  results,
25
  loading,
26
- submit
 
27
  }
28
  }
 
9
  setLoading(true);
10
  const formData = new FormData();
11
 
12
+ const fileToBlob = new Blob([file], { type: file.type });
13
+
14
+ formData.append("file", fileToBlob);
15
  const res = await fetch("/api/check-hair-color", {
16
  method: "POST",
17
  body: formData,
 
22
  setLoading(false);
23
  }
24
 
25
+ const reset = () => setResults([]);
26
+
27
  return {
28
  results,
29
  loading,
30
+ submit,
31
+ reset
32
  }
33
  }
components/form/index.tsx CHANGED
@@ -1,5 +1,5 @@
1
  "use client";
2
- import React from "react";
3
  import { useRef } from "react";
4
  import { TiUpload, TiTrash } from "react-icons/ti";
5
 
@@ -10,7 +10,7 @@ import { useClassifier } from "./hook";
10
  export const FormUpload = () => {
11
  const inputRef = useRef<HTMLInputElement>(null);
12
  const [file, setFile] = React.useState<File | null>(null);
13
- const { loading, results, submit } = useClassifier();
14
 
15
  const handleUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
16
  const file = e.target.files?.[0];
@@ -19,6 +19,8 @@ export const FormUpload = () => {
19
  }
20
  };
21
 
 
 
22
  return (
23
  <>
24
  <div className="w-full">
@@ -32,7 +34,7 @@ export const FormUpload = () => {
32
  <div
33
  className="w-full h-full bg-slate-900 mx-auto rounded-xl relative bg-cover bg-center flex items-start justify-end p-3 gap-2"
34
  style={{
35
- backgroundImage: `url(${URL.createObjectURL(file)})`,
36
  }}
37
  >
38
  <div
@@ -41,6 +43,7 @@ export const FormUpload = () => {
41
  e.stopPropagation();
42
  e.preventDefault();
43
  setFile(null);
 
44
  }}
45
  >
46
  <TiTrash className="w-6 h-6 text-slate-700 group-hover:text-red-600 mx-auto" />
 
1
  "use client";
2
+ import React, { useMemo } from "react";
3
  import { useRef } from "react";
4
  import { TiUpload, TiTrash } from "react-icons/ti";
5
 
 
10
  export const FormUpload = () => {
11
  const inputRef = useRef<HTMLInputElement>(null);
12
  const [file, setFile] = React.useState<File | null>(null);
13
+ const { loading, results, submit, reset } = useClassifier();
14
 
15
  const handleUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
16
  const file = e.target.files?.[0];
 
19
  }
20
  };
21
 
22
+ const fileUrl = useMemo(() => file && URL.createObjectURL(file), [file]);
23
+
24
  return (
25
  <>
26
  <div className="w-full">
 
34
  <div
35
  className="w-full h-full bg-slate-900 mx-auto rounded-xl relative bg-cover bg-center flex items-start justify-end p-3 gap-2"
36
  style={{
37
+ backgroundImage: `url(${fileUrl})`,
38
  }}
39
  >
40
  <div
 
43
  e.stopPropagation();
44
  e.preventDefault();
45
  setFile(null);
46
+ reset();
47
  }}
48
  >
49
  <TiTrash className="w-6 h-6 text-slate-700 group-hover:text-red-600 mx-auto" />
components/form/main_color.tsx ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useMemo } from "react";
2
+
3
+ import { ResultsInterface } from "./results";
4
+
5
+ export const MainColor = ({ result }: { result: ResultsInterface }) => {
6
+ const mainColor = useMemo(() => {
7
+ switch (result.label) {
8
+ case "black hair":
9
+ return "#000000";
10
+ case "blond hair":
11
+ return "#ffcc00";
12
+ case "brown hair":
13
+ return "#663300";
14
+ case "white hair":
15
+ return "#cccccc";
16
+ case "red hair":
17
+ return "#ff6600";
18
+ case "completely bald":
19
+ return "transparent";
20
+ }
21
+ }, [result]);
22
+
23
+ return (
24
+ <div className="flex items-center justify-between">
25
+ <p className="text-slate-400 text-sm font-semibold capitalize">
26
+ {result.label}
27
+ </p>
28
+ <div
29
+ className="w-[20px] h-[20px] rounded-full border border-slate-950 ring-[1px] ring-slate-400"
30
+ style={{
31
+ backgroundColor: mainColor,
32
+ }}
33
+ />
34
+ </div>
35
+ );
36
+ };
components/form/progress.tsx ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ResultsInterface } from "./results";
2
+
3
+ export const Progress = ({ result }: { result: ResultsInterface }) => {
4
+ return (
5
+ <div key={result.label}>
6
+ <div className="flex items-center justify-between border-b border-dashed border-slate-500/20 pb-1">
7
+ <p className="text-slate-400 font-medium text-xs">{result.label}</p>
8
+ <p className="text-slate-300 font-bold text-xs">
9
+ {(result.score * 100).toFixed(3)}%
10
+ </p>
11
+ </div>
12
+ <div className="w-full h-1.5 rounded-full bg-slate-700 mt-2">
13
+ <div
14
+ className="bg-gradient-to-r from-indigo-600 to-teal-500 h-full blur-[1px] rounded-full"
15
+ style={{
16
+ width: `${result.score * 100}%`,
17
+ }}
18
+ ></div>
19
+ </div>
20
+ </div>
21
+ );
22
+ };
components/form/results.tsx CHANGED
@@ -1,12 +1,21 @@
1
  import Image from "next/image";
 
 
 
2
 
3
  import HFSad from "@/assets/hfsad.svg";
4
- interface ResultsInterface {
 
 
 
 
5
  label: string;
6
  score: number;
7
  }
8
 
9
  export const Results = ({ results }: { results: ResultsInterface[] }) => {
 
 
10
  return (
11
  <div className="w-full flex-col flex gap-5">
12
  {results?.length > 0 ? (
@@ -14,25 +23,12 @@ export const Results = ({ results }: { results: ResultsInterface[] }) => {
14
  <p className="text-white font-bold text-xl text-center uppercase tracking-widest">
15
  Results
16
  </p>
17
- {results?.map((result) => (
18
- <div key={result.label}>
19
- <div className="flex items-center justify-between border-b border-dashed border-slate-500/20 pb-1">
20
- <p className="text-slate-400 font-medium text-xs">
21
- {result.label}
22
- </p>
23
- <p className="text-slate-300 font-bold text-xs">
24
- {(result.score * 100).toFixed(3)}%
25
- </p>
26
- </div>
27
- <div className="w-full h-1.5 rounded-full bg-slate-700 mt-2">
28
- <div
29
- className="bg-gradient-to-r from-indigo-600 to-teal-500 h-full blur-[1px] rounded-full"
30
- style={{
31
- width: `${result.score * 100}%`,
32
- }}
33
- ></div>
34
- </div>
35
- </div>
36
  ))}
37
  </>
38
  ) : (
 
1
  import Image from "next/image";
2
+ import { useMemo, useState } from "react";
3
+ import { TbChevronDown } from "react-icons/tb";
4
+ import classNames from "classnames";
5
 
6
  import HFSad from "@/assets/hfsad.svg";
7
+
8
+ import { Progress } from "./progress";
9
+ import { MainColor } from "./main_color";
10
+
11
+ export interface ResultsInterface {
12
  label: string;
13
  score: number;
14
  }
15
 
16
  export const Results = ({ results }: { results: ResultsInterface[] }) => {
17
+ const primaryResult = useMemo(() => results[0], [results]);
18
+
19
  return (
20
  <div className="w-full flex-col flex gap-5">
21
  {results?.length > 0 ? (
 
23
  <p className="text-white font-bold text-xl text-center uppercase tracking-widest">
24
  Results
25
  </p>
26
+ <MainColor result={primaryResult} />
27
+ <p className="text-slate-600 text-left uppercase text-xs font-semibold">
28
+ Details
29
+ </p>
30
+ {results?.slice(1, results.length)?.map((result) => (
31
+ <Progress result={result} key={result.label} />
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  ))}
33
  </>
34
  ) : (