C5—Qt生成特定应用的COE文件 2021-11-18

Qt生成COE文件

背景:

在vivado中某些IP核的配置中,需要使用COE(Coefficient)文件来传递参数,例如MATLAB自动生成FIR滤波器所需的滤波系数文件以及RAM中的初始化数据文件等。COE文件是一种ASCII文本文件,文件头部定义数据基数(Radix),可以时2、10或16。数据以向量的形式给出,向量以分号结尾,向量之间用逗号隔开。Vivado会解析COE文件格式,并在生成IP核时导出相关的MIF格式文件,用于行为级仿真。
本次小项目生成COE文件是为了配置Block Memory Generator IP核时使用,将数据写入COE文件,传递给IP核,在vivado的应用中使用。如何写coe文件与vivado具体的应用息息相关,本次应用是将png、jpg等常见的图片灰度化、二值化后,写入coe中,位宽是128bit。coe中以32个16进制数据为一组,每个16进制是4bit,32个即为128bit。
Matlab作为主流的算法平台与验证平台之一往往成为很多数据的重要来源,因此在matlab下生成coe文件是非常普遍的操作,例如使用matlab提供的一些文件操作函数如fprint和fclose。除此之外,python提供了扩展库Numpy,可以方便地对矩阵操作,利用python生成coe也是比较简便的。正在写qt的总结,此处使用qt写coe文件,也并不复杂。

coe文件格式

memory_initialization_radix = 10;
memory_initialization_vector = 1,2,3,4,5,6,…,99
其中:
memory_initialization_radix 是数值格式
memory_initialization_vector 是初始化的数值向量,分别对应各个深度
结合我们的应用需求,数值格式为16,数值向量是32个16进制数为一组。

开发步骤

1.搭建界面,简单布局
C5—Qt生成特定应用的COE文件 2021-11-18_第1张图片
选择图片弹出文件对话框,选择将要生成coe的图片;QtextEdit用来打印当前状态,与用户做一些简单交互;clearBox用来清除状态提示框。
2.初始化Init()
初始化成员变量,打印当前状态,连接按钮信号和对应的槽。由于我的应用中只对应三种分辨率的图片,分别是“25601600”、“20481080”、“1024*768”。因此用户输入其他分辨率的图片将会提示输入不正确,程序不做任何处理。
3.槽函数
clearBox按钮的槽函数直接将edit清除就可以。选择图片的按钮则是要,打开文件选择框,用户选择标准图片后,对图片进行灰度化,二值化,然后移位处理,最后写成coe文件。
首先,通过用户点击路径“获取”图片对象

    QString fileName = QFileDialog::getOpenFileName(this,"Seclect img","F:\\");
    if(fileName.isEmpty())
    {
        return;
    }
    QFileInfo fileInfo=QFileInfo(fileName);//获取文件信息
    QImage img = QImage(fileName);

然后,使用QImage方法,对其进行灰度化

img=img.convertToFormat(QImage::Format_Grayscale8);//将png、jpg转换成8位灰度 这个算法是Qt内部实现的

然后,保存灰度化数据

savePix = img.bits(); //保存灰度化数据

然后计算二值化阈值,采用求平均的方法(结合代码查看)

int  threshold_value=average(savePix,height*width);

然后,根据灰度化数据计算并保存二值化数据

 for(int j=0;j<width*height;j++)//计算保存二值化数据
    {
        
        if(savePix[j]>threshold_value)
        {tempbin[j]=0;}
        else
        {tempbin[j]=1;}
    }

然后,将二值化数据移位处理成1个16进制数的4bit分别代表4个像素数据

for (int i = 0;i<width*height/4;i++ )
        for(int j = 0;j<4;j++)
        {
            if(j==0)
            {
                temphex = tempbin[i*4];
            }
            else
            {
                temphex=(temphex<<1)+tempbin[i*4+j];
            }
        }
        dataBuf[i]=temphex;

最后,调用写coe文件函数,将dataBuf中的数据写到coe中

bool Widget::writeCoe7000(QFileInfo info)
{
     QString str=info.baseName();
     str.append(".coe");
     FILE *fp= fopen(str.toLatin1(),"w");
     fprintf(fp, "memory_initialization_radix = 16;\n");
     fprintf(fp, "memory_initialization_vector = \n");
     for(int q=0;q<6144;q++)
     {
         for (int i =0;i<32 ;i++ )
         {
             {fprintf(fp, "%x", dataBuf[q*32+i]);}
         }
        fprintf(fp, ",\n");//每128bit,即每32个16进制数据写一个逗号加换行
     }
     fclose(fp);
     return true;
}

说明:

希望总结和整理的部分是,图片二值化的方法,移位处理的方法,使用c++的方式进行文件的写入操作,简单程序的快速搭建思路;需要澄清的是,由于下位机设备特殊,因此不同分辨率的图片做了不同调整,此处略过即可。

效果

生成的coe与程序截图
C5—Qt生成特定应用的COE文件 2021-11-18_第2张图片

代码

main

#include "widget.h"

#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

widget.cpp

#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    Init();
}
Widget::~Widget()
{
    delete ui;
}
void Widget::Init()
{
    //init 成员变量
    height = 0;
    width = 0;
    temphex = 0;
    currentState("请选择标准图片2560*1600或2048*1080或1024*768"); //调用显示当前状态函数
    currentState("请注意在使用时将最后一个逗号改为分号才能进入vivado使用"); //调用显示当前状态函数
    //关联按钮信号与槽
    connect(ui->clearBtn,SIGNAL(clicked(bool)),ui->textEdit,SLOT(clear()));
    connect(ui->selBtn,SIGNAL(clicked(bool)),this,SLOT(doProcessSelBtn(bool)));
}
void Widget::currentState(QString str)
{
    dataTime = QDateTime::currentDateTime();
    dataStr= dataTime.toString( "yyyy年MM月dd日 hh:mm:ss");
    dataStr.append(":   ");
    dataStr.append(str);
    ui->textEdit->append(dataStr); //设置显示当前系统时间
    dataStr.clear();
}
int Widget::average(uchar *ary, int len)
{
     int sum=0;
     for (int i=0;i<len;i++)
     {
         sum+=ary[i];
     }
     return sum/len;
}
void Widget::doProcessSelBtn(bool)
{
    QString fileName = QFileDialog::getOpenFileName(this,"Seclect img","F:\\");
    if(fileName.isEmpty())
    {
        return;
    }
    QFileInfo fileInfo=QFileInfo(fileName);//获取文件信息
    QImage img = QImage(fileName);
    width = img.width();
    height = img.height();
    qDebug()<<width<<height;
    if((width!=2560)||(height!=1600))//不是DLP9000X
    {
        if((width!=2048)||(height!=1080))//不是DLP9500
        {
            if((width!=1024)||(height!=768))//不是DLP7000
            {
                currentState("此次输入无效,请选择标准图片输入");
                return;
            }
        }
    }
    //判断过程结束,是标准图片
    img=img.convertToFormat(QImage::Format_Grayscale8);//将png、jpg转换成8位灰度 这个算法是Qt内部实现的
    savePix = img.bits(); //保存灰度化数据
    tempbin = new int[height*width];//二值化数据存储空间
    int  threshold_value=average(savePix,height*width);//二值化算法(求平均)
    for(int j=0;j<width*height;j++)//计算保存二值化数据
    {
        //这里01赋值已经验证过
        if(savePix[j]>threshold_value)
        {tempbin[j]=0;}
        else
        {tempbin[j]=1;}
    }
    dataBuf = new char[width*height/4];//使用一个数组元素保存四个像素数据。(一个16进制为4位二进制)
    for (int i = 0;i<width*height/4;i++ )//1024000=2560*1600/4,相当于每一个databuf中保存
    {
        for(int j = 0;j<4;j++)
        {
            if(j==0)
            {
                temphex = tempbin[i*4];
            }
            else
            {
                temphex=(temphex<<1)+tempbin[i*4+j];//已经验证!!!
            }
        }
        dataBuf[i]=temphex;
    }
    if(width==2560)//9000X
    {
       if(writeCoe9000x(fileInfo))
       {
           currentState("DLP9000X---COE文件已生成,请在软件目录下查看!");
       }
       else
       {
           currentState("DLP9000X---COE文件生成失败,请重新输入!");
       }
    }
    if(width==2048)//9500
    {
        if(writeCoe9500(fileInfo))
        {
            currentState("DLP9500---COE文件已生成,请在软件目录下查看!");
        }
        else
        {
            currentState("DLP9500---COE文件生成失败,请重新输入!");
        }
    }
    if(width==1024)//7000
    {
        if(writeCoe7000(fileInfo))
        {
            currentState("DLP7000---COE文件已生成,请在软件目录下查看!");
        }
        else
        {
            currentState("DLP7000---COE文件生成失败,请重新输入!");
        }
    }
    delete [] tempbin;
    delete [] dataBuf;
}
bool Widget::writeCoe7000(QFileInfo info)
{
     QString str=info.baseName();
     str.append(".coe");
     FILE *fp= fopen(str.toLatin1(),"w");
     fprintf(fp, "memory_initialization_radix = 16;\n");
     fprintf(fp, "memory_initialization_vector = \n");
     /*说明:
     coe文件一个数据的位宽是128bit,也就是需要32个16进制数,即32*4bit = 128。
     对于7000来说,一行1024个数据,只有一组数据通道,256个16进制数据为一行。
     一帧1024*768的图片总共有1024*768/4个数据保存,而1024*768/4 = 32*6144(32代表一行coe数据,6144代表一共这么多行)
     另外每一行,每128bit,每32个16进制数都要写一个逗号。
     */
     for(int q=0;q<6144;q++)
     {
         for (int i =0;i<32 ;i++ )
         {
             {fprintf(fp, "%x", dataBuf[q*32+i]);}
         }
        fprintf(fp, ",\n");//每128bit,即每32个16进制数据写一个逗号加换行
     }
     fclose(fp);
     return true;
}
bool Widget::writeCoe9500(QFileInfo info)
{
    QString str=info.baseName();
    QString str2=info.baseName();
    str.append("_ab.coe");
    str2.append("_cd.coe");
    FILE *fp= fopen(str.toLatin1(),"w");
    FILE *fp2= fopen(str2.toLatin1(),"w");
    fprintf(fp, "memory_initialization_radix = 16;\n");
    fprintf(fp, "memory_initialization_vector = \n");
    fprintf(fp2, "memory_initialization_radix = 16;\n");
    fprintf(fp2, "memory_initialization_vector = \n");
    /*说明:
    coe文件一个数据的位宽是128bit,也就是需要32个16进制数,即32*4bit = 128。
    对于9500来说,一行2048个数据,也就是有512个16进制数保存一行数据。那么前256个数据属于是ab通道,后256个数据是cd通道。
    一帧2048*1080的图片总共有2048*1080/4个数据保存,而2048*1080/4 = 32*17280(32代表一行coe数据,17280代表一共这么多行)
    另外每一行,每128bit,每32个16进制数都要写一个逗号。
    */
    for(int q=0;q<17280;q++)
    {
        for (int i =0;i<32 ;i++ )
        {
            if((q*32+i)%512<256)//前320个属于ab通道
            {fprintf(fp, "%x", dataBuf[q*32+i]);}
            else
            {fprintf(fp2, "%x", dataBuf[q*32+i]);}
        }
           if((q*32+31)%512<256)//每128bit,即每32个16进制数据写一个逗号
           {fprintf(fp, ",\n");}
           else
           {fprintf(fp2, ",\n");}
    }
    fclose(fp);
    fclose(fp2);
    return true;
}

bool Widget::writeCoe9000x(QFileInfo info)
{
    QString str=info.baseName();
    QString str2=info.baseName();
    str.append("_ab.coe");
    str2.append("_cd.coe");
    FILE *fp= fopen(str.toLatin1(),"w");
    FILE *fp2= fopen(str2.toLatin1(),"w");
    fprintf(fp, "memory_initialization_radix = 16;\n");
    fprintf(fp, "memory_initialization_vector = \n");
    fprintf(fp2, "memory_initialization_radix = 16;\n");
    fprintf(fp2, "memory_initialization_vector = \n");
    /*说明:
    coe文件一个数据的位宽是128bit,也就是需要32个16进制数,即32*4bit = 128。
    对于9000X来说,一行2560个数据,也就是有640个16进制数保存一行数据。那么前320个数据属于是ab通道,后320个数据是cd通道。
    一帧2560*1600的图片总共有2560*1600/4个数据保存,而2560*1600/4 = 32*32000(32代表一行coe数据,32000代表一共这么多行)
    另外每一行,每128bit,每32个16进制数都要写一个逗号。
    */
    for(int q=0;q<32000;q++)
    {
        for (int i =0;i<32 ;i++ )
        {
            if((q*32+i)%640<320)//前320个属于ab通道
            {fprintf(fp, "%x", dataBuf[q*32+i]);}
            else
            {fprintf(fp2, "%x", dataBuf[q*32+i]);}
        }
           if((q*32+31)%640<320)//每128bit,即每32个16进制数据写一个逗号
           {fprintf(fp, ",\n");}
           else
            {fprintf(fp2, ",\n");}
    }
    fclose(fp);
    fclose(fp2);
    return true;
}

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include 
#include 
#include 
#include 
#include 
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void Init();//初始化成员变量
    void currentState(QString str);//显示当前时间戳加状态信息
    int average(uchar *ary, int len);//计算二值化阈值
private slots:
    void doProcessSelBtn(bool);
    bool writeCoe7000(QFileInfo info);
    bool writeCoe9500(QFileInfo info);
    bool writeCoe9000x(QFileInfo info);
private:
    Ui::Widget *ui;
    QDateTime dataTime;//当前时间
    QString dataStr;//text字符串
    int height;//图片高
    int width;//图片宽
    int *tempbin;//保存一帧图片的二值化数据
    uchar *savePix;//保存一帧图片的灰度化数据(8bit)
    char *dataBuf ;//一个数组元素对应四个像素的二值化数据,即使用一个16进制代表四位二进制。
    uchar temphex;
};
#endif // WIDGET_H

你可能感兴趣的:(Qt积累——小项目,qt,ui,开发语言)