🐛 fixed the logic of judging screenshots
This commit is contained in:
@@ -2,11 +2,11 @@ import localforage from 'localforage';
|
||||
|
||||
interface UploadPredictResult {
|
||||
isAnime: boolean;
|
||||
hasFace: boolean;
|
||||
objectArray: string[] | unknown[];
|
||||
landscape: 'building' | 'forest' | 'glacier' | 'mountain' | 'sea' | 'street' | 'none';
|
||||
isScreenshot: boolean;
|
||||
topCategory: string | undefined;
|
||||
exif: object | null;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,14 +15,13 @@ export const useUploadStore = defineStore(
|
||||
() => {
|
||||
const openUploadDrawer = ref<boolean>(false);
|
||||
|
||||
const exifData = ref<any>();
|
||||
const predictResult = reactive<UploadPredictResult>({
|
||||
isAnime: false,
|
||||
hasFace: false,
|
||||
objectArray: [],
|
||||
landscape: 'none',
|
||||
isScreenshot: false,
|
||||
topCategory: ''
|
||||
topCategory: '',
|
||||
exif: {}
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -37,18 +36,17 @@ export const useUploadStore = defineStore(
|
||||
*/
|
||||
function clearPredictResult() {
|
||||
predictResult.isAnime = false;
|
||||
predictResult.hasFace = false;
|
||||
predictResult.objectArray = [];
|
||||
predictResult.landscape = 'none';
|
||||
predictResult.isScreenshot = false;
|
||||
predictResult.topCategory = '';
|
||||
predictResult.exif = {};
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
openUploadDrawer,
|
||||
predictResult,
|
||||
exifData,
|
||||
openUploadDrawerFn,
|
||||
clearPredictResult,
|
||||
};
|
||||
|
@@ -16,8 +16,11 @@ async function isScreenshot(file) {
|
||||
'screenshot', '屏幕截图', '截屏', 'Snip', 'Capture', 'Snapshot', '截图'
|
||||
];
|
||||
|
||||
// 获取文件的 MIME 类型
|
||||
const fileType = file.type.toLowerCase();
|
||||
|
||||
try {
|
||||
// 文件名匹配
|
||||
// 判断文件名是否包含截图相关关键词
|
||||
const fileName = file.name.toLowerCase();
|
||||
const matchesFilename = screenshotFilenameKeywords.some(keyword =>
|
||||
fileName.includes(keyword.toLowerCase())
|
||||
@@ -26,7 +29,11 @@ async function isScreenshot(file) {
|
||||
return true; // 如果文件名包含截图相关关键词,直接判断为截图
|
||||
}
|
||||
|
||||
// 提取 EXIF 数据
|
||||
// 判断文件类型是否为支持的格式
|
||||
const isSupportedFormat = ['image/jpeg', 'image/tiff', 'image/heif', 'image/png'].includes(fileType);
|
||||
|
||||
if (isSupportedFormat) {
|
||||
// 如果是支持的格式,提取 EXIF 数据
|
||||
const exifData = await exifr.parse(file, ['ImageWidth', 'ImageHeight', 'Software', 'XResolution', 'YResolution']);
|
||||
const {ImageWidth, ImageHeight, Software} = exifData || {};
|
||||
|
||||
@@ -55,16 +62,15 @@ async function isScreenshot(file) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 无 EXIF 数据或不匹配时,通过宽高比的容差检测
|
||||
const img: any = await getImageDimensions(file);
|
||||
const imgAspectRatio = img.width / img.height;
|
||||
const imgMatchesAspectRatio = commonAspectRatios.some(
|
||||
return commonAspectRatios.some(
|
||||
(ratio) => Math.abs(ratio - imgAspectRatio) <= aspectRatioTolerance
|
||||
);
|
||||
|
||||
return imgMatchesAspectRatio;
|
||||
|
||||
} catch (error) {
|
||||
console.error('判断截图时发生错误:', error);
|
||||
return false;
|
||||
|
@@ -21,6 +21,8 @@
|
||||
:disabled="predicting"
|
||||
:progress="progress"
|
||||
@remove="removeFile"
|
||||
@reject="rejectFile"
|
||||
:openFileDialogOnClick="true"
|
||||
accept="image/*"
|
||||
list-type="picture"
|
||||
method="post"
|
||||
@@ -52,7 +54,6 @@ import i18n from "@/locales";
|
||||
|
||||
import {NSFWJS} from "nsfwjs";
|
||||
import {animePredictImagePro} from "@/utils/tfjs/anime_classifier_pro.ts";
|
||||
import {fnDetectFace} from "@/utils/tfjs/face_extraction.ts";
|
||||
import {cocoSsdPredict} from "@/utils/tfjs/mobilenet.ts";
|
||||
import {predictLandscape} from "@/utils/tfjs/landscape_recognition.ts";
|
||||
import {useRequest} from 'alova/client';
|
||||
@@ -92,8 +93,9 @@ const progress: UploadProps['progress'] = {
|
||||
/**
|
||||
* 图片上传前的校验
|
||||
* @param file
|
||||
* @param fileList
|
||||
*/
|
||||
async function beforeUpload(file: File) {
|
||||
async function beforeUpload(file: File, fileList: File[]) {
|
||||
predicting.value = true;
|
||||
upload.clearPredictResult();
|
||||
progressPercent.value = 0; // 初始化进度条
|
||||
@@ -132,16 +134,18 @@ async function beforeUpload(file: File) {
|
||||
|
||||
if (isNSFW) {
|
||||
message.error(i18n.global.t('comment.illegalImage'));
|
||||
predicting.value = false;
|
||||
|
||||
progressPercent.value = 100; // 重置进度条
|
||||
progressStatus.value = 'exception'; // 异常状态
|
||||
fileList.pop(); // 清空文件列表
|
||||
predicting.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 提取 EXIF 数据
|
||||
const exifData = await extractAllExifData(file);
|
||||
if (exifData) {
|
||||
upload.exifData = exifData;
|
||||
upload.predictResult.exif = exifData;
|
||||
}
|
||||
|
||||
// 判断是否为截图
|
||||
@@ -158,14 +162,7 @@ async function beforeUpload(file: File) {
|
||||
progressPercent.value = 100; // 直接完成
|
||||
return true;
|
||||
}
|
||||
// 人脸检测
|
||||
const faceImageData = await fnDetectFace(image);
|
||||
if (faceImageData) {
|
||||
upload.predictResult.hasFace = true;
|
||||
predicting.value = false;
|
||||
progressPercent.value = 100; // 直接完成
|
||||
return true;
|
||||
}
|
||||
|
||||
//目标检测和风景检测并行处理
|
||||
const [cocoResults, landscape] = await Promise.all([
|
||||
cocoSsdPredict(image), // 目标检测
|
||||
@@ -218,7 +215,6 @@ async function customUploadRequest(file: any) {
|
||||
formData.append("data", JSON.stringify({
|
||||
fileType: file.file.type,
|
||||
...upload.predictResult,
|
||||
exif: JSON.stringify(upload.exifData) || '',
|
||||
}));
|
||||
watch(
|
||||
() => uploading.value,
|
||||
@@ -248,6 +244,14 @@ function cancelUpload() {
|
||||
progressPercent.value = 0; // 重置进度条
|
||||
}
|
||||
|
||||
/**
|
||||
* 拒绝文件回调
|
||||
* @param fileList
|
||||
*/
|
||||
function rejectFile(fileList: any) {
|
||||
fileList.value.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
* @param file
|
||||
|
Reference in New Issue
Block a user