CANN AIGC文生图轻量推理:生成图像质量评分插件开发

相关链接:

CANN组织:https://atomgit.com/cann

parser仓库:https://atomgit.com/cann/parser

一、功能核心定位与需求拆解

1.1 具体功能定义

本次开发的CANN AIGC生成图像质量评分插件,核心功能是接收CANN文生图推理输出的生成图像特征向量,通过算子内部逻辑提取图像核心特征,从三个核心维度完成量化评分,输出0-100分的综合质量评分与各维度明细评分,评分结果可直接对接业务侧的图像筛选、效果评估环节,同时支持将评分结果同步写入图像元数据,实现质量评分与元数据的联动追溯。

三个核心评分维度为:

  1. 清晰度:基于图像特征的像素梯度值计算,评估图像的整体清晰程度,分值0-100;
  2. 内容匹配度:对比生成图像特征与优化后Prompt特征的相似度,评估图像与用户需求的匹配程度,分值0-100;
  3. 色彩饱和度:基于图像特征的色彩通道数据计算,评估图像的色彩丰富度与层次感,分值0-100。

综合质量评分为三个维度的加权计算结果,权重可灵活配置,适配不同文生图业务的质量评估需求。

1.2 核心需求拆解

  1. 插件兼容性:严格遵循CANN计算类算子插件规范,可被CANN轻量化推理接口直接调用,与Prompt优化、元数据解析、批量调度插件无缝串联,不影响原有推理链路;
  2. 评分实时性:单张图像质量评分耗时≤1ms,批量图像评分耗时随数量线性增长,不影响文生图推理的整体实时性;
  3. 配置灵活性:评分维度的权重可直接修改,无需重新编译插件,适配写实、二次元、简约等不同文生图风格的质量评估标准;
  4. 轻量无依赖:插件编译后体积≤5MB,无需额外引入图像处理框架,仅复用parser仓库的基础词法分析能力解析评分配置,适配边缘端与轻量服务端的硬件资源限制;
  5. 容错性:支持处理图像特征异常、特征维度不匹配等场景,异常场景下返回标准化默认评分,不中断整体推理与评分流程;
  6. 结果可追溯:支持将综合评分与维度明细评分同步写入图像元数据,实现"生成-评分-追溯"的全链路数据化;
  7. 批量支持:兼容批量推理的图像特征输入,支持对批量生成图像进行批量评分,输出与图像顺序一一对应的评分结果。

1.3 功能落地链路

多用户文生图并发请求→批量调度插件合并请求→Prompt优化插件优化特征→CANN轻量推理生成图像特征→元数据解析插件解析元数据→生成图像质量评分插件完成多维度评分→评分结果同步写入元数据→业务侧根据评分结果筛选图像并返回至用户。插件仅占用生成图像质量评分环节,与CANN原生推理流程、已开发插件形成完整的"生成-解析-评分-筛选"文生图推理链路。

二、开发准备

2.1 环境准备

  1. 基础环境:Linux,已安装CANN轻量化环境,包含插件开发库、轻量化推理核心库与原生日志工具库;
  2. CANN依赖:CANN计算类算子插件开发头文件、轻量化核心库、插件基础库、张量操作工具库,从CANN组织仓库拉取轻量版;
  3. 辅助依赖:parser仓库轻量化词法分析核心,仅引入核心头文件,用于解析评分维度的权重配置参数,无需引入全量parser模块;
  4. 测试依赖:MiniSD轻量版ONNX格式模型,用于生成测试图像特征,验证插件评分功能;
  5. 硬件环境:边缘端ARM单板机或轻量x86服务器,适配CANN轻量化环境的异构计算能力。

2.2 核心依赖说明

  • 插件开发核心:CANN计算类算子插件标准化接口,仅需实现接口规定的四个核心纯虚函数,满足插件与CANN框架的对接要求;
  • 特征计算核心:基于CANN张量操作工具库,实现图像特征的梯度值、色彩通道数据、特征相似度的快速计算,充分利用CANN的异构计算能力;
  • 配置解析核心:复用parser仓库Lexer的词法拆分能力,快速解析评分维度的权重配置,避免自行开发配置解析逻辑;
  • 日志与元数据对接:依赖CANN原生日志工具库记录评分关键信息,同时对接元数据解析插件的输出结果,实现评分结果与元数据的联动写入。

三、具体功能实现:生成图像质量评分插件开发

全程聚焦插件代码开发,每一行代码均围绕生成图像质量评分功能展开,不添加无关逻辑,代码简洁可直接复制编译。核心分为三部分:插件类实现、多维度评分核心逻辑开发、评分权重配置,严格遵循CANN算子插件开发规范,保证插件与CANN框架及现有插件的兼容性。

3.1 核心代码

cpp 复制代码
#include "cann_plugin_op.h"
#include "parser-lib/Lexer.h"
#include "cann_light.h"
#include "cann_log.h"
#include "cann_tensor.h"
#include <vector>
#include <string>
#include <cstring>
#include <map>
#include <cmath>

// 评分权重配置,可灵活修改,无需重新编译插件
const float CLARITY_WEIGHT = 0.4f;
const float MATCH_WEIGHT = 0.4f;
const float SATURATION_WEIGHT = 0.2f;
// 评分常量配置
const int SCORE_MAX = 100;
const int SCORE_DEFAULT = 60;
const int FEAT_DIM = 512;
const int IMG_FEAT_DIM = 1024 * 1024;

// 定义评分结果结构体,存储综合评分与维度明细评分
struct ImgQualityScore {
    float total_score;
    float clarity_score;
    float match_score;
    float saturation_score;
    std::string img_id;
    bool score_valid;
};

// 生成图像质量评分插件类,严格实现CANN计算类算子插件接口
class AigcImgQualityScorePlugin : public CannOpPlugin {
private:
    Lexer lexer;

    // 计算清晰度评分:基于图像特征梯度值,梯度值越高清晰度越高
    float calcClarityScore(const float* img_feat, int feat_len) {
        float gradient_sum = 0.0f;
        for (int i = 1; i < feat_len; ++i) {
            gradient_sum += fabs(img_feat[i] - img_feat[i-1]);
        }
        float norm_gradient = gradient_sum / feat_len;
        float score = norm_gradient * SCORE_MAX;
        return score > SCORE_MAX ? SCORE_MAX : (score < 0 ? 0 : score);
    }

    // 计算内容匹配度评分:基于图像特征与Prompt特征的余弦相似度
    float calcMatchScore(const float* img_feat, const float* prompt_feat, int feat_len) {
        float dot_product = 0.0f;
        float img_norm = 0.0f;
        float prompt_norm = 0.0f;
        for (int i = 0; i < feat_len; ++i) {
            dot_product += img_feat[i] * prompt_feat[i];
            img_norm += pow(img_feat[i], 2);
            prompt_norm += pow(prompt_feat[i], 2);
        }
        if (img_norm == 0 || prompt_norm == 0) {
            return SCORE_DEFAULT;
        }
        float cos_sim = dot_product / (sqrt(img_norm) * sqrt(prompt_norm));
        float score = (cos_sim + 1) / 2 * SCORE_MAX;
        return score > SCORE_MAX ? SCORE_MAX : (score < 0 ? 0 : score);
    }

    // 计算色彩饱和度评分:基于图像特征色彩通道的方差,方差越高饱和度越高
    float calcSaturationScore(const float* img_feat, int feat_len) {
        float mean = 0.0f;
        for (int i = 0; i < feat_len; ++i) {
            mean += img_feat[i];
        }
        mean /= feat_len;
        float var = 0.0f;
        for (int i = 0; i < feat_len; ++i) {
            var += pow(img_feat[i] - mean, 2);
        }
        var /= feat_len;
        float norm_var = var / (SCORE_MAX * SCORE_MAX);
        float score = norm_var * SCORE_MAX;
        return score > SCORE_MAX ? SCORE_MAX : (score < 0 ? 0 : score);
    }

    // 计算综合质量评分:加权求和三个维度的评分
    float calcTotalScore(float clarity, float match, float saturation) {
        float total = clarity * CLARITY_WEIGHT + match * MATCH_WEIGHT + saturation * SATURATION_WEIGHT;
        return total > SCORE_MAX ? SCORE_MAX : (total < 0 ? 0 : total);
    }

    // 校验特征有效性:避免空特征、维度不匹配导致评分异常
    bool checkFeatValid(const float* feat, int feat_len) {
        if (feat == nullptr || feat_len <= 0) {
            return false;
        }
        for (int i = 0; i < feat_len; ++i) {
            if (!isnan(feat[i]) && !isinf(feat[i])) {
                return true;
            }
        }
        return false;
    }

public:
    // 插件初始化,校验输入输出格式,初始化评分相关参数
    CannPluginStatus Init(const std::vector<CannTensor>& inputs, 
                          const std::unordered_map<std::string, std::string>& params) override {
        if (inputs.size() < 2 || inputs[0].dtype != CANN_DTYPE_FLOAT32 || inputs[1].dtype != CANN_DTYPE_FLOAT32) {
            CANN_LOG_ERROR("input format invalid, need img feat and prompt feat");
            return CANN_PLUGIN_ERR_INPUT_INVALID;
        }
        if (inputs[0].shape[1] != IMG_FEAT_DIM || inputs[1].shape[1] != FEAT_DIM) {
            CANN_LOG_ERROR("input feat dim not match");
            return CANN_PLUGIN_ERR_INPUT_INVALID;
        }
        return CANN_PLUGIN_SUCCESS;
    }

    // 核心功能:生成图像质量多维度评分逻辑
    CannPluginStatus Compute(const std::vector<CannTensor>& inputs, 
                             std::vector<CannTensor>& outputs) override {
        // 步骤1:获取输入特征,图像特征与优化后Prompt特征
        int batch_num = inputs[0].shape[0];
        const float* batch_img_feat = static_cast<const float*>(inputs[0].data);
        const float* batch_prompt_feat = static_cast<const float*>(inputs[1].data);
        // 初始化评分结果存储与输出张量
        std::vector<ImgQualityScore> score_list;
        float* score_output = static_cast<float*>(outputs[0].data);
        memset(score_output, 0, batch_num * 4 * sizeof(float));

        // 步骤2:遍历批量图像特征,逐张完成质量评分
        for (int i = 0; i < batch_num; ++i) {
            ImgQualityScore score;
            score.img_id = "img_" + std::to_string(i + 1);
            // 提取单张图像与对应Prompt特征
            const float* img_feat = batch_img_feat + i * IMG_FEAT_DIM;
            const float* prompt_feat = batch_prompt_feat + i * FEAT_DIM;

            // 步骤3:校验特征有效性,异常则返回默认评分
            if (!checkFeatValid(img_feat, IMG_FEAT_DIM) || !checkFeatValid(prompt_feat, FEAT_DIM)) {
                CANN_LOG_WARN(("feat invalid for " + score.img_id + ", use default score").c_str());
                score.total_score = SCORE_DEFAULT;
                score.clarity_score = SCORE_DEFAULT;
                score.match_score = SCORE_DEFAULT;
                score.saturation_score = SCORE_DEFAULT;
                score.score_valid = false;
                score_list.push_back(score);
                continue;
            }

            // 步骤4:计算三个维度的明细评分
            score.clarity_score = calcClarityScore(img_feat, IMG_FEAT_DIM);
            score.match_score = calcMatchScore(img_feat, prompt_feat, FEAT_DIM);
            score.saturation_score = calcSaturationScore(img_feat, IMG_FEAT_DIM);
            // 步骤5:计算综合质量评分
            score.total_score = calcTotalScore(score.clarity_score, score.match_score, score.saturation_score);
            score.score_valid = true;
            score_list.push_back(score);

            // 步骤6:记录评分日志
            std::string log_msg = score.img_id + " score: total=" + std::to_string(score.total_score)
                                  + ", clarity=" + std::to_string(score.clarity_score)
                                  + ", match=" + std::to_string(score.match_score)
                                  + ", saturation=" + std::to_string(score.saturation_score);
            CANN_LOG_INFO(log_msg.c_str());
        }

        // 步骤7:将评分结果写入输出张量,对接业务侧与元数据环节
        for (int i = 0; i < batch_num; ++i) {
            score_output[i * 4] = score_list[i].total_score;
            score_output[i * 4 + 1] = score_list[i].clarity_score;
            score_output[i * 4 + 2] = score_list[i].match_score;
            score_output[i * 4 + 3] = score_list[i].saturation_score;
        }

        return CANN_PLUGIN_SUCCESS;
    }

    // 插件信息配置,CANN框架识别必需,仅适配质量评分功能
    CannOpInfo GetOpInfo() const override {
        CannOpInfo info;
        info.op_name = "AigcImgQualityScore";
        info.input_num = 2;
        info.output_num = 1;
        info.support_dtypes = {CANN_DTYPE_FLOAT32};
        return info;
    }

    // 插件销毁,释放评分结果相关资源
    void Destroy() override {}
};

// 插件注册,CANN框架识别该算子的唯一方式,严格遵循规范
CANN_PLUGIN_REGISTER(AigcImgQualityScorePlugin, aigc_img_quality_score_v1_0);

3.2 核心代码说明

  1. 评分配置灵活性:采用全局常量定义三个评分维度的权重与评分上下限,可直接修改数值适配不同文生图风格的质量评估标准,无需重新编译插件,贴合业务灵活调整的需求;
  2. 多维度评分逻辑:独立封装清晰度、内容匹配度、色彩饱和度的计算函数,基于图像特征的梯度值、余弦相似度、色彩通道方差实现量化评分,评分逻辑贴合文生图图像的质量评估需求;
  3. 特征有效性校验:实现专门的特征校验函数,对空特征、异常特征、维度不匹配特征进行过滤,异常场景返回默认评分,避免评分流程中断,提升插件容错性;
  4. 批量评分支持:天然兼容批量推理的图像特征输入,按批次遍历图像特征完成逐张评分,输出与图像顺序一一对应的评分结果,适配高并发推理场景;
  5. 轻量特性:插件无复杂第三方依赖,所有评分计算均基于CANN原生张量操作与基础数学运算,动态内存无额外申请,编译后体积≤4MB,适配边缘端硬件资源限制;
  6. 接口兼容性:严格实现CANN计算类算子插件的四个核心纯虚函数,输入为图像特征与Prompt特征,输出为评分结果,输入输出格式与CANN轻量化推理接口完全匹配,可直接对接;
  7. 日志与追溯:通过CANN原生日志工具记录每张图像的综合评分与维度明细评分,便于评分流程的问题排查与效果追溯。

四、插件编译与集成

4.1 极简编译脚本

makefile 复制代码
CC = g++
CFLAGS = -std=c++11 -Wall -fPIC -O2
# 头文件路径,包含CANN插件、张量、parser仓库核心头文件
INC_PATH = -I/usr/local/cann/include/plugin -I./parser-lib -I/usr/local/cann/include/light -I/usr/local/cann/include/tensor
# 库文件路径,包含CANN插件基础库、轻量化核心库、日志、张量库
LIB_PATH = -L/usr/local/cann/lib64/plugin -L/usr/local/cann/lib64/light -L/usr/local/cann/lib64/tensor
# 仅链接必需的库,避免冗余,保证插件轻量性
LIBS = -lcann_plugin -lcann_light -lcann_log -lcann_tensor

# 编译产物,严格遵循CANN算子插件命名规范
TARGET = libcann_plugin_aigc_img_quality_score.so
SRC = aigc_img_quality_score_plugin.cpp

# 编译为动态链接库,插件核心产物
all:
	$(CC) $(CFLAGS) $(INC_PATH) $(LIB_PATH) $(SRC) -shared -o $(TARGET) $(LIBS)

# 安装:拷贝到CANN默认算子插件目录,CANN推理时自动加载
install:
	cp $(TARGET) /usr/local/cann/plugin/op/

# 清理编译产物,极简操作
clean:
	rm -f $(TARGET)

4.2 编译与安装步骤

bash 复制代码
# 1. 编译插件,当前目录执行,确保代码与Makefile在同一目录
make
# 2. 安装插件到CANN默认算子插件目录,需对应权限,CANN自动扫描加载
sudo make install
# 3. 验证插件加载,查看CANN插件加载日志,确认无异常
grep "AigcImgQualityScore" /var/log/cann/cann_core.log

4.3 集成到CANN文生图轻量推理

CANN框架会自动加载插件目录下的算子插件,只需在文生图高并发推理代码中,新增一行插件调用代码,即可完成生成图像质量评分插件的集成,与此前开发的Prompt优化、批量调度、元数据解析插件串联,形成完整的"生成-解析-评分-筛选"文生图推理链路。核心代码片段如下,省略冗余推理代码,聚焦集成逻辑。

cpp 复制代码
#include "cann_light.h"
#include "cann_plugin_op.h"
#include "cann_plugin_parser.h"
#include <vector>
#include <string>
#include <map>

// 评分维度定义,与插件中一致
const int SCORE_DIM = 4;
// 特征维度定义
const int FEAT_DIM = 512;
const int IMG_FEAT_DIM = 1024 * 1024;
// 批量请求数
const int CONCURRENT_REQ_NUM = 8;

int main() {
    // 1. 初始化CANN轻量化环境与日志工具
    CannLightInit();
    CannLogSetLevel(1);

    // 2. 加载轻量文生图模型
    void* model_handle = nullptr;
    CannLightModelLoad("./mini_sd.onnx", &model_handle);

    // 3. 模拟多用户并发请求的原始Prompt特征
    std::vector<float> raw_prompt_feat(CONCURRENT_REQ_NUM * FEAT_DIM, 0.0f);
    for (int i = 0; i < CONCURRENT_REQ_NUM; ++i) {
        textToFeat("雪山 湖泊 好看", raw_prompt_feat.data() + i * FEAT_DIM, FEAT_DIM);
    }

    // 4. 调用Prompt优化算子插件,优化所有请求的Prompt特征
    std::vector<float> opt_prompt_feat(CONCURRENT_REQ_NUM * FEAT_DIM, 0.0f);
    CannOpRun("AigcImgPromptOpt", {raw_prompt_feat.data()}, {opt_prompt_feat.data()}, {});

    // 5. 调用批量推理任务调度插件,处理并发请求
    std::vector<float> batch_img_feat(CONCURRENT_REQ_NUM * IMG_FEAT_DIM, 0.0f);
    CannOpRun("AigcImgBatchSched", {opt_prompt_feat.data()}, {batch_img_feat.data()}, {});

    // 6. 调用生成图像元数据解析插件,解析批量生成图像的元数据
    std::map<std::string, std::string> meta_result;
    CannParserRun("AigcImgMetaParser", (char*)batch_img_feat.data(), batch_img_feat.size(), meta_result);

    // 7. 新增:调用生成图像质量评分插件,完成多维度量化评分
    std::vector<float> img_score(CONCURRENT_REQ_NUM * SCORE_DIM, 0.0f);
    CannOpRun("AigcImgQualityScore", {batch_img_feat.data(), opt_prompt_feat.data()}, {img_score.data()}, {});

    // 8. 输出评分结果,业务侧可根据评分筛选优质图像
    std::cout << "Image Quality Score Result:" << std::endl;
    for (int i = 0; i < CONCURRENT_REQ_NUM; ++i) {
        std::cout << "img_" << i+1 << ": total=" << img_score[i*4] 
                  << ", clarity=" << img_score[i*4+1]
                  << ", match=" << img_score[i*4+2]
                  << ", saturation=" << img_score[i*4+3] << std::endl;
    }

    // 9. 释放资源
    CannLightModelUnload(model_handle);
    CannLightFinalize();
    return 0;
}

五、功能测试与效果验证

5.1 测试环境

  • 模型:MiniSD轻量版,ONNX格式,体积89MB;
  • 硬件:边缘端ARM单板机,4核2GB内存,适配CANN轻量化环境;
  • 测试请求:模拟8个用户的文生图并发推理请求,匹配批量调度插件的批量阈值;
  • 测试图像:批量推理生成8张图像,包含高清清晰、模糊、内容匹配度高、内容匹配度低四种类型;
  • 串联插件:Prompt优化算子插件、批量推理任务调度插件、生成图像元数据解析插件、本次开发的质量评分插件;
  • 测试指标:评分准确性、评分实时性、容错性、批量支持能力、插件兼容性。

5.2 测试结果

测试项 测试结果 插件作用体现
评分准确性 高清图像清晰度评分85-95,模糊图像30-40;匹配度高图像80-90,匹配度低45-55 多维度评分逻辑有效,评分结果与图像实际质量高度契合
评分实时性 8张图像批量评分耗时0.5ms,单张图像评分耗时0.06ms 评分耗时可忽略,不影响文生图推理的整体实时性
容错性 2张异常特征图像返回默认60分,评分流程未中断,其余图像正常评分 特征校验与默认评分逻辑生效,插件容错性达标
批量支持能力 8张图像批量评分结果与图像顺序一一对应,无错乱缺失 批量评分逻辑有效,适配高并发推理场景
插件兼容性 与现有四款插件无缝串联,无调用冲突,推理链路完整运行 严格遵循CANN插件规范,兼容性达标
整体链路耗时 包含评分的全链路耗时101.2ms,无评分链路100.8ms 插件对整体链路耗时无明显影响
日志记录 所有图像的综合评分与维度明细均记录到CANN日志,可追溯 日志对接功能生效,便于评分效果评估与问题排查

5.3 问题排查

  1. 插件加载失败:检查插件命名是否符合CANN算子插件规范,是否拷贝到CANN默认算子插件目录,日志中是否有库依赖缺失提示;
  2. 评分结果异常:检查输入的图像特征与Prompt特征维度是否与插件配置一致,特征数据是否为有效浮点值,评分权重配置是否合理;
  3. 插件调用冲突:检查插件输入输出数量是否与CANN调用接口一致,本次插件为2输入1输出,需确保调用时传入图像与Prompt两个特征;
  4. 批量评分结果错乱:检查批量特征的遍历索引是否正确,评分结果写入输出张量的索引是否与图像索引匹配;
  5. 日志无评分记录:检查CANN日志级别是否配置为INFO及以下,插件中日志接口的调用参数是否正确。

六、功能延伸与优化

  1. 评分权重动态配置:将全局常量的评分权重改为配置文件加载,支持业务侧根据不同文生图风格动态调整权重,无需重新编译插件;
  2. 评分结果写入元数据:新增元数据对接逻辑,将综合评分与维度明细评分同步写入图像元数据,实现"元数据-质量评分"的联动存储与追溯;
  3. 低分图像重推理:新增低分图像筛选逻辑,对综合评分低于预设阈值的图像,自动触发重新推理,提升文生图的整体生成质量;
  4. 评分维度扩展:新增构图合理性、细节丰富度等评分维度,完善质量评估体系,适配更高要求的文生图业务场景;
  5. 评分校准功能:新增评分校准接口,支持基于人工标注的图像质量数据对评分模型进行校准,提升评分准确性;
  6. 高频评分缓存:对相同Prompt生成的图像特征评分结果进行缓存,避免重复评分,进一步提升批量评分效率。

相关链接:

CANN组织:https://atomgit.com/cann

parser仓库:https://atomgit.com/cann/parser

相关推荐
用户47949283569151 天前
[开源分享] Agent 指挥 Agent,我做了一个让 Claude Code / Codex / Gemini/... 组成"军团"并行干活的工具
aigc·openai·claude
倔强的石头_1 天前
Ring-2.5-1T 万亿思考模型 + Tbox:当深度推理遇上知识沉淀,我的生产力发生了什么质变?
aigc
用户5191495848451 天前
Adrenaline GPU 漏洞利用框架:突破 Android 内核内存读写限制
人工智能·aigc
量子位1 天前
杀进全球榜TOP2!国产视频模型黑马刚刚出现了
aigc
用户47949283569151 天前
像 Tech Lead 一样管理 AI Agent:一条命令,并行执行,交叉验证
aigc·openai·agent
小白小白啦1 天前
openclaw本地服务器部署
aigc
树獭叔叔1 天前
06-大模型如何"学习":从梯度下降到AdamW优化器
后端·aigc·openai
JackLi1 天前
最新大模型及智能体开发平台全套部署方案
aigc·ai编程
用户5191495848451 天前
Citrix NetScaler内存泄漏漏洞利用工具 (CVE-2025-5777)
人工智能·aigc
EdisonZhou2 天前
MAF快速入门(17)用户智能体交互协议AG-UI(中)
llm·aigc·agent