Coder Social home page Coder Social logo

Comments (9)

windosx avatar windosx commented on May 28, 2024

@chaniqure ArcFace并不支持多线程,如果要在协程下调用SDK,请为每一个goroutine使用NewFaceEngine方法创建一个单独的FaceEngine实例(线上环境应该优先考虑使用池化)。如果不符合上述情况,请贴出代码以供分析。

from face-engine.

chaniqure avatar chaniqure commented on May 28, 2024

嗯,我就是池化了引擎,引用的 github.com/jolestar/go-commons-pool/v2 来缓存,引擎对象做了部分修改,关键部分代码代码如下:

type Proxy struct {
	c FeatureClient
	*pool.ObjectPool
	context.Context
}

type FeatureClient struct {
	engine *FaceEngine
	valid  bool
	Name   string
}

// 为了解决循环依赖而单独放到face包里面
var (
	Client *Proxy
)

func InitProxy() *Proxy {
	ctx := context.Background()
	p := &Proxy{}
	p.Context = ctx
	//p.ObjectPool = pool.NewObjectPoolWithDefaultConfig(ctx, &ClientFactory{})
	p.ObjectPool = pool.NewObjectPool(ctx, &ClientFactory{created: atomic.NewInt64(0)}, &pool.ObjectPoolConfig{
		LIFO:                    pool.DefaultLIFO, // 池是否具有关于空闲对象的 LIFO(后进先出)行为 - 始终从池中返回最近使用的对象,或者作为 FIFO(先进先出)队列,其中池始终返回最旧的对象空闲对象池中的对象。
		MaxIdle:                 15,
		MaxTotal:                15,               // 在给定池可以分配的对象数量上限。使用负值表示没有限制
		MinIdle:                 0,                // 要在池中维护的最少空闲对象数。此设置仅在 TimeBetweenEvictionRuns 大于零时才有效。如果是这种情况,则会尝试确保池在空闲对象驱逐运行期间具有所需的最少实例数。如果 MinIdle 的配置值大于 MaxIdle 的配置值,则将使用 MaxIdle 的值。
		MinEvictableIdleTime:    10 * time.Minute, // 对象在被回收之前可以在池中闲置的最短时间(如果有的话 - 请参阅 TimeBetweenEvictionRuns。当非正数时,仅由于空闲时间,不会从池中驱逐任何对象
		EvictionPolicyName:      pool.DefaultEvictionPolicyName,
		EvitionContext:          context.Background(),
		TimeBetweenEvictionRuns: time.Minute,
		NumTestsPerEvictionRun:  15,
		BlockWhenExhausted:      pool.DefaultBlockWhenExhausted}, //是否在池耗尽时调用 ObjectPool.BorrowObject() 方法时阻塞(已达到“活动”对象的最大数量)。
	)
	p.ObjectPool.StartEvictor()
	return p
}

type ClientFactory struct {
	created *atomic.Int64
	mu      sync.Mutex
}

func (f *ClientFactory) MakeObject(ctx context.Context) (*pool.PooledObject, error) {
	f.mu.Lock()
	defer f.mu.Unlock()
	global.Log.Infof("初始化虹软引擎开始,目前已经初始化引擎数量:%d", f.created.Load())
	profile := global.Conf.ArcSoft.Linux
	if runtime.GOOS == "windows" {
		profile = global.Conf.ArcSoft.Windows
	}
	if err := OnlineActivation(profile.AppId, profile.SdkKey, profile.ActiveKey); err != nil {
		global.Log.Errorf("虹软引擎激活失败,堆栈信息: %s", string(debug.Stack()))
		return nil, err
	}
	// 初始化引擎
	engine, err := NewFaceEngine(DetectModeImage,
		OrientPriority0,
		10, // 4.0最大支持10个人脸
		EnableFaceDetect|EnableFaceRecognition|EnableFace3DAngle|EnableLiveness|EnableIRLiveness|EnableAge|EnableGender|EnableMaskDetect|EnableFaceLandMark)
	if err != nil {
		global.Log.Errorf("虹软引擎初始化失败,堆栈信息: %s", string(debug.Stack()))
		return nil, err
	}
	f.created.Inc()
	global.Log.Infof("初始化虹软引擎结束,目前已经初始化虹软引擎数量:%d", f.created.Load())
	return pool.NewPooledObject(
		&FeatureClient{
			engine: engine,
			Name:   fmt.Sprintf("虹软引擎客户端%d号", f.created.Load()),
		}), nil
}

func (f *ClientFactory) DestroyObject(ctx context.Context, object *pool.PooledObject) error {
	// do destroy
	global.Log.Infof("销毁虹软引擎开始,目前已经初始化虹软引擎数量:%d", f.created.Load())
	if c, ok := object.Object.(*FeatureClient); ok {
		err := c.engine.Destroy()
		if err != nil {
			return err
		}
		f.created.Dec()
		global.Log.Infof("销毁虹软引擎结束,目前已经初始化虹软引擎数量:%d", f.created.Load())
	}
	return nil
}

func (f *ClientFactory) ValidateObject(ctx context.Context, object *pool.PooledObject) bool {
	// do validate
	if c, ok := object.Object.(*FeatureClient); ok {
		return c.valid
	}
	return true
}

func (f *ClientFactory) ActivateObject(ctx context.Context, object *pool.PooledObject) error {
	// do activate
	if c, ok := object.Object.(*FeatureClient); ok {
		c.valid = true
	}
	return nil
}

func (f *ClientFactory) PassivateObject(ctx context.Context, object *pool.PooledObject) error {
	// do passivate
	if c, ok := object.Object.(*FeatureClient); ok {
		c.valid = false
	}
	return nil
}


func (p *Proxy) do(f func(c *FeatureClient) (interface{}, error)) (interface{}, error) {
	obj1, err := p.BorrowObject(p.Context)
	if err != nil {
		panic(fmt.Sprintf("租借虹软引擎错误:%s", err))
	}

	o := obj1.(*FeatureClient)
	global.Log.Debugf("%s开始执行", o.Name)
	result, err := f(o)
	defer func() {
		e := p.ReturnObject(p.Context, obj1)
		if e != nil {
			panic(fmt.Sprintf("归还虹软引擎错误:%s", e))
		}
		if e := recover(); e != nil {
			// 将异常写入日志
			global.Log.Error(fmt.Sprintf("[Exception]虹软引擎执行异常: %v\n堆栈信息: %v", err, string(debug.Stack())))
			// 服务器异常
			return
		}
	}()
	return result, err
}

func (p *Proxy) ExtractFaceFeatureByBase64(saveFace bool, faceBase64 string) (*FaceFeature, error) {
	result, err := p.do(func(c *FeatureClient) (interface{}, error) {
		return c.ExtractFaceFeatureByBase64(saveFace, faceBase64)
	})
	return result.(*FaceFeature), err
}

func (p *Proxy) ExtractFaceFeatureByFile(saveFace bool, filePath string) (*FaceFeature, error) {
	result, err := p.do(func(c *FeatureClient) (interface{}, error) {
		return c.ExtractFaceFeatureByFile(saveFace, filePath)
	})
	return result.(*FaceFeature), err
}

func (c *FeatureClient) ExtractFaceFeatureByFile(save bool, path string) (*FaceFeature, error) {
	imageInfo, err := util.GetResizedImageInfo(path)
	if err != nil {
		return nil, err
	}
	info, err := c.engine.DetectFaces(imageInfo.Width, imageInfo.Height, ColorFormatBGR24, imageInfo.DataUInt8)
	if err != nil {
		return nil, errors.New("未检测到人脸信息")
	}
	registerOrNot := RecognitionPhoto
	if save {
		// 注册模式
		registerOrNot = RegisterPhoto
	}
	// 获取单人脸信息
	singleFaceInfoArr := GetSingleFaceInfo(info)
	if len(singleFaceInfoArr) <= 0 {
		return nil, errors.New("未检测到人脸信息")
	}
	// 图像转为特征
	feature, err := c.engine.FaceFeatureExtract(imageInfo.Width, imageInfo.Height, ColorFormatBGR24, imageInfo.DataUInt8, singleFaceInfoArr[0], registerOrNot, 0)
	feature.Release()
	return &feature, err
}

from face-engine.

chaniqure avatar chaniqure commented on May 28, 2024

补充一下,批量抽取特征的图片数量大约2000张,有些时候能全部成功,有些时候只能抽取几十张到几百张,然后报错

from face-engine.

windosx avatar windosx commented on May 28, 2024

@chaniqure 你可以先试试竞态检查。我看了一下你发的日志调用堆栈信息,能确定是多线程调用触发SDK的内存保护导致的。我有空了在看看你发的代码。

from face-engine.

chaniqure avatar chaniqure commented on May 28, 2024

刚刚试了下,竞态检查没问题。辛苦大大帮忙看一下 ,谢谢!!

from face-engine.

windosx avatar windosx commented on May 28, 2024

@chaniqure client.go第100行处,没有对error进行判断就调用了释放方法

from face-engine.

chaniqure avatar chaniqure commented on May 28, 2024

还是不行
微信截图_20210806100729
错误.txt

from face-engine.

windosx avatar windosx commented on May 28, 2024

从这行代码开始查
QQ截图20210806105323

from face-engine.

chaniqure avatar chaniqure commented on May 28, 2024

是批量获取图片的人像信息报错了,之前保存图片相关信息时候,除了要提取人像特征,同时也会获取图片人像的年龄,性别等信息,现在单独
Uploading 错误.txt…
批量抽特征没问题了。刚刚测试了一下,并发获取图片人像信息会报错,而且是100%的会报错

func (c *FeatureClient) GetLiveInfoByFile(path string) (LiveInfo, error) {
	imageInfo, err := util.GetResizedImageInfo(path)
	if err != nil {
		return LiveInfo{}, err
	}
	info, err := c.engine.DetectFaces(imageInfo.Width, imageInfo.Height, ColorFormatBGR24, imageInfo.DataUInt8)
	if err != nil {
		return LiveInfo{}, errors.New("未检测到人脸信息")
	}
	// 获取单人脸信息
	err = c.engine.Process(imageInfo.Width, imageInfo.Height, ColorFormatBGR24, imageInfo.DataUInt8, info, EnableAge|EnableGender|EnableMaskDetect)
	if err != nil {
		return LiveInfo{}, err
	}
	liveInfo := LiveInfo{}
	age, err := c.engine.GetAge()
	if err != nil {
		return liveInfo, err
	}
	fmt.Println(age)
	mask, err := c.engine.GetMask()
	if err != nil {
		return liveInfo, err
	}
	fmt.Println(mask)
	gender, err := c.engine.GetGender()
	if err != nil {
		return liveInfo, err
	}
	fmt.Println(gender)
	return liveInfo, nil
}

from face-engine.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.