qw-gallery-ambience/server/helpers/image_ops.ts

446 lines
17 KiB
TypeScript

import config from "../../config/config"
import FormData from 'form-data'
import axios from 'axios'
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
async function calculate_phash_features(image_id: number, image: Buffer) {
const form = new FormData()
form.append('image', image, { filename: 'document' }) //hack to make nodejs buffer work with form-data
form.append('image_id', image_id.toString())
const status = await axios.post(`${config.phash_microservice_url}/calculate_phash_features`, form.getBuffer(), {
maxContentLength: Infinity,
maxBodyLength: Infinity,
headers: {
...form.getHeaders()
}
})
return status.data
}
async function calculate_local_features(image_id: number, image: Buffer) {
const form = new FormData()
form.append('image', image, { filename: 'document' }) //hack to make nodejs buffer work with form-data
form.append('image_id', image_id.toString())
const status = await axios.post(`${config.local_features_microservice_url}/calculate_local_features`, form.getBuffer(), {
maxContentLength: Infinity,
maxBodyLength: Infinity,
headers: {
...form.getHeaders()
}
})
return status.data
}
async function calculate_global_features(image_id: number, image: Buffer) {
const form = new FormData()
form.append('image', image, { filename: 'document' }) //hack to make nodejs buffer work with form-data
form.append('image_id', image_id.toString())
const status = await axios.post(`${config.global_features_microservice_url}/calculate_global_features`, form.getBuffer(), {
maxContentLength: Infinity,
maxBodyLength: Infinity,
headers: {
...form.getHeaders()
}
})
return status.data
}
async function calculate_image_text_features(image_id: number, image: Buffer) {
const form = new FormData()
form.append('image', image, { filename: 'document' }) //hack to make nodejs buffer work with form-data
form.append('image_id', image_id.toString())
const status = await axios.post(`${config.image_text_features_microservice_url}/calculate_image_text_features`, form.getBuffer(), {
maxContentLength: Infinity,
maxBodyLength: Infinity,
headers: {
...form.getHeaders()
}
})
return status.data
}
async function calculate_color_features(image_id: number, image: Buffer) {
const form = new FormData()
form.append('image', image, { filename: 'document' }) //hack to make nodejs buffer work with form-data
form.append('image_id', image_id.toString())
const status = await axios.post(`${config.color_microservice_url}/calculate_color_features`, form.getBuffer(), {
maxContentLength: Infinity,
maxBodyLength: Infinity,
headers: {
...form.getHeaders()
}
})
return status.data
}
// async function calculate_text_features(image_id: number, image: Buffer) {
// const form = new FormData()
// form.append('image', image, { filename: 'document' }) //hack to make nodejs buffer work with form-data
// form.append('image_id', image_id.toString())
// const status = await axios.post(`${config.text_microservice_url}/calculate_text_features`, form.getBuffer(), {
// maxContentLength: Infinity,
// maxBodyLength: Infinity,
// headers: {
// ...form.getHeaders()
// }
// })
// return status.data
// }
interface ImageSearchProps {
image: Buffer,
k?: number,
distance_threshold?: number
k_clusters?: number
knn_min_matches?: number
matching_threshold?: number
aqe_n?: number
aqe_alpha?: number
use_smnn_matching?: number
smnn_match_threshold?: number
use_ransac?: number
}
async function phash_get_similar_images_by_image_buffer({ image, k, distance_threshold }: ImageSearchProps) {
try {
const form = new FormData()
if (k) {
form.append('k', k.toString())
} else if (distance_threshold) {
form.append('distance_threshold', distance_threshold.toString())
}
form.append('image', image, { filename: 'document' }) //hack to make nodejs buffer work with form-data
const res = await axios.post(`${config.phash_microservice_url}/phash_get_similar_images_by_image_buffer`, form.getBuffer(), {
maxContentLength: Infinity,
maxBodyLength: Infinity,
headers: {
...form.getHeaders()
}
})
return res.data
} catch (err) {
console.log(err)
return []
}
}
async function global_features_get_similar_images_by_image_buffer({ image, k, distance_threshold, aqe_n, aqe_alpha }: ImageSearchProps) {
try {
const form = new FormData()
if (k) {
form.append('k', k.toString())
} else if (distance_threshold) {
form.append('distance_threshold', distance_threshold.toString())
}
if (aqe_n) {
form.append('aqe_n', aqe_n.toString())
}
if (aqe_alpha) {
form.append('aqe_alpha', aqe_alpha.toString())
}
form.append('image', image, { filename: 'document' }) //hack to make nodejs buffer work with form-data
const res = await axios.post(`${config.global_features_microservice_url}/global_features_get_similar_images_by_image_buffer`, form.getBuffer(), {
maxContentLength: Infinity,
maxBodyLength: Infinity,
headers: {
...form.getHeaders()
}
})
return res.data
} catch (err) {
console.log(err)
return []
}
}
async function image_text_features_get_similar_images_by_image_buffer({ image, k, distance_threshold, aqe_n, aqe_alpha }: ImageSearchProps) {
try {
const form = new FormData()
if (k) {
form.append('k', k.toString())
} else if (distance_threshold) {
form.append('distance_threshold', distance_threshold.toString())
}
if (aqe_n) {
form.append('aqe_n', aqe_n.toString())
}
if (aqe_alpha) {
form.append('aqe_alpha', aqe_alpha.toString())
}
form.append('image', image, { filename: 'document' }) //hack to make nodejs buffer work with form-data
const res = await axios.post(`${config.image_text_features_microservice_url}/image_text_features_get_similar_images_by_image_buffer`, form.getBuffer(), {
maxContentLength: Infinity,
maxBodyLength: Infinity,
headers: {
...form.getHeaders()
}
})
return res.data
} catch (err) {
console.log(err)
return []
}
}
async function color_get_similar_images_by_image_buffer({ image, k, distance_threshold }: ImageSearchProps) {
try {
const form = new FormData()
if (k) {
form.append('k', k.toString())
} else if (distance_threshold) {
form.append('distance_threshold', distance_threshold.toString())
}
form.append('image', image, { filename: 'document' }) //hack to make nodejs buffer work with form-data
const res = await axios.post(`${config.color_microservice_url}/color_get_similar_images_by_image_buffer`, form.getBuffer(), {
maxContentLength: Infinity,
maxBodyLength: Infinity,
headers: {
...form.getHeaders()
}
})
return res.data
} catch (err) {
console.log(err)
return []
}
}
async function local_features_get_similar_images_by_image_buffer({ image,
k, k_clusters, knn_min_matches, matching_threshold, use_smnn_matching, smnn_match_threshold, use_ransac }: ImageSearchProps) {
try {
const form = new FormData()
if (k) {
form.append('k', k.toString())
}
if (k_clusters) {
form.append('k_clusters', k_clusters.toString())
}
if (knn_min_matches) {
form.append('knn_min_matches', knn_min_matches.toString())
}
if (matching_threshold) {
form.append('matching_threshold', matching_threshold.toString())
}
if (use_smnn_matching) {
form.append('use_smnn_matching', use_smnn_matching.toString())
}
if (smnn_match_threshold) {
form.append('smnn_match_threshold', smnn_match_threshold.toString())
}
if (use_ransac) {
form.append('use_ransac', use_ransac.toString())
}
form.append('image', image, { filename: 'document' }) //hack to make nodejs buffer work with form-data
const res = await axios.post(`${config.local_features_microservice_url}/local_features_get_similar_images_by_image_buffer`, form.getBuffer(), {
maxContentLength: Infinity,
maxBodyLength: Infinity,
headers: {
...form.getHeaders()
}
})
return res.data
} catch (err) {
console.log(err)
return []
}
}
// async function text_get_similar_images_by_image_buffer({image,k,distance_threshold}:ImageSearchProps) {
// try {
// const form = new FormData()
// if(k){
// form.append('k', k.toString())
// }else if(distance_threshold){
// form.append('distance_threshold', distance_threshold.toString())
// }
// form.append('image', image, { filename: 'document' }) //hack to make nodejs buffer work with form-data
// const res = await axios.post(`${config.text_microservice_url}/text_get_similar_images_by_image_buffer`, form.getBuffer(), {
// maxContentLength: Infinity,
// maxBodyLength: Infinity,
// headers: {
// ...form.getHeaders()
// }
// })
// return res.data
// } catch (err) {
// console.log(err)
// return []
// }
// }
async function get_similar_images(image: Buffer, find_duplicate:boolean) {
let phash_res:any = []
let global_features_res:any = []
let local_features_res:any = []
let color_res:any = []
let image_text_res:any = [];
// const text_res = await text_get_similar_images_by_image_buffer({image:image,k:200})
if(find_duplicate){
// local_features_res = (await local_features_get_similar_images_by_image_buffer({
// image: image, k: 5, k_clusters: 15,
// knn_min_matches: 4, matching_threshold: 0.8,
// use_smnn_matching: 1, use_ransac: 1
// })).filter((el:any)=>el["matches"]>=8)
let res = await phash_get_similar_images_by_image_buffer({ image: image, distance_threshold: 64 })
if (res.length>=1){
return {duplicate:res}
}
else{
return {}
}
}else{
[phash_res, global_features_res,local_features_res,color_res,image_text_res] = (await Promise.allSettled([
phash_get_similar_images_by_image_buffer({ image: image, distance_threshold: 64 }),
global_features_get_similar_images_by_image_buffer({ image: image, k: 200 }),
local_features_get_similar_images_by_image_buffer({
image: image, k: 200, k_clusters: 10,
knn_min_matches: 4, matching_threshold: 0.8,
use_smnn_matching: 1, use_ransac: 1
}),
color_get_similar_images_by_image_buffer({ image: image, k: 200 }),
image_text_features_get_similar_images_by_image_buffer({ image: image, k: 200 })
])).map((promise: any) => promise.value)
}
console.log("==================")
console.log("local_features")
console.log(local_features_res)
console.log("==================")
console.log("==================")
console.log("phash")
console.log(phash_res)
console.log("==================")
console.log("==================")
console.log("global_features")
console.log(global_features_res)
console.log("==================")
console.log("==================")
console.log("color")
console.log(color_res)
console.log("==================")
// console.log("==================")
// console.log("text")
// console.log(text_res)
// console.log("==================")
const _unified_res: { [key: string]: any } = {}
const unified_res = []
for (const data_source of [[phash_res, "phash_res"],
[global_features_res, "global_features_res"], [local_features_res, "local_features_res"],
[color_res, "color_res"], [image_text_res, "image_text_res"]]) {
for (let i = 0; i < data_source[0].length; i++) {
const res = data_source[0][i]
if (!_unified_res[res.image_id]) {
_unified_res[res.image_id] = {}
}
_unified_res[res.image_id][data_source[1]] = i
}
}
console.log(_unified_res)
for (const image of Object.entries(_unified_res)) {
if (Object.entries(image[1]).length > 1) {
let score = 0
for (const data_source in image[1]) {
const idx_in_results = image[1][data_source] + 1
let multiplier = 1
switch (data_source) {
case "global_features_res":
multiplier = 1
break
case "local_features_res":
multiplier = 1
break
case "color_res":
multiplier = 1
break
case "image_text_res":
multiplier = 1
break
case "phash_res":
multiplier = 1
break
}
score += multiplier * (1 / idx_in_results)
}
unified_res.push({ image_id: parseInt(image[0]), score: score })
}
}
const all_results: any = {}
for (const data_source of [[phash_res, "phash_res"],
[global_features_res, "global_features_res"], [local_features_res, "local_features_res"],
[color_res, "color_res"], [image_text_res, "image_text_res"]]) {
if (data_source[0].length!==0){
all_results[data_source[1]] = data_source[0].slice(0,20)
}
}
if (unified_res.length !== 0) {
all_results.unified_res = unified_res.sort((a:any,b:any)=>-(a.score-b.score))
}
return all_results
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
async function delete_local_features_by_id(image_id: number) {
const status = await axios.post(`${config.local_features_microservice_url}/delete_local_features`, { image_id: image_id })
return status.data
}
async function delete_global_features_by_id(image_id: number) {
const status = await axios.post(`${config.global_features_microservice_url}/delete_global_features`, { image_id: image_id })
return status.data
}
async function delete_image_text_features_by_id(image_id: number) {
const status = await axios.post(`${config.image_text_features_microservice_url}/delete_image_text_features`, { image_id: image_id })
return status.data
}
async function delete_color_features_by_id(image_id: number) {
const status = await axios.post(`${config.color_microservice_url}/delete_color_features`, { image_id: image_id })
return status.data
}
async function delete_phash_features_by_id(image_id: number) {
const status = await axios.post(`${config.phash_microservice_url}/delete_phash_features`, { image_id: image_id })
return status.data
}
// async function delete_text_features_by_id(image_id: number) {
// const status = await axios.post(`${config.text_microservice_url}/delete_text_features`, { image_id: image_id })
// return status.data
// }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
async function calculate_all_image_features(image_id: number, image_buffer: Buffer) {
return Promise.allSettled([
calculate_global_features(image_id, image_buffer),
calculate_local_features(image_id, image_buffer),
calculate_color_features(image_id, image_buffer),
calculate_phash_features(image_id, image_buffer),
calculate_image_text_features(image_id, image_buffer),
// calculate_text_features(image_id, image_buffer)
])
}
async function delete_all_image_features(image_id: number) {
return Promise.allSettled([
delete_global_features_by_id(image_id),
delete_local_features_by_id(image_id),
delete_color_features_by_id(image_id),
delete_phash_features_by_id(image_id),
delete_image_text_features_by_id(image_id)
// delete_text_features_by_id(image_id)
])
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
export default { calculate_all_image_features, delete_all_image_features, get_similar_images }