[视觉实战案例]Qt实现8位灰度图叠加通道转换成24位三通道图像


前言

在项目中,可能一些输入的图像要求是RGB的图像格式,但是手边只有单通道的黑白相机进行采图,这时就需要将8位的单通道图像转换成24位的三通道图像,一般都是使用3个8位图像叠加成三通道图像。

1、OpenCV实现单通道转三通道

首先创建一个CV_8UC3的三通道图像,图像大小尺寸与单通道图像尺寸一致,然后将3份单通道图像叠加融合填入刚创建的三通道图像。

Mat Widget::convertTo3Channels(const cv::Mat& binImg)
{
    cv::Mat three_channel = cv::Mat::zeros(binImg.rows,binImg.cols,CV_8UC3);
    std::vector<cv::Mat> channels;
    for (int i=0;i<3;i++)
    {
        channels.push_back(binImg);
    }
    cv::merge(channels,three_channel);
    return three_channel;
}

2、判断图像通道数进行转换

这里先判断图像是否是单通道还是四通道,如果是四通道,将图像直接转换成三通道。如果是单通道使用上面程序转换成三通道

        //判断输入图像的通道数
        if(curMatImg.type() == CV_8UC4)
        {
            cvtColor(curMatImg,curMatImg,COLOR_RGBA2RGB);
            qDebug()<<"width"<<curMatImg.cols<<curMatImg.rows;
        }
        else if(curMatImg.type() == CV_8UC1)
        {
            curMatImg = convertTo3Channels(curMatImg);
        }
        else
        {
            qDebug()<<"Img is 3 Channels";
        }

3、程序实现代码

transWidget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QSettings>
#include <QFileInfo>
#include <QFileDialog>
#include <QDebug>
#include <QMessageBox>
#include "opencv2/opencv.hpp"

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:

    void on_btn_trans_clicked();

    void on_btn_close_clicked();

    void on_btn_OpenFile_clicked();

    void on_btn_NextPic_clicked();

    void on_btn_LoadPic_clicked();

    void on_btn_SaveImgPath_clicked();

private:
    Ui::Widget *ui;

    QImage curImg;
    //文件夹中图像数量
    int PicNum;
    //文件夹当前加载图像下标
    int CurPicIndex=0;
    //批量加载图像文件夹的路径
    QString file_path;
    //当前图像名称
    QString CurfileName;
    //图像名称缓存
    QMap<int,QString> PicNameInfo;
    //保存文件夹文件数量
    int saveFile_Num;
    //Mat转换QImage
    QImage cvMat2QImage(const cv::Mat& mat);
    //QImage转换Mat
    cv::Mat QImage2Mat(QImage image);
    //图像转换到三通道
    cv::Mat convertTo3Channels(const cv::Mat& binImg);
    void checkCurPicIndex();
};

#endif // WIDGET_H

transWidget.cpp

#pragma execution_character_set("utf-8")
#include "widget.h"
#include "ui_widget.h"

using namespace cv;
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->btn_NextPic->setEnabled(false);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_btn_trans_clicked()
{
    //保存图像
    if(ui->edt_ImgSavePath->text().isEmpty())
    {
        QMessageBox::critical(NULL, "critical", "未指定保存文件夹,请设置!");
        return;
    }
    else
    {
        Mat curMatImg = QImage2Mat(curImg);
        //判断输入图像是不是8位灰度图像
        if(curMatImg.type() == CV_8UC4)
        {
            cvtColor(curMatImg,curMatImg,COLOR_RGBA2RGB);
            qDebug()<<"width"<<curMatImg.cols<<curMatImg.rows;
        }
        else if(curMatImg.type() == CV_8UC1)
        {
            curMatImg = convertTo3Channels(curMatImg);
        }
        else
        {
            qDebug()<<"Img is 3 Channels";
        }
        QImage QimgTrans = cvMat2QImage(curMatImg);
        QImage ShowTranImg = QimgTrans.scaled(ui->lbl_RGBImg->size(),Qt::KeepAspectRatio);
        ui->lbl_RGBImg->setPixmap(QPixmap::fromImage(ShowTranImg));
        QString SaveFilePath = ui->edt_ImgSavePath->text() + "/trans_" + QString::number(saveFile_Num) +".bmp";
        QimgTrans.save(SaveFilePath);
        saveFile_Num++;
    }
}


Mat Widget::QImage2Mat(QImage image)
{
    Mat cvMat;
    qDebug()<<image.format();
    switch(image.format())
    {
    case QImage::Format_ARGB32:
    case QImage::Format_RGB32:
        cvMat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());
        cv::cvtColor(cvMat, cvMat, COLOR_BGRA2BGR);
        break;
    case QImage::Format_ARGB32_Premultiplied:
        cvMat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());
        break;
    case QImage::Format_RGB888:
        cvMat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());
        cv::cvtColor(cvMat, cvMat, COLOR_BGR2RGB);
        break;
    case QImage::Format_Indexed8:
        cvMat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());
        break;
    case QImage::Format_Grayscale8:
        cvMat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());
        break;
    }
    return cvMat;
}


QImage Widget::cvMat2QImage(const cv::Mat &mat)
{
    switch ( mat.type() )
    {
    // 8-bit  4 channel
    case CV_8UC4:
    {
        QImage image( (const uchar*)mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_RGB32 );
        return image;
    }
        
        // 8-bit  3 channel
    case CV_8UC3:
    {
        QImage image( (const uchar*)mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_RGB888 );
        return image.rgbSwapped();
    }
        
        // 8-bit  1 channel
    case CV_8UC1:
    {
        static QVector<QRgb> sColorTable;
        
        // only create our color table once
        
        if ( sColorTable.isEmpty() )
            
        {
            
            for ( int i = 0; i < 256; ++i )
                
                sColorTable.push_back( qRgb( i, i, i ) );
            
        }
        
        QImage image( mat.data, mat.cols, mat.rows, mat.step, QImage::Format_Indexed8 );
        
        image.setColorTable( sColorTable );
        
        return image;
    }
        
    default:
        qDebug("Image format is not supported: depth=%d and %d channels\n", mat.depth(), mat.channels());
        qWarning() << "cvMatToQImage - cv::Mat image type not handled in switch:" << mat.type();
        break;
    }
    
    return QImage();
}

Mat Widget::convertTo3Channels(const cv::Mat& binImg)
{
    cv::Mat three_channel = cv::Mat::zeros(binImg.rows,binImg.cols,CV_8UC3);
    std::vector<cv::Mat> channels;
    for (int i=0;i<3;i++)
    {
        channels.push_back(binImg);
    }
    //    cv::merge()
    cv::merge(channels,three_channel);
    return three_channel;
}

void Widget::on_btn_close_clicked()
{
    close();
}

void Widget::on_btn_OpenFile_clicked()
{
    QSettings setting("./Setting.ini", QSettings::IniFormat);          //为了能记住上次打开的路径
    QString lastPath = setting.value("LastFilePath").toString();
    
    file_path = QFileDialog::getExistingDirectory(this, "请选择文件夹路径...", lastPath);
    
    if(file_path.isEmpty())
    {
        QMessageBox::warning(this,"Warning","未选择文件夹,请重新选择文件夹!");
        return;
    }
    else
    {
        PicNameInfo.clear();
        CurPicIndex = 0;
        QDir dir(file_path);  //改文件夹已经判断是否存在,保证当前必定存在
        QStringList filter;
        filter<<"*.bmp"<<"*.jpg"<<"*.png";
        QFileInfoList list = dir.entryInfoList(filter, QDir::Files);        //获取文件信息列表
        if(!list.isEmpty())
        {
            //            qDebug() << list.count();//输出图片名
            for(int i=0;i<list.count();i++){
                QFileInfo fileInfo = list.at(i);
                qDebug() << fileInfo.fileName();//输出图片名
                PicNameInfo.insert(i,fileInfo.fileName());
            }
            CurfileName = PicNameInfo.value(0);
            QString picPathShow = file_path + "/" + PicNameInfo.value(0);
            ui->edt_ImgFolderPath->setText(picPathShow);
            curImg.load(picPathShow);
            QImage ShowImg = curImg.scaled(ui->lbl_GrayImg->size(),Qt::KeepAspectRatio);
            ui->lbl_GrayImg->setPixmap(QPixmap::fromImage(ShowImg));
            ui->lbl_RGBImg->clear();
            checkCurPicIndex();
        }
        else
        {
            QMessageBox::warning(this,"Warning","该文件夹为空,请重新选择文件夹!");
            return;
        }
    }
    
}

void Widget::on_btn_NextPic_clicked()
{
    CurPicIndex++;
    CurfileName = PicNameInfo.value(CurPicIndex);
    QString curPicPath = file_path + "/" + PicNameInfo.value(CurPicIndex);
    ui->edt_ImgFolderPath->setText(curPicPath);
    curImg.load(curPicPath);
    QImage ShowImg = curImg.scaled(ui->lbl_GrayImg->size(),Qt::KeepAspectRatio);
    ui->lbl_GrayImg->setPixmap(QPixmap::fromImage(ShowImg));
    ui->lbl_RGBImg->clear();
    checkCurPicIndex();
}

void Widget::on_btn_LoadPic_clicked()
{
    QSettings setting("./Setting.ini", QSettings::IniFormat);          //为了能记住上次打开的路径
    QString lastPath = setting.value("LastFilePath").toString();
    QFileInfo file;
    QString PicPath =QFileDialog::getOpenFileName(this,QString::fromLocal8Bit("选择图像文件"),lastPath,"图像文件 (*.bmp *.jpg *.png)");
    
    if(PicPath.isEmpty())
    {
        return;
    }
    else
    {
        file = QFileInfo(PicPath);
        //            CurfileName = file.fileName();  //获取文件名
        //            qDebug() << CurfileName;//输出图片名
        qDebug()<<"加载图像成功";
        //        QImage img(PicPath);
        curImg.load(PicPath);
        QImage ShowImg = curImg.scaled(ui->lbl_GrayImg->size(),Qt::KeepAspectRatio);
        ui->lbl_GrayImg->setPixmap(QPixmap::fromImage(ShowImg));
        ui->lbl_RGBImg->clear();
        //        ui->graphicsView->fitInView(ImageItem);
    }
}

void Widget::checkCurPicIndex()
{
    if(PicNameInfo.size()>1)
    {
        if(CurPicIndex == 0)
        {
            //            ui->btnPreviousPic->setEnabled(false);
            ui->btn_NextPic->setEnabled(true);
        }
        else if(CurPicIndex == PicNameInfo.count()-1)
        {
            ui->btn_NextPic->setEnabled(false);
            //            ui->btnPreviousPic->setEnabled(true);
        }
        else
        {
            //            ui->btnPreviousPic->setEnabled(true);
            ui->btn_NextPic->setEnabled(true);
        }
    }
    
}
void Widget::on_btn_SaveImgPath_clicked()
{
    QSettings setting("./Setting.ini", QSettings::IniFormat);          //为了能记住上次打开的路径
    QString lastPath = setting.value("LastFilePath").toString();
    
    QString save_path = QFileDialog::getExistingDirectory(this, "请选择文件夹路径...", lastPath);
    ui->edt_ImgSavePath->setText(save_path);
    QDir *dir = new QDir(save_path);
    QStringList filter;
    QFileInfoList fileInfoList = dir->entryInfoList(filter);
    saveFile_Num  = fileInfoList.count()-2;
}

4、实现效果

13.GrayTo3channel.gif

5、源码学习

最后附上源码学习,可以在仓库按需自取,仓库中代码仅供学习使用。
GrayTo3channel

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容