diff --git a/pages/reverse_search.tsx b/pages/reverse_search.tsx index 00014fa..7fab006 100644 --- a/pages/reverse_search.tsx +++ b/pages/reverse_search.tsx @@ -22,30 +22,33 @@ export default function ReverseSearch() { const router = useRouter() const [Files, setFiles] = useState([]); const [open, setOpen] = useState(false); - const send_image = (token: string) => { + const send_image = (token: string,mode:string) => { setOpen(true) const formData = new FormData(); formData.append("image", Files[0]); formData.append("g-recaptcha-response", token); + formData.append("mode", mode); axios(`/reverse_search`, { method: "post", data: formData, headers: { 'Content-Type': 'multipart/form-data' - } + }, + timeout:120000 //2min }).then((resp) => { setOpen(false) + console.log(resp.data.ids) router.push("/show?ids=" + resp.data.ids) }).catch((err) => { setOpen(false) console.log(err) }) } - const _send_image = () => { + const _send_image = (mode:string) => { /*global grecaptcha*/ // defined in pages/_document.tsx grecaptcha.ready(function () { grecaptcha.execute(config.recaptcha_site_key, { action: 'login' }).then(function (token) { - send_image(token) + send_image(token,mode) }); }) } @@ -64,7 +67,9 @@ export default function ReverseSearch() { onChange={(files) => setFiles((files as never))} /> - + +
+ ); } \ No newline at end of file diff --git a/server/bulk_calculate_orb_features/calculate_orb_features.ts b/server/bulk_calculate_orb_features/calculate_orb_features.ts new file mode 100644 index 0000000..c5f89bd --- /dev/null +++ b/server/bulk_calculate_orb_features/calculate_orb_features.ts @@ -0,0 +1,36 @@ +import * as cv from 'opencv4nodejs' +import path from 'path'; +import db_ops from '../helpers/db_ops' +import config from '../../config/config' +const detector=new cv.ORBDetector() +const PATH_TO_IMAGES = path.join(config.root_path, 'public', 'images') + +async function calculate_orb_features(){ + const images = await db_ops.image_ops.get_all_images() + for(const image of images){ + const check_if_already_calculated= await db_ops.image_search.get_orb_features_by_id(image.id) + if(check_if_already_calculated.length!==0){ + continue + } + console.log(image.id) + try{ + const img = await cv.imreadAsync(`${PATH_TO_IMAGES}/${image.id}.${image.file_ext}`); + const keyPoints = await detector.detectAsync(img); + const descriptors = await detector.computeAsync(img, keyPoints); + const descriptors_as_array=descriptors.getDataAsArray() + descriptors.release() + img.release() + await db_ops.image_search.add_orb_features_by_id(image.id,descriptors_as_array) + }catch(err){ + console.log(err) + console.log(image.id) + } + + } +} + +async function run() { + await calculate_orb_features() + process.exit() +} +run() \ No newline at end of file diff --git a/server/bulk_calculate_orb_features/delete_orb_features.ts b/server/bulk_calculate_orb_features/delete_orb_features.ts new file mode 100644 index 0000000..8804873 --- /dev/null +++ b/server/bulk_calculate_orb_features/delete_orb_features.ts @@ -0,0 +1,11 @@ +import db_ops from '../helpers/db_ops' + +async function delete_orb_feature_by_id() { +// const color_hist_image_ids=[] + const similarities_image_ids=[-1] + for (const id of similarities_image_ids){ + await db_ops.image_search.delete_orb_feature_by_id(id) + } + process.exit() +} +delete_orb_feature_by_id() \ No newline at end of file diff --git a/server/bulk_calculate_sift_features/calculate_sift_features.ts b/server/bulk_calculate_sift_features/calculate_sift_features.ts deleted file mode 100644 index 69e03d6..0000000 --- a/server/bulk_calculate_sift_features/calculate_sift_features.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as cv from 'opencv4nodejs' -import path from 'path'; -import db_ops from '../helpers/db_ops' -import config from '../../config/config' -const detector=new cv.SIFTDetector({ nFeatures: 400 }) -const PATH_TO_IMAGES = path.join(config.root_path, 'public', 'images') - -async function calculate_color_hist(){ - const images = await db_ops.image_ops.get_all_images() - for(const image of images){ - const check_if_already_calculated= await db_ops.image_search.get_sift_features_by_id(image.id) - if(check_if_already_calculated.length!==0){ - continue - } - console.log(image.id) - const img = await cv.imreadAsync(`${PATH_TO_IMAGES}/${image.id}.${image.file_ext}`); - const keyPoints = await detector.detectAsync(img); - const descriptors = await detector.computeAsync(img, keyPoints); - descriptors.release() - img.release() - const descriptors_as_array=descriptors.getDataAsArray() - await db_ops.image_search.add_sift_features_by_id(image.id,descriptors_as_array) - } -} - -async function run() { - await calculate_color_hist() -} -run() \ No newline at end of file diff --git a/server/helpers/db_ops.ts b/server/helpers/db_ops.ts index 47de447..8c09e56 100644 --- a/server/helpers/db_ops.ts +++ b/server/helpers/db_ops.ts @@ -91,6 +91,36 @@ async function generate_id() { } /////////////////////////////////////////////////IMAGE SEARCH OPS +async function delete_orb_feature_by_id(id:number){ + removeDocument("orb_reverse_search",{id:id}) +} + +async function add_orb_features_by_id(id:number,orb_features:Array){ + insertDocuments("orb_reverse_search", [{ + id:id, + orb_features:orb_features + }]) +} + +async function get_orb_features_batch(skip:number,limit:number){ + const collection = client.db(db_main).collection("orb_reverse_search"); + const similarities = collection.find().skip(skip).limit(limit).project({_id:0}).toArray() + return similarities +} + + +async function get_orb_features_by_id(id:number){ + const collection = client.db(db_main).collection("orb_reverse_search"); + const similarities = collection.find({id:id}).project({_id:0,id:0}).toArray() + return similarities +} +async function get_number_of_images_orb_reverse_search(){ + const collection = client.db(db_main).collection("orb_reverse_search"); + const number_of_images_in_collection = collection.countDocuments() + return number_of_images_in_collection +} + + async function get_color_similarities_by_id(id:number){ const collection = client.db(db_main).collection("color_similarities"); // collection.find(selector).project({_id:0}).explain((_err,exp)=>console.log(exp)) @@ -364,6 +394,11 @@ export default { add_tags_to_image_by_id }, image_search:{ + delete_orb_feature_by_id, + add_orb_features_by_id, + get_orb_features_batch, + get_orb_features_by_id, + get_number_of_images_orb_reverse_search, get_all_color_hists, get_color_hist_by_id, add_color_hist_by_id, diff --git a/server/helpers/image_ops.ts b/server/helpers/image_ops.ts index 2d1c760..cb9bd1e 100644 --- a/server/helpers/image_ops.ts +++ b/server/helpers/image_ops.ts @@ -1,6 +1,12 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ import * as cv from 'opencv4nodejs' import { HistAxes } from 'opencv4nodejs'; import db_ops from './db_ops'; + +const detector=new cv.ORBDetector() +const matchFunc=cv.matchBruteForceHammingAsync +const imghash = require('imghash'); + const BIN_SIZE=16 const histAxes:HistAxes[]= [ new HistAxes({ @@ -19,6 +25,7 @@ const histAxes:HistAxes[]= [ ranges: [0, 255] }), ] + async function calculate_color_hist_and_similarities(new_image_id:number,image:Buffer){ const img_mat = await cv.imdecodeAsync(image) let rgb_hist = await cv.calcHistAsync(img_mat, histAxes) @@ -39,4 +46,58 @@ const histAxes:HistAxes[]= [ } await db_ops.image_search.add_color_similarities_by_id(new_image_id,similarities) } -export default {calculate_color_hist_and_similarities} \ No newline at end of file +async function get_similar_images_by_orb(image:Buffer) { + const img_mat = await cv.imdecodeAsync(image) + const keyPoints = await detector.detectAsync(img_mat); + const img_descriptors = await detector.computeAsync(img_mat, keyPoints); + const number_of_images = await db_ops.image_search.get_number_of_images_orb_reverse_search() + const batch = 500; + const similar_images=[] + console.time() + for (let i = 0; i < number_of_images; i += batch) { + const descriptors = await db_ops.image_search.get_orb_features_batch(i, batch) + for (const img of descriptors) { + const descriptors2 = new cv.Mat(img.orb_features, cv.CV_8UC1) + const matches = await matchFunc(img_descriptors, descriptors2); + descriptors2.release() + let sum = 0 + for (const x of matches) { + sum += x.distance + } + if (sum===0){ + return [img.id] + } + similar_images.push({id:img.id,avg_distance:sum / matches.length}) + } + } + console.timeEnd() + similar_images.sort((a,b)=>a.avg_distance-b.avg_distance) + similar_images.length=30 + const ids=similar_images.map((el)=>el.id) + return ids +} +function hamming_distance(str1: string, str2: string) { + let distance = 0; + for (let i = 0; i < str1.length; i += 1) { + if (str1[i] !== str2[i]) { + distance += 1; + } + } + return distance; +} + +async function get_similar_images_by_phash(image:Buffer){ + const phash= await imghash.hash(image,16) + const images=await db_ops.image_ops.get_ids_and_phashes() + for(let i=0;ia.dist-b.dist) + images.length=30 + const ids=images.map((el)=>el.id) + return ids +} +export default {calculate_color_hist_and_similarities,get_similar_images_by_orb,get_similar_images_by_phash} \ No newline at end of file diff --git a/server/routes/reverse_search.ts b/server/routes/reverse_search.ts index 3efe4aa..3ef3e99 100644 --- a/server/routes/reverse_search.ts +++ b/server/routes/reverse_search.ts @@ -3,17 +3,8 @@ // import db_ops from './../helpers/db_ops' import { Request, Response } from 'express'; import { RecaptchaResponseV3 } from 'express-recaptcha/dist/interfaces'; -import db_ops from '../helpers/db_ops' -const imghash: any = require('imghash'); -function hamming_distance(str1: string, str2: string) { - let distance = 0; - for (let i = 0; i < str1.length; i += 1) { - if (str1[i] !== str2[i]) { - distance += 1; - } - } - return distance; -} +import image_ops from '../helpers/image_ops' + async function reverse_search(req: Request, res: Response) { const recaptcha_score=(req.recaptcha as RecaptchaResponseV3)?.data?.score if (req.recaptcha?.error|| (typeof recaptcha_score==="number" && recaptcha_score<0.5)) { @@ -21,15 +12,21 @@ async function reverse_search(req: Request, res: Response) { message: "Captcha error" }); } - const phash= await imghash.hash(req.file.buffer,16) - const images=await db_ops.image_ops.get_ids_and_phashes() - for(let i=0;ia.dist-b.dist) - images.length=30 - const ids=images.map((el)=>el.id) - res.json({ids:ids.join(',')}) } export default reverse_search; \ No newline at end of file