🐳 add docker file
This commit is contained in:
BIN
app/aisvc/resources/models/caffemodel/bvlc_googlenet.caffemodel
Normal file
BIN
app/aisvc/resources/models/caffemodel/bvlc_googlenet.caffemodel
Normal file
Binary file not shown.
2156
app/aisvc/resources/models/caffemodel/bvlc_googlenet.prototxt
Normal file
2156
app/aisvc/resources/models/caffemodel/bvlc_googlenet.prototxt
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -4,15 +4,35 @@ package ai;
|
||||
option go_package = "./pb";
|
||||
|
||||
|
||||
|
||||
// 人脸识别
|
||||
message FaceRecognitionRequest {
|
||||
bytes face = 1;
|
||||
string user_id = 2;
|
||||
}
|
||||
message FaceRecognitionResponse {
|
||||
int64 face_id = 1;
|
||||
int64 face_id = 3;
|
||||
}
|
||||
// tf分类识别
|
||||
message TfClassificationRequest {
|
||||
bytes image = 1;
|
||||
}
|
||||
message TfClassificationResponse {
|
||||
string class_name = 2;
|
||||
float score = 3;
|
||||
}
|
||||
// caffe分类识别
|
||||
message CaffeClassificationRequest {
|
||||
bytes image = 1;
|
||||
}
|
||||
message CaffeClassificationResponse {
|
||||
string class_name = 2;
|
||||
float score = 3;
|
||||
}
|
||||
service AiService {
|
||||
// FaceRecognition
|
||||
rpc FaceRecognition (FaceRecognitionRequest) returns (FaceRecognitionResponse);
|
||||
// TfClassification
|
||||
rpc TfClassification (TfClassificationRequest) returns (TfClassificationResponse);
|
||||
// CaffeClassification
|
||||
rpc CaffeClassification (CaffeClassificationRequest) returns (CaffeClassificationResponse);
|
||||
}
|
@@ -14,12 +14,20 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
FaceRecognitionRequest = pb.FaceRecognitionRequest
|
||||
FaceRecognitionResponse = pb.FaceRecognitionResponse
|
||||
CaffeClassificationRequest = pb.CaffeClassificationRequest
|
||||
CaffeClassificationResponse = pb.CaffeClassificationResponse
|
||||
FaceRecognitionRequest = pb.FaceRecognitionRequest
|
||||
FaceRecognitionResponse = pb.FaceRecognitionResponse
|
||||
TfClassificationRequest = pb.TfClassificationRequest
|
||||
TfClassificationResponse = pb.TfClassificationResponse
|
||||
|
||||
AiService interface {
|
||||
// FaceRecognition
|
||||
FaceRecognition(ctx context.Context, in *FaceRecognitionRequest, opts ...grpc.CallOption) (*FaceRecognitionResponse, error)
|
||||
// TfClassification
|
||||
TfClassification(ctx context.Context, in *TfClassificationRequest, opts ...grpc.CallOption) (*TfClassificationResponse, error)
|
||||
// CaffeClassification
|
||||
CaffeClassification(ctx context.Context, in *CaffeClassificationRequest, opts ...grpc.CallOption) (*CaffeClassificationResponse, error)
|
||||
}
|
||||
|
||||
defaultAiService struct {
|
||||
@@ -38,3 +46,15 @@ func (m *defaultAiService) FaceRecognition(ctx context.Context, in *FaceRecognit
|
||||
client := pb.NewAiServiceClient(m.cli.Conn())
|
||||
return client.FaceRecognition(ctx, in, opts...)
|
||||
}
|
||||
|
||||
// TfClassification
|
||||
func (m *defaultAiService) TfClassification(ctx context.Context, in *TfClassificationRequest, opts ...grpc.CallOption) (*TfClassificationResponse, error) {
|
||||
client := pb.NewAiServiceClient(m.cli.Conn())
|
||||
return client.TfClassification(ctx, in, opts...)
|
||||
}
|
||||
|
||||
// CaffeClassification
|
||||
func (m *defaultAiService) CaffeClassification(ctx context.Context, in *CaffeClassificationRequest, opts ...grpc.CallOption) (*CaffeClassificationResponse, error) {
|
||||
client := pb.NewAiServiceClient(m.cli.Conn())
|
||||
return client.CaffeClassification(ctx, in, opts...)
|
||||
}
|
||||
|
@@ -1,9 +1,11 @@
|
||||
Name: aisvc.rpc
|
||||
ListenOn: 0.0.0.0:8080
|
||||
ListenOn: 0.0.0.0:8888
|
||||
Etcd:
|
||||
Hosts:
|
||||
- 127.0.0.1:2379
|
||||
- 1.95.0.111:2379
|
||||
Key: aisvc.rpc
|
||||
User: root
|
||||
Pass: LDQ20020618xxx
|
||||
# MySQL 配置
|
||||
Mysql:
|
||||
# 数据源dsn
|
||||
|
@@ -0,0 +1,31 @@
|
||||
package aiservicelogic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"schisandra-album-cloud-microservices/app/aisvc/rpc/internal/svc"
|
||||
"schisandra-album-cloud-microservices/app/aisvc/rpc/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type CaffeClassificationLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewCaffeClassificationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CaffeClassificationLogic {
|
||||
return &CaffeClassificationLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// CaffeClassification
|
||||
func (l *CaffeClassificationLogic) CaffeClassification(in *pb.CaffeClassificationRequest) (*pb.CaffeClassificationResponse, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &pb.CaffeClassificationResponse{}, nil
|
||||
}
|
@@ -43,8 +43,12 @@ func NewFaceRecognitionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *F
|
||||
|
||||
// FaceRecognition 人脸识别
|
||||
func (l *FaceRecognitionLogic) FaceRecognition(in *pb.FaceRecognitionRequest) (*pb.FaceRecognitionResponse, error) {
|
||||
toJPEG, err := l.ConvertImageToJPEG(in.GetFace())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 提取人脸特征
|
||||
faceFeatures, err := l.svcCtx.FaceRecognizer.RecognizeSingle(in.GetFace())
|
||||
faceFeatures, err := l.svcCtx.FaceRecognizer.RecognizeSingle(toJPEG)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -82,7 +86,7 @@ func (l *FaceRecognitionLogic) FaceRecognition(in *pb.FaceRecognitionRequest) (*
|
||||
|
||||
// 人脸分类
|
||||
classify := l.svcCtx.FaceRecognizer.ClassifyThreshold(faceFeatures.Descriptor, 0.6)
|
||||
if classify >= 0 {
|
||||
if classify >= 0 && classify < len(ids) {
|
||||
return &pb.FaceRecognitionResponse{
|
||||
FaceId: int64(ids[classify]),
|
||||
}, nil
|
||||
@@ -92,6 +96,26 @@ func (l *FaceRecognitionLogic) FaceRecognition(in *pb.FaceRecognitionRequest) (*
|
||||
return l.saveNewFace(in, faceFeatures, hashKey)
|
||||
}
|
||||
|
||||
// ConvertImageToJPEG 将非 JPEG 格式的图片字节数据转换为 JPEG
|
||||
func (l *FaceRecognitionLogic) ConvertImageToJPEG(imageData []byte) ([]byte, error) {
|
||||
// 使用 image.Decode 解码图像数据
|
||||
img, _, err := image.Decode(bytes.NewReader(imageData))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode image: %v", err)
|
||||
}
|
||||
|
||||
// 创建一个缓冲区来存储 JPEG 格式的数据
|
||||
var jpegBuffer bytes.Buffer
|
||||
|
||||
// 将图片编码为 JPEG 格式
|
||||
err = jpeg.Encode(&jpegBuffer, img, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode image to JPEG: %v", err)
|
||||
}
|
||||
|
||||
return jpegBuffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// 保存新的人脸样本到数据库和 Redis
|
||||
func (l *FaceRecognitionLogic) saveNewFace(in *pb.FaceRecognitionRequest, faceFeatures *face.Face, hashKey string) (*pb.FaceRecognitionResponse, error) {
|
||||
// 人脸有效性判断 (大小必须大于50)
|
||||
|
@@ -0,0 +1,79 @@
|
||||
package aiservicelogic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"gocv.io/x/gocv"
|
||||
"image"
|
||||
"schisandra-album-cloud-microservices/app/aisvc/rpc/internal/svc"
|
||||
"schisandra-album-cloud-microservices/app/aisvc/rpc/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type TfClassificationLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewTfClassificationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *TfClassificationLogic {
|
||||
return &TfClassificationLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// TfClassification is a server endpoint to classify an image using TensorFlow.
|
||||
func (l *TfClassificationLogic) TfClassification(in *pb.TfClassificationRequest) (*pb.TfClassificationResponse, error) {
|
||||
className, source, err := l.ClassifyImage(in.GetImage())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.TfClassificationResponse{
|
||||
Score: source,
|
||||
ClassName: className,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ClassifyImage 从字节数据分类图像,返回分类标签和最大概率值
|
||||
func (l *TfClassificationLogic) ClassifyImage(imageBytes []byte) (string, float32, error) {
|
||||
|
||||
// 解码字节数据为图像
|
||||
img, err := gocv.IMDecode(imageBytes, gocv.IMReadColor)
|
||||
if err != nil || img.Empty() {
|
||||
return "", 0, fmt.Errorf("failed to decode image: %v", err)
|
||||
}
|
||||
defer func(img *gocv.Mat) {
|
||||
_ = img.Close()
|
||||
}(&img)
|
||||
|
||||
// 将图像 Mat 转换为 224x224 blob,以便分类器分析
|
||||
blob := gocv.BlobFromImage(img, 1.0, image.Pt(224, 224), gocv.NewScalar(0, 0, 0, 0), true, false)
|
||||
|
||||
// 将 blob 输入分类器
|
||||
l.svcCtx.TfNet.SetInput(blob, "input")
|
||||
|
||||
// 运行网络的正向传递
|
||||
prob := l.svcCtx.TfNet.Forward("softmax2")
|
||||
|
||||
// 将结果重塑为 1x1000 矩阵
|
||||
probMat := prob.Reshape(1, 1)
|
||||
|
||||
// 确定最可能的分类
|
||||
_, maxVal, _, maxLoc := gocv.MinMaxLoc(probMat)
|
||||
|
||||
// 获取分类描述
|
||||
desc := ""
|
||||
if maxLoc.X < 1000 {
|
||||
desc = l.svcCtx.TfDesc[maxLoc.X]
|
||||
}
|
||||
|
||||
// 清理资源
|
||||
_ = blob.Close()
|
||||
_ = prob.Close()
|
||||
_ = probMat.Close()
|
||||
|
||||
return desc, maxVal, nil
|
||||
}
|
@@ -28,3 +28,15 @@ func (s *AiServiceServer) FaceRecognition(ctx context.Context, in *pb.FaceRecogn
|
||||
l := aiservicelogic.NewFaceRecognitionLogic(ctx, s.svcCtx)
|
||||
return l.FaceRecognition(in)
|
||||
}
|
||||
|
||||
// TfClassification
|
||||
func (s *AiServiceServer) TfClassification(ctx context.Context, in *pb.TfClassificationRequest) (*pb.TfClassificationResponse, error) {
|
||||
l := aiservicelogic.NewTfClassificationLogic(ctx, s.svcCtx)
|
||||
return l.TfClassification(in)
|
||||
}
|
||||
|
||||
// CaffeClassification
|
||||
func (s *AiServiceServer) CaffeClassification(ctx context.Context, in *pb.CaffeClassificationRequest) (*pb.CaffeClassificationResponse, error) {
|
||||
l := aiservicelogic.NewCaffeClassificationLogic(ctx, s.svcCtx)
|
||||
return l.CaffeClassification(in)
|
||||
}
|
||||
|
@@ -3,11 +3,14 @@ package svc
|
||||
import (
|
||||
"github.com/Kagami/go-face"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gocv.io/x/gocv"
|
||||
"schisandra-album-cloud-microservices/app/aisvc/model/mysql"
|
||||
"schisandra-album-cloud-microservices/app/aisvc/model/mysql/query"
|
||||
"schisandra-album-cloud-microservices/app/aisvc/rpc/internal/config"
|
||||
"schisandra-album-cloud-microservices/common/caffe_classifier"
|
||||
"schisandra-album-cloud-microservices/common/face_recognizer"
|
||||
"schisandra-album-cloud-microservices/common/redisx"
|
||||
"schisandra-album-cloud-microservices/common/tf_classifier"
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
@@ -15,15 +18,25 @@ type ServiceContext struct {
|
||||
FaceRecognizer *face.Recognizer
|
||||
DB *query.Query
|
||||
RedisClient *redis.Client
|
||||
TfNet *gocv.Net
|
||||
TfDesc []string
|
||||
CaffeNet *gocv.Net
|
||||
CaffeDesc []string
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
redisClient := redisx.NewRedis(c.RedisConf.Host, c.RedisConf.Pass, c.RedisConf.DB)
|
||||
_, queryDB := mysql.NewMySQL(c.Mysql.DataSource, c.Mysql.MaxOpenConn, c.Mysql.MaxIdleConn, redisClient)
|
||||
tfClassifier, tfDesc := tf_classifier.NewTFClassifier()
|
||||
caffeClassifier, caffeDesc := caffe_classifier.NewCaffeClassifier()
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
FaceRecognizer: face_recognizer.NewFaceRecognition(),
|
||||
DB: queryDB,
|
||||
RedisClient: redisClient,
|
||||
TfNet: tfClassifier,
|
||||
TfDesc: tfDesc,
|
||||
CaffeNet: caffeClassifier,
|
||||
CaffeDesc: caffeDesc,
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ const (
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// 人脸识别
|
||||
type FaceRecognitionRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -78,7 +79,7 @@ type FaceRecognitionResponse struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
FaceId int64 `protobuf:"varint,1,opt,name=face_id,json=faceId,proto3" json:"face_id,omitempty"`
|
||||
FaceId int64 `protobuf:"varint,3,opt,name=face_id,json=faceId,proto3" json:"face_id,omitempty"`
|
||||
}
|
||||
|
||||
func (x *FaceRecognitionResponse) Reset() {
|
||||
@@ -118,6 +119,204 @@ func (x *FaceRecognitionResponse) GetFaceId() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// tf分类识别
|
||||
type TfClassificationRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Image []byte `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TfClassificationRequest) Reset() {
|
||||
*x = TfClassificationRequest{}
|
||||
mi := &file_aisvc_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TfClassificationRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TfClassificationRequest) ProtoMessage() {}
|
||||
|
||||
func (x *TfClassificationRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_aisvc_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TfClassificationRequest.ProtoReflect.Descriptor instead.
|
||||
func (*TfClassificationRequest) Descriptor() ([]byte, []int) {
|
||||
return file_aisvc_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *TfClassificationRequest) GetImage() []byte {
|
||||
if x != nil {
|
||||
return x.Image
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TfClassificationResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ClassName string `protobuf:"bytes,2,opt,name=class_name,json=className,proto3" json:"class_name,omitempty"`
|
||||
Score float32 `protobuf:"fixed32,3,opt,name=score,proto3" json:"score,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TfClassificationResponse) Reset() {
|
||||
*x = TfClassificationResponse{}
|
||||
mi := &file_aisvc_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TfClassificationResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TfClassificationResponse) ProtoMessage() {}
|
||||
|
||||
func (x *TfClassificationResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_aisvc_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TfClassificationResponse.ProtoReflect.Descriptor instead.
|
||||
func (*TfClassificationResponse) Descriptor() ([]byte, []int) {
|
||||
return file_aisvc_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *TfClassificationResponse) GetClassName() string {
|
||||
if x != nil {
|
||||
return x.ClassName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TfClassificationResponse) GetScore() float32 {
|
||||
if x != nil {
|
||||
return x.Score
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// caffe分类识别
|
||||
type CaffeClassificationRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Image []byte `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CaffeClassificationRequest) Reset() {
|
||||
*x = CaffeClassificationRequest{}
|
||||
mi := &file_aisvc_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CaffeClassificationRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CaffeClassificationRequest) ProtoMessage() {}
|
||||
|
||||
func (x *CaffeClassificationRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_aisvc_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CaffeClassificationRequest.ProtoReflect.Descriptor instead.
|
||||
func (*CaffeClassificationRequest) Descriptor() ([]byte, []int) {
|
||||
return file_aisvc_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *CaffeClassificationRequest) GetImage() []byte {
|
||||
if x != nil {
|
||||
return x.Image
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CaffeClassificationResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ClassName string `protobuf:"bytes,2,opt,name=class_name,json=className,proto3" json:"class_name,omitempty"`
|
||||
Score float32 `protobuf:"fixed32,3,opt,name=score,proto3" json:"score,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CaffeClassificationResponse) Reset() {
|
||||
*x = CaffeClassificationResponse{}
|
||||
mi := &file_aisvc_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CaffeClassificationResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CaffeClassificationResponse) ProtoMessage() {}
|
||||
|
||||
func (x *CaffeClassificationResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_aisvc_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CaffeClassificationResponse.ProtoReflect.Descriptor instead.
|
||||
func (*CaffeClassificationResponse) Descriptor() ([]byte, []int) {
|
||||
return file_aisvc_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *CaffeClassificationResponse) GetClassName() string {
|
||||
if x != nil {
|
||||
return x.ClassName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CaffeClassificationResponse) GetScore() float32 {
|
||||
if x != nil {
|
||||
return x.Score
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_aisvc_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_aisvc_proto_rawDesc = []byte{
|
||||
@@ -128,15 +327,42 @@ var file_aisvc_proto_rawDesc = []byte{
|
||||
0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x32, 0x0a, 0x17, 0x46, 0x61, 0x63, 0x65,
|
||||
0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x66, 0x61, 0x63, 0x65, 0x49, 0x64, 0x32, 0x57, 0x0a, 0x09,
|
||||
0x41, 0x69, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x0f, 0x46, 0x61, 0x63,
|
||||
0x65, 0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x61,
|
||||
0x69, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, 0x69, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x69, 0x2e, 0x46, 0x61,
|
||||
0x63, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x06, 0x5a, 0x04, 0x2e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x66, 0x61, 0x63, 0x65, 0x49, 0x64, 0x22, 0x2f, 0x0a, 0x17,
|
||||
0x54, 0x66, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x22, 0x4f, 0x0a,
|
||||
0x18, 0x54, 0x66, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x61,
|
||||
0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63,
|
||||
0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x32,
|
||||
0x0a, 0x1a, 0x43, 0x61, 0x66, 0x66, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05,
|
||||
0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x69, 0x6d, 0x61,
|
||||
0x67, 0x65, 0x22, 0x52, 0x0a, 0x1b, 0x43, 0x61, 0x66, 0x66, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73,
|
||||
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65,
|
||||
0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52,
|
||||
0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x32, 0xfe, 0x01, 0x0a, 0x09, 0x41, 0x69, 0x53, 0x65, 0x72,
|
||||
0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x0f, 0x46, 0x61, 0x63, 0x65, 0x52, 0x65, 0x63, 0x6f,
|
||||
0x67, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x61, 0x69, 0x2e, 0x46, 0x61, 0x63,
|
||||
0x65, 0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x69, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x52, 0x65, 0x63,
|
||||
0x6f, 0x67, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x4d, 0x0a, 0x10, 0x54, 0x66, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x66, 0x69, 0x63, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x2e, 0x61, 0x69, 0x2e, 0x54, 0x66, 0x43, 0x6c, 0x61, 0x73,
|
||||
0x73, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x69, 0x2e, 0x54, 0x66, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x66,
|
||||
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x56, 0x0a, 0x13, 0x43, 0x61, 0x66, 0x66, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x66, 0x69,
|
||||
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x61, 0x69, 0x2e, 0x43, 0x61, 0x66, 0x66,
|
||||
0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x69, 0x2e, 0x43, 0x61, 0x66, 0x66,
|
||||
0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x06, 0x5a, 0x04, 0x2e, 0x2f, 0x70, 0x62, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -151,16 +377,24 @@ func file_aisvc_proto_rawDescGZIP() []byte {
|
||||
return file_aisvc_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_aisvc_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_aisvc_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_aisvc_proto_goTypes = []any{
|
||||
(*FaceRecognitionRequest)(nil), // 0: ai.FaceRecognitionRequest
|
||||
(*FaceRecognitionResponse)(nil), // 1: ai.FaceRecognitionResponse
|
||||
(*FaceRecognitionRequest)(nil), // 0: ai.FaceRecognitionRequest
|
||||
(*FaceRecognitionResponse)(nil), // 1: ai.FaceRecognitionResponse
|
||||
(*TfClassificationRequest)(nil), // 2: ai.TfClassificationRequest
|
||||
(*TfClassificationResponse)(nil), // 3: ai.TfClassificationResponse
|
||||
(*CaffeClassificationRequest)(nil), // 4: ai.CaffeClassificationRequest
|
||||
(*CaffeClassificationResponse)(nil), // 5: ai.CaffeClassificationResponse
|
||||
}
|
||||
var file_aisvc_proto_depIdxs = []int32{
|
||||
0, // 0: ai.AiService.FaceRecognition:input_type -> ai.FaceRecognitionRequest
|
||||
1, // 1: ai.AiService.FaceRecognition:output_type -> ai.FaceRecognitionResponse
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
2, // 1: ai.AiService.TfClassification:input_type -> ai.TfClassificationRequest
|
||||
4, // 2: ai.AiService.CaffeClassification:input_type -> ai.CaffeClassificationRequest
|
||||
1, // 3: ai.AiService.FaceRecognition:output_type -> ai.FaceRecognitionResponse
|
||||
3, // 4: ai.AiService.TfClassification:output_type -> ai.TfClassificationResponse
|
||||
5, // 5: ai.AiService.CaffeClassification:output_type -> ai.CaffeClassificationResponse
|
||||
3, // [3:6] is the sub-list for method output_type
|
||||
0, // [0:3] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
@@ -177,7 +411,7 @@ func file_aisvc_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_aisvc_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
@@ -19,7 +19,9 @@ import (
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
AiService_FaceRecognition_FullMethodName = "/ai.AiService/FaceRecognition"
|
||||
AiService_FaceRecognition_FullMethodName = "/ai.AiService/FaceRecognition"
|
||||
AiService_TfClassification_FullMethodName = "/ai.AiService/TfClassification"
|
||||
AiService_CaffeClassification_FullMethodName = "/ai.AiService/CaffeClassification"
|
||||
)
|
||||
|
||||
// AiServiceClient is the client API for AiService service.
|
||||
@@ -28,6 +30,10 @@ const (
|
||||
type AiServiceClient interface {
|
||||
// FaceRecognition
|
||||
FaceRecognition(ctx context.Context, in *FaceRecognitionRequest, opts ...grpc.CallOption) (*FaceRecognitionResponse, error)
|
||||
// TfClassification
|
||||
TfClassification(ctx context.Context, in *TfClassificationRequest, opts ...grpc.CallOption) (*TfClassificationResponse, error)
|
||||
// CaffeClassification
|
||||
CaffeClassification(ctx context.Context, in *CaffeClassificationRequest, opts ...grpc.CallOption) (*CaffeClassificationResponse, error)
|
||||
}
|
||||
|
||||
type aiServiceClient struct {
|
||||
@@ -48,12 +54,36 @@ func (c *aiServiceClient) FaceRecognition(ctx context.Context, in *FaceRecogniti
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aiServiceClient) TfClassification(ctx context.Context, in *TfClassificationRequest, opts ...grpc.CallOption) (*TfClassificationResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(TfClassificationResponse)
|
||||
err := c.cc.Invoke(ctx, AiService_TfClassification_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *aiServiceClient) CaffeClassification(ctx context.Context, in *CaffeClassificationRequest, opts ...grpc.CallOption) (*CaffeClassificationResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CaffeClassificationResponse)
|
||||
err := c.cc.Invoke(ctx, AiService_CaffeClassification_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AiServiceServer is the server API for AiService service.
|
||||
// All implementations must embed UnimplementedAiServiceServer
|
||||
// for forward compatibility.
|
||||
type AiServiceServer interface {
|
||||
// FaceRecognition
|
||||
FaceRecognition(context.Context, *FaceRecognitionRequest) (*FaceRecognitionResponse, error)
|
||||
// TfClassification
|
||||
TfClassification(context.Context, *TfClassificationRequest) (*TfClassificationResponse, error)
|
||||
// CaffeClassification
|
||||
CaffeClassification(context.Context, *CaffeClassificationRequest) (*CaffeClassificationResponse, error)
|
||||
mustEmbedUnimplementedAiServiceServer()
|
||||
}
|
||||
|
||||
@@ -67,6 +97,12 @@ type UnimplementedAiServiceServer struct{}
|
||||
func (UnimplementedAiServiceServer) FaceRecognition(context.Context, *FaceRecognitionRequest) (*FaceRecognitionResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method FaceRecognition not implemented")
|
||||
}
|
||||
func (UnimplementedAiServiceServer) TfClassification(context.Context, *TfClassificationRequest) (*TfClassificationResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method TfClassification not implemented")
|
||||
}
|
||||
func (UnimplementedAiServiceServer) CaffeClassification(context.Context, *CaffeClassificationRequest) (*CaffeClassificationResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CaffeClassification not implemented")
|
||||
}
|
||||
func (UnimplementedAiServiceServer) mustEmbedUnimplementedAiServiceServer() {}
|
||||
func (UnimplementedAiServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
@@ -106,6 +142,42 @@ func _AiService_FaceRecognition_Handler(srv interface{}, ctx context.Context, de
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AiService_TfClassification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(TfClassificationRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AiServiceServer).TfClassification(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AiService_TfClassification_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AiServiceServer).TfClassification(ctx, req.(*TfClassificationRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AiService_CaffeClassification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CaffeClassificationRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AiServiceServer).CaffeClassification(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AiService_CaffeClassification_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AiServiceServer).CaffeClassification(ctx, req.(*CaffeClassificationRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// AiService_ServiceDesc is the grpc.ServiceDesc for AiService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@@ -117,6 +189,14 @@ var AiService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "FaceRecognition",
|
||||
Handler: _AiService_FaceRecognition_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "TfClassification",
|
||||
Handler: _AiService_TfClassification_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CaffeClassification",
|
||||
Handler: _AiService_CaffeClassification_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "aisvc.proto",
|
||||
|
@@ -433,7 +433,7 @@ type (
|
||||
@server (
|
||||
group: storage // 微服务分组
|
||||
prefix: /api/auth/storage // 微服务前缀
|
||||
timeout: 10s // 超时时间
|
||||
timeout: 20s // 超时时间
|
||||
maxBytes: 104857600 // 最大请求大小
|
||||
signature: false // 是否开启签名验证
|
||||
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,AuthorizationMiddleware,NonceMiddleware // 注册中间件
|
||||
|
@@ -22,8 +22,10 @@ Verbose: false
|
||||
AiSvcRpc:
|
||||
Etcd:
|
||||
Hosts:
|
||||
- 127.0.0.1:2379
|
||||
- 1.95.0.111:2379
|
||||
Key: aisvc.rpc
|
||||
User: root
|
||||
Pass: LDQ20020618xxx
|
||||
# 日志配置
|
||||
Log:
|
||||
# 服务名称
|
||||
|
@@ -202,7 +202,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
),
|
||||
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||
rest.WithPrefix("/api/auth/storage"),
|
||||
rest.WithTimeout(10000*time.Millisecond),
|
||||
rest.WithTimeout(20000*time.Millisecond),
|
||||
rest.WithMaxBytes(104857600),
|
||||
)
|
||||
|
||||
|
@@ -69,6 +69,13 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) {
|
||||
if face != nil {
|
||||
faceId = face.GetFaceId()
|
||||
}
|
||||
// 分类
|
||||
classification, err := l.svcCtx.AiSvcRpc.TfClassification(l.ctx, &pb.TfClassificationRequest{Image: bytes})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fmt.Println(classification.ClassName)
|
||||
fmt.Println(classification.Score)
|
||||
|
||||
// 解析 EXIF 信息
|
||||
exif, err := l.parseExifData(result.Exif)
|
||||
|
@@ -1,120 +0,0 @@
|
||||
# to build this docker image:
|
||||
# docker build -f opencv.Dockerfile -t schisandra-cloud-album-server .
|
||||
# docker build --build-arg OPENCV_VERSION="4.x" --build-arg OPENCV_FILE="https://github.com/opencv/opencv/archive/refs/heads/4.x.zip" --build-arg OPENCV_CONTRIB_FILE="https://github.com/opencv/opencv_contrib/archive/refs/heads/4.x.zip" -f opencv.Dockerfile -t schisandra-cloud-album-server .
|
||||
|
||||
FROM ubuntu:20.04 AS opencv-builder
|
||||
|
||||
LABEL maintainer="landaiqing <<landaiqing@126.com>>"
|
||||
|
||||
RUN sed -i 's|http://archive.ubuntu.com/ubuntu/|http://mirrors.aliyun.com/ubuntu/|g' /etc/apt/sources.list
|
||||
|
||||
ENV TZ=Europe/Madrid
|
||||
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends --fix-missing \
|
||||
tzdata git build-essential cmake pkg-config wget unzip libgtk2.0-dev \
|
||||
curl ca-certificates libcurl4-openssl-dev libssl-dev \
|
||||
libavcodec-dev libavformat-dev libswscale-dev libtbb2 libtbb-dev \
|
||||
libharfbuzz-dev libfreetype6-dev \
|
||||
libjpeg-turbo8-dev libpng-dev libtiff-dev libdc1394-22-dev nasm && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ARG OPENCV_VERSION="4.10.0"
|
||||
|
||||
ENV OPENCV_VERSION=$OPENCV_VERSION
|
||||
|
||||
ARG OPENCV_FILE="https://github.com/opencv/opencv/archive/${OPENCV_VERSION}.zip"
|
||||
|
||||
ENV OPENCV_FILE=$OPENCV_FILE
|
||||
|
||||
ARG OPENCV_CONTRIB_FILE="https://github.com/opencv/opencv_contrib/archive/${OPENCV_VERSION}.zip"
|
||||
|
||||
ENV OPENCV_CONTRIB_FILE=$OPENCV_CONTRIB_FILE
|
||||
|
||||
RUN curl -Lo opencv.zip ${OPENCV_FILE} && \
|
||||
unzip -q opencv.zip && \
|
||||
curl -Lo opencv_contrib.zip ${OPENCV_CONTRIB_FILE} && \
|
||||
unzip -q opencv_contrib.zip && \
|
||||
rm opencv.zip opencv_contrib.zip && \
|
||||
cd opencv-${OPENCV_VERSION} && \
|
||||
mkdir build && cd build && \
|
||||
cmake -D CMAKE_BUILD_TYPE=RELEASE \
|
||||
-D WITH_IPP=OFF \
|
||||
-D WITH_OPENGL=OFF \
|
||||
-D WITH_QT=OFF \
|
||||
-D WITH_FREETYPE=ON \
|
||||
-D CMAKE_INSTALL_PREFIX=/usr/local \
|
||||
-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-${OPENCV_VERSION}/modules \
|
||||
-D OPENCV_ENABLE_NONFREE=ON \
|
||||
-D WITH_JASPER=OFF \
|
||||
-D WITH_TBB=ON \
|
||||
-D BUILD_JPEG=ON \
|
||||
-D WITH_SIMD=ON \
|
||||
-D ENABLE_LIBJPEG_TURBO_SIMD=ON \
|
||||
-D BUILD_DOCS=OFF \
|
||||
-D BUILD_EXAMPLES=OFF \
|
||||
-D BUILD_TESTS=OFF \
|
||||
-D BUILD_PERF_TESTS=ON \
|
||||
-D BUILD_opencv_java=NO \
|
||||
-D BUILD_opencv_python=NO \
|
||||
-D BUILD_opencv_python2=NO \
|
||||
-D BUILD_opencv_python3=NO \
|
||||
-D OPENCV_GENERATE_PKGCONFIG=ON .. && \
|
||||
make -j $(nproc --all) && \
|
||||
make preinstall && make install && ldconfig && \
|
||||
cd / && rm -rf opencv*
|
||||
|
||||
|
||||
FROM golang:1.23.1-alpine AS go-builder
|
||||
|
||||
RUN apk add --no-cache gcc musl-dev libgcc libstdc++ cmake
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
ENV CGO_ENABLED=1
|
||||
|
||||
ENV CGO_CFLAGS="-I/usr/local/include/opencv4"
|
||||
|
||||
ENV CGO_LDFLAGS="-L/usr/local/lib -lopencv_core -lopencv_imgproc -lopencv_highgui"
|
||||
|
||||
ENV GOOS=linux
|
||||
|
||||
ENV GOARCH=amd64
|
||||
|
||||
ENV GOPROXY=https://goproxy.cn,direct
|
||||
|
||||
COPY --from=opencv-builder /usr/local/lib /usr/local/lib
|
||||
COPY --from=opencv-builder /usr/local/include/opencv4 /usr/local/include/opencv4
|
||||
|
||||
RUN go mod download
|
||||
|
||||
RUN go build -o schisandra-cloud-album-server
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||
|
||||
RUN apk add --no-cache tzdata
|
||||
|
||||
ENV TZ=Asia/Shanghai
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=go-builder /app/schisandra-cloud-album-server .
|
||||
|
||||
COPY --from=go-builder /app/config.yaml .
|
||||
|
||||
COPY --from=go-builder /app/resource ./resource
|
||||
|
||||
COPY --from=go-builder /app/config/rbac_model.conf ./config/rbac_model.conf
|
||||
|
||||
COPY --from=opencv-builder /usr/local/lib /usr/local/lib
|
||||
|
||||
COPY --from=opencv-builder /usr/local/include/opencv4 /usr/local/include/opencv4
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["./schisandra-cloud-album-server"]
|
Reference in New Issue
Block a user