lots change

This commit is contained in:
2026-09-03 14:48:13 +08:00
parent 2bf7db6bb9
commit bf4d764e58
11 changed files with 101 additions and 97 deletions
+3
View File
@@ -23,6 +23,7 @@ env/
uploads/* uploads/*
core/dataset/*.zip
core/dataset/toy/* core/dataset/toy/*
core/dataset/benchmark/* core/dataset/benchmark/*
core/models/*.pth core/models/*.pth
@@ -33,3 +34,5 @@ build/bin
frontend/node_modules frontend/node_modules
frontend/dist frontend/dist
frontend/public frontend/public
static/images/*
BIN
View File
Binary file not shown.
+59 -52
View File
@@ -3,6 +3,7 @@ package backend
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/base64"
"fmt" "fmt"
"math" "math"
"os" "os"
@@ -17,23 +18,12 @@ import (
) )
var ( var (
publicImagePath = "./frontend/public/images" staticImagesPath = "./static/images"
modelPath = "resnet_epoch_100.onnx" modelPath = "resnet18_epoch_50.onnx"
// ImageNet 标准化参数 // ImageNet 标准化参数
mean = []float32{0.485, 0.456, 0.406} mean = []float32{0.485, 0.456, 0.406}
std = []float32{0.229, 0.224, 0.225} std = []float32{0.229, 0.224, 0.225}
// 猫品种标签
labelName = []string{
"american_shorthair",
"bengal",
"british_shorthair",
"exotic_shorthair",
"maine_coon",
"ragdoll",
"sphynx",
}
) )
func NewApp() *App { func NewApp() *App {
@@ -90,16 +80,14 @@ func preprocessImage(imgData []byte) ([]float32, error) {
return input, nil return input, nil
} }
func runInference(input []float32) (string, float64, error) { func runInference(input []float32, labels []string) (string, float64, error) {
// 创建输入张量 [1, 3, 224, 224]
inputTensor, err := ort.NewTensor(ort.Shape{1, 3, 224, 224}, input) inputTensor, err := ort.NewTensor(ort.Shape{1, 3, 224, 224}, input)
if err != nil { if err != nil {
return "", 0, fmt.Errorf("create input tensor: %w", err) return "", 0, fmt.Errorf("create input tensor: %w", err)
} }
defer inputTensor.Destroy() defer inputTensor.Destroy()
// 创建输出张量 [1, 7] outputTensor, err := ort.NewTensor(ort.Shape{1, 12}, make([]float32, 12))
outputTensor, err := ort.NewTensor(ort.Shape{1, 7}, make([]float32, 7))
if err != nil { if err != nil {
return "", 0, fmt.Errorf("create output tensor: %w", err) return "", 0, fmt.Errorf("create output tensor: %w", err)
} }
@@ -147,7 +135,7 @@ func runInference(input []float32) (string, float64, error) {
} }
confidence := math.Exp(float64(maxVal)) / sum confidence := math.Exp(float64(maxVal)) / sum
return labelName[maxIdx], confidence, nil return labels[maxIdx], confidence, nil
} }
func (a *App) GormDB() (*gorm.DB, error) { func (a *App) GormDB() (*gorm.DB, error) {
@@ -159,7 +147,7 @@ func (a *App) GormDB() (*gorm.DB, error) {
} }
func (a *App) UploadImage(data []byte, filename string) Response { func (a *App) UploadImage(data []byte, filename string) Response {
uploadsDir := publicImagePath uploadsDir := staticImagesPath
err := os.MkdirAll(uploadsDir, 0755) err := os.MkdirAll(uploadsDir, 0755)
if err != nil { if err != nil {
return Response{Code: 1, Message: "failed", Data: err.Error()} return Response{Code: 1, Message: "failed", Data: err.Error()}
@@ -174,32 +162,12 @@ func (a *App) UploadImage(data []byte, filename string) Response {
return Response{Code: 1, Message: "failed", Data: err.Error()} return Response{Code: 1, Message: "failed", Data: err.Error()}
} }
println("333") imageResult := map[string]any{
"filename": newFilename,
return Response{Code: 0, Message: "success", Data: newFilename} "data": base64.StdEncoding.EncodeToString(data),
}
func (a *App) GetHistory(page int, pageSize int) Response {
db, err := a.GormDB()
if err != nil {
return Response{Code: 1, Message: err.Error()}
} }
var total int64 return Response{Code: 0, Message: "success", Data: imageResult}
db.Table("history_test").Count(&total)
var historyList []HistoryWithBreed
err = db.Table("history_test").Select("history_test.*, breeds_test.brief, breeds_test.name").
Joins("LEFT JOIN breeds_test ON history_test.breed = breeds_test.id").
Order("history_test.id DESC").Limit(pageSize).Offset((page - 1) * pageSize).Find(&historyList).Error
if err != nil {
return Response{Code: 1, Message: err.Error()}
}
historyData := HistoryData{Page: page, PageSize: pageSize, Total: total, List: historyList}
return Response{Code: 0, Message: "success", Data: historyData}
} }
func (a *App) Detect(filename string) Response { func (a *App) Detect(filename string) Response {
@@ -208,7 +176,7 @@ func (a *App) Detect(filename string) Response {
return Response{Code: 1, Message: err.Error()} return Response{Code: 1, Message: err.Error()}
} }
filePath := filepath.Join(publicImagePath, filename) filePath := filepath.Join(staticImagesPath, filename)
imgData, err := os.ReadFile(filePath) imgData, err := os.ReadFile(filePath)
if err != nil { if err != nil {
return Response{Code: 1, Message: "failed to read image: " + err.Error()} return Response{Code: 1, Message: "failed to read image: " + err.Error()}
@@ -219,22 +187,31 @@ func (a *App) Detect(filename string) Response {
return Response{Code: 1, Message: "failed to preprocess: " + err.Error()} return Response{Code: 1, Message: "failed to preprocess: " + err.Error()}
} }
// 查询breeds表的Code列,组成切片
var labels []string
err = db.Table("breeds").Pluck("code", &labels).Error
if err != nil {
return Response{Code: 1, Message: "failed to query breed codes: " + err.Error()}
}
// 推理 // 推理
detectRet, confidence, err := runInference(input) detectRet, confidence, err := runInference(input, labels)
if err != nil { if err != nil {
return Response{Code: 1, Message: "model inference failed: " + err.Error()} return Response{Code: 1, Message: "model inference failed: " + err.Error()}
} }
confidence = math.Round(confidence*10000) / 10000
var breed Breed var breed Breed
err = db.Table("breeds_test").Where("code = ?", detectRet).First(&breed).Error err = db.Table("breeds").Where("code = ?", detectRet).First(&breed).Error
if err != nil { if err != nil {
return Response{Code: 1, Message: err.Error()} return Response{Code: 1, Message: err.Error()}
} }
now := int(time.Now().Unix()) now := int(time.Now().Unix())
print("confidence", confidence)
one := HistoryItem{Img: filename, Breed: int(breed.Id), Date: now} one := HistoryItem{Img: filename, Breed: int(breed.Id), Confidence: confidence, Date: now}
result := db.Table("history_test").Create(&one) result := db.Table("history").Create(&one)
if result.Error != nil { if result.Error != nil {
return Response{Code: 1, Message: result.Error.Error()} return Response{Code: 1, Message: result.Error.Error()}
} }
@@ -250,13 +227,43 @@ func (a *App) Detect(filename string) Response {
return Response{Code: 0, Message: "success", Data: detectData} return Response{Code: 0, Message: "success", Data: detectData}
} }
func (a *App) GetHistory(page int, pageSize int) Response {
db, err := a.GormDB()
if err != nil {
return Response{Code: 1, Message: err.Error()}
}
var total int64
db.Table("history").Count(&total)
var historyList []HistoryWithBreed
err = db.Table("history").Select("history.*, breeds.brief, breeds.name").
Joins("LEFT JOIN breeds ON history.breed = breeds.id").
Order("history.id DESC").Limit(pageSize).Offset((page - 1) * pageSize).Find(&historyList).Error
if err != nil {
return Response{Code: 1, Message: err.Error()}
}
for i := range historyList {
imgPath := filepath.Join(staticImagesPath, historyList[i].Img)
if data, err := os.ReadFile(imgPath); err == nil {
historyList[i].ImgData = base64.StdEncoding.EncodeToString(data)
}
}
historyData := HistoryData{Page: page, PageSize: pageSize, Total: total, List: historyList}
return Response{Code: 0, Message: "success", Data: historyData}
}
func (a *App) DeleteOneHistory(id uint) Response { func (a *App) DeleteOneHistory(id uint) Response {
db, err := a.GormDB() db, err := a.GormDB()
if err != nil { if err != nil {
return Response{Code: 1, Message: err.Error()} return Response{Code: 1, Message: err.Error()}
} }
result := db.Table("history_test").Delete(&HistoryItem{}, id) result := db.Table("history").Delete(&HistoryItem{}, id)
if result.Error != nil { if result.Error != nil {
return Response{Code: 1, Message: result.Error.Error()} return Response{Code: 1, Message: result.Error.Error()}
} }
@@ -270,15 +277,15 @@ func (a *App) ClearHistory() Response {
return Response{Code: 1, Message: err.Error()} return Response{Code: 1, Message: err.Error()}
} }
result := db.Exec("DELETE FROM history_test") result := db.Exec("DELETE FROM history")
if result.Error != nil { if result.Error != nil {
return Response{Code: 1, Message: result.Error.Error()} return Response{Code: 1, Message: result.Error.Error()}
} }
if err := os.RemoveAll(publicImagePath); err != nil { if err := os.RemoveAll(staticImagesPath); err != nil {
return Response{Code: 1, Message: fmt.Sprintf("failed to remove images: %v", err)} return Response{Code: 1, Message: fmt.Sprintf("failed to remove images: %v", err)}
} }
if err := os.MkdirAll(publicImagePath, 0755); err != nil { if err := os.MkdirAll(staticImagesPath, 0755); err != nil {
return Response{Code: 1, Message: fmt.Sprintf("failed to recreate images dir: %v", err)} return Response{Code: 1, Message: fmt.Sprintf("failed to recreate images dir: %v", err)}
} }
+2
View File
@@ -19,12 +19,14 @@ type (
Id uint `gorm:"primaryKey"` Id uint `gorm:"primaryKey"`
Img string `gorm:"column:img"` Img string `gorm:"column:img"`
Breed int `gorm:"column:breed"` Breed int `gorm:"column:breed"`
Confidence float64 `gorm:"column:confidence"`
Date int `gorm:"column:date"` Date int `gorm:"column:date"`
} }
HistoryWithBreed struct { HistoryWithBreed struct {
Id uint `gorm:"column:id" json:"id"` Id uint `gorm:"column:id" json:"id"`
Img string `gorm:"column:img" json:"img"` Img string `gorm:"column:img" json:"img"`
ImgData string `gorm:"-" json:"img_data"`
Breed int `gorm:"column:breed" json:"breed"` Breed int `gorm:"column:breed" json:"breed"`
Date int `gorm:"column:date" json:"date"` Date int `gorm:"column:date" json:"date"`
Name string `gorm:"column:name" json:"name"` Name string `gorm:"column:name" json:"name"`
+2 -5
View File
@@ -1,11 +1,8 @@
import os import os
import torch import torch
import sys
# from core.nets.resnet import resnet
from core.nets.resnet18 import resnet18 from core.nets.resnet18 import resnet18
# 加载 pth # 加载 pth
# net = resnet() # 实例化你的模型
net = resnet18() net = resnet18()
@@ -14,7 +11,7 @@ core_dir = os.path.dirname(script_dir)
model_dir = os.path.join(core_dir, "models") model_dir = os.path.join(core_dir, "models")
net.load_state_dict(torch.load(os.path.join(model_dir, "resnet18_epoch_50_bak2.pth"), map_location="cpu")) net.load_state_dict(torch.load(os.path.join(model_dir, "resnet18_epoch_50.pth"), map_location="cpu"))
net.eval() net.eval()
@@ -23,7 +20,7 @@ dummy_input = torch.randn(1, 3, 224, 224)
torch.onnx.export( torch.onnx.export(
net, net,
dummy_input, dummy_input,
os.path.join(model_dir, "resnet18_epoch_50_bak2.onnx"), os.path.join(model_dir, "resnet18_epoch_50.onnx"),
export_params=True, export_params=True,
opset_version=11, opset_version=11,
input_names=["input"], input_names=["input"],
+1 -1
View File
@@ -2,7 +2,7 @@ export const Footer = () => (
<footer class="app-footer"> <footer class="app-footer">
<span>📩 xiadongliang88@163.com</span> <span>📩 xiadongliang88@163.com</span>
<span>|</span> <span>|</span>
<span>📦 https://git.leonstack.com/owner</span> <span>📦 https://git.leonstack.com/xiadongliang</span>
<span>|</span> <span>|</span>
<span>🌏 https://www.leonstack.com/</span> <span>🌏 https://www.leonstack.com/</span>
</footer> </footer>
+2 -2
View File
@@ -4,6 +4,7 @@ import Modal from './Modal'
import { message } from '../utils/toast' import { message } from '../utils/toast'
import type { HistoryItem } from '../preact' import type { HistoryItem } from '../preact'
const formatDate = (timestamp: number) => { const formatDate = (timestamp: number) => {
const date = new Date(timestamp * 1000) const date = new Date(timestamp * 1000)
return date.toLocaleString('zh-CN', { return date.toLocaleString('zh-CN', {
@@ -64,7 +65,6 @@ const History = () => {
message.error('currentId为空') message.error('currentId为空')
return return
} }
// dfdfdf
const result = await (window as any).go.backend.App.DeleteOneHistory(currentId) const result = await (window as any).go.backend.App.DeleteOneHistory(currentId)
if (result.code === 0) { if (result.code === 0) {
message.success('删除成功') message.success('删除成功')
@@ -109,7 +109,7 @@ const History = () => {
{historyList.map((item: HistoryItem) => {historyList.map((item: HistoryItem) =>
<div key={item.id} class="history-card"> <div key={item.id} class="history-card">
<div class="card-left"> <div class="card-left">
<img src={`/images/${item.img}`} onClick={() => handleShowItem(item)} /> <img src={`data:image/jpeg;base64,${item.img_data}`} onClick={() => handleShowItem(item)} />
</div> </div>
<div class="card-right"> <div class="card-right">
<div class="right-top"> <div class="right-top">
+11 -10
View File
@@ -2,9 +2,11 @@ import { useState, useRef } from 'preact/hooks'
import type { DetectResult } from '../preact' import type { DetectResult } from '../preact'
import { message } from '../utils/toast' import { message } from '../utils/toast'
const Main = () => { const Main = () => {
const fileInputRef = useRef<HTMLInputElement>(null) const fileInputRef = useRef<HTMLInputElement>(null)
const [fileSrc, setFileSrc] = useState<string>('') const [fileSrc, setFileSrc] = useState<string>('')
const [filename, setFilename] = useState<string>('')
const [step, setStep] = useState<number>(0) const [step, setStep] = useState<number>(0)
const [detectResult, setDetectResult] = useState<DetectResult | null>(null) const [detectResult, setDetectResult] = useState<DetectResult | null>(null)
@@ -46,7 +48,6 @@ const Main = () => {
} }
const handleFileChange = (e: Event) => { const handleFileChange = (e: Event) => {
console.log('handleFileChange')
const target = e.target as HTMLInputElement const target = e.target as HTMLInputElement
const file = target.files?.[0] const file = target.files?.[0]
if (file) { if (file) {
@@ -64,8 +65,8 @@ const Main = () => {
file.name file.name
) )
if (result.code === 0) { if (result.code === 0) {
console.log("rrr", result) setFileSrc(result.data.data)
setFileSrc(result.data) setFilename(result.data.filename)
setStep(1) setStep(1)
} else if (result.code === 1) { } else if (result.code === 1) {
message.error(result.message) message.error(result.message)
@@ -92,14 +93,16 @@ const Main = () => {
setStep(2) setStep(2)
setTimeout(async() => { setTimeout(async() => {
const result = await (window as any).go.backend.App.Detect(fileSrc) if (filename) {
const result = await (window as any).go.backend.App.Detect(filename)
if (result.code === 0) { if (result.code === 0) {
setDetectResult(result.data) setDetectResult(result.data)
setStep(3) setStep(3)
} else if (result.code === 1) { } else if (result.code === 1) {
message.error(result.message) message.error(result.message)
} }
}, 1000) }
}, 500)
} }
const handleTryOther = () => { const handleTryOther = () => {
@@ -108,8 +111,6 @@ const Main = () => {
resetUpload() resetUpload()
} }
console.log("fff", fileSrc)
return ( return (
<main class="app-main"> <main class="app-main">
<div class="main-container"> <div class="main-container">
@@ -135,7 +136,7 @@ const Main = () => {
> >
{fileSrc.length > 0 && step == 1 ? {fileSrc.length > 0 && step == 1 ?
<div id="previewContent"> <div id="previewContent">
<img src={`/images/${fileSrc}`} /> <img src={`data:image/jpeg;base64,${fileSrc}`} />
<button onClick={handleRemovePhoto}> <button onClick={handleRemovePhoto}>
</button> </button>
@@ -194,7 +195,7 @@ const Main = () => {
<h2></h2> <h2></h2>
</div> </div>
<div class="result-main"> <div class="result-main">
<img src={`/images/${fileSrc}`} /> <img src={`data:image/jpeg;base64,${fileSrc}`} />
<div class="result-word"> <div class="result-word">
<div> <div>
<h3>{detectResult?.name}</h3> <h3>{detectResult?.name}</h3>
@@ -202,7 +203,7 @@ const Main = () => {
<div class="probability-bar"> <div class="probability-bar">
<div /> <div />
</div> </div>
<span> {detectResult ? detectResult.confidence_level * 100 + '%' : ''}</span> <span> {detectResult ? (detectResult.confidence_level * 100).toFixed(2) + '%' : ''}</span>
</div> </div>
</div> </div>
<p>{detectResult?.brief}</p> <p>{detectResult?.brief}</p>
+3 -6
View File
@@ -1,9 +1,8 @@
import type { ModalProps } from '../preact' import type { ModalProps } from '../preact'
const Modal = ({ open, title, onClick, onClose, children }: ModalProps) => { const Modal = ({ open, title, onClick, onClose, children }: ModalProps) => {
const handleClose = () => { const handleClose = () => onClose?.()
onClose?.()
}
const handleConfirm = () => { const handleConfirm = () => {
onClick?.() onClick?.()
@@ -11,9 +10,7 @@ const Modal = ({ open, title, onClick, onClose, children }: ModalProps) => {
} }
const handleMaskClick = (e: MouseEvent) => { const handleMaskClick = (e: MouseEvent) => {
if (e.target === e.currentTarget) { if (e.target === e.currentTarget) handleClose()
handleClose()
}
} }
return ( return (
+1
View File
@@ -1,6 +1,7 @@
export interface HistoryItem { export interface HistoryItem {
id: number id: number
img: string img: string
img_data: string
breed: number breed: number
date: number date: number
name: string name: string
+2 -6
View File
@@ -23,9 +23,7 @@ export const message: MessageAPI = {
div.appendChild(subDiv) div.appendChild(subDiv)
document.body.appendChild(div) document.body.appendChild(div)
setTimeout(() => { setTimeout(() => div.remove(), 3000)
div.remove()
}, 3000)
}, },
error: (text: string) => { error: (text: string) => {
const div = document.createElement('div') const div = document.createElement('div')
@@ -46,8 +44,6 @@ export const message: MessageAPI = {
div.appendChild(subDiv) div.appendChild(subDiv)
document.body.appendChild(div) document.body.appendChild(div)
setTimeout(() => { setTimeout(() => div.remove(), 3000)
div.remove()
}, 3000)
} }
} }