• 数字图像处理2(C++)


    有话在先

      请跟随博主一起学习“数字图像处理技术”!

      这里开了一个“图像处理”的专栏,博主会根据自己的学习进度及时地将相关的收获分享给大家。那么,博主选用的开发语言是什么呢?在一开始的时候,博主原本想使用M语言,并且还借了一本相关的书——continue——.

    摆好龙门阵

      visual studio 2019 + mfc + photo(bmp)

      如何把 JPG 和 PNG 等图像格式转化为 BMP 格式?简单的操作方法如下:打开“画图”,

      

      然后打开图片:左键点击【文件】→【打开】,在弹出的对话框中找到要打开的图像:

      

      然后,另存为“bmp”格式的图像。左键点击【文件】→【另存为】→【BMP图片】,在弹出的对话框中找到要打开的图像:

      

      接下来的操作就不展示了。

    代码走起

      不急,先使用VS创建一个空项目。

      

      然后在“头文件”里创建三个头文件,分别为:BMP.h、eightbitmap.h、twentyfourbitmap.h。接着在源文件里,创建main.cpp文件。并把下载好的(或通过上述方式转换好的bmp)图像与代码放在一起。

      

       在BMP.h头文件里,写下述代码:

    #pragma once
    #ifndef BMP_H//预处理器
    #define BMP_H
    
    typedef unsigned char BYTE;
    typedef unsigned short WORD;
    typedef unsigned int DWORD;
    typedef long LONG;
    
    //BMP文件头(14字节)
    typedef struct tagBITMAPFILEHEADER {
        //WORD bfType;//位图文件的类型,必须为BM(在结构体中读取会发生错误,所以在函数中读取)
        DWORD bfSize;//位图文件的大小,以字节为单位
        WORD bfReserved1;//位图文件保留字,必须为0
        WORD bfReserved2;//位图文件保留字,必须为0
        DWORD bfOffBits;//位图数据的起始位置,以相对于位图文件头的偏移量表示,以字节为单位
    }BITMAPFILEHEADER;
    
    //BMP信息头(40字节)
    typedef struct tagBITMAPINFOHEADER {
        DWORD biSize;//本结构所占用字节数
        LONG biWidth;//位图的宽度,以像素为单位
        LONG biHeight;//位图的高度,以像素为单位
        WORD biPlanes;//目标设备的级别,必须为1
        WORD biBitCount;//每个像素所需的位数,必须是1(双色),4(16色),8(256色)16(高彩色)或24(真彩色)之一
        DWORD biCompression;//位图压缩类型,必须是0(不压缩),1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
        DWORD biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位
        LONG biXPelsPerMeter;//位图水平分辨率,每米像素数
        LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数
        DWORD biClrUsed;//位图实际使用的颜色表中的颜色数
        DWORD biClrImportant;//位图显示过程中重要的颜色数
    }BITMAPINFOHEADER;
    
    //调色板
    //只有8位位图才用调色板,用像素值作为索引(0~255),调色板中RGB值都是一样的,范围是0~255
    //一个unsigned char的范围刚好是0~255,所以用BYTE类型存储8位位图的像素
    typedef struct tagRGBQUAD {
        BYTE rgbBlue;
        BYTE rgbGreen;
        BYTE rgbRed;
        BYTE rgbReserved;//保留,必须为0
    }RGBQUAD;
    
    //像素信息
    //8位BMP图1个字节代表一个像素,所以可以不用结构存储像素素组,直接用一个指针即可
    typedef struct tagIMAGEDATA {
        BYTE blue;
        BYTE green;
        BYTE red;
    }IMAGEDATA;
    
    #endif

      在eightbitmap.h文件里,写下面代码:

    #pragma once
    #ifndef EIGHTBITMAP_H//预处理器
    #define EIGHTBITMAP_H
    
    #include<iostream>
    #include"BMP.h"
    using namespace std;
    
    class eightBitMap {
    private:
        char imageName[30];//图像名
        int width, height;//图像的宽高
        BITMAPFILEHEADER bmpHead;//文件头
        BITMAPINFOHEADER bmpInfo;//信息头
        BYTE* imagedata = NULL, * newimagedata = NULL;//存储图片像素信息的二维数组
        RGBQUAD* pallet = new RGBQUAD[256];//调色板指针
        FILE* fpin, * fpout;//文件指针
    
                           //平滑算子也是通过模板进行处理的,所以可以把平滑处理和锐化处理通过一个函数实现
        int Template1[3][3]{ 1,1,1,1,1,1,1,1,1 };//平滑模板
        int Template2[3][3]{ 0,-1,0,-1,5,-1,0,-1,0 };//laplace锐化模板,4邻域(原图减去轮廓)
        int Template3[3][3]{ -1,-1,-1,-1,9,-1,-1,-1,-1 };//laplace锐化模板,8邻域
    public:
        bool readImage();//读取图片
        bool writeImage();//保存图片
        bool Operation(int x);//图像平滑和锐化处理
        bool Operation(int Template[][3], int coefficient);//图像处理
        bool Binarization();//二值化
        void showBmpHead(BITMAPFILEHEADER BmpHead);//显示文件头
        void showBmpInfo(tagBITMAPINFOHEADER BmpInfo);//显示信息头
    };
    bool eightBitMap::readImage() {
        cout << "输入要读取的图片名:";
        cin >> imageName;
        if (!fopen_s(&fpin, imageName, "rb")) {
            //读取图片类型
            WORD bfType;
            fread(&bfType, sizeof(WORD), 1, fpin);//fread()的使用
            if (bfType != 0x4d42) {
                cout << "该图片不是BMP!" << endl;
                return false;
            }
            //读取文件头和信息头
            fread(&bmpHead, sizeof(tagBITMAPFILEHEADER), 1, fpin);
            fread(&bmpInfo, sizeof(tagBITMAPINFOHEADER), 1, fpin);
            //检查是否是8位位图
            if (bmpInfo.biBitCount != 8) {
                cout << "该图片不是8位!" << endl;
                return false;
            }
            //读取调色板
            fread(pallet, sizeof(RGBQUAD), 256, fpin);
            //读取图片的像素信息
            width = bmpInfo.biWidth;
            height = bmpInfo.biHeight;
            width = (width * sizeof(BYTE) + 3) / 4 * 4;//图像的每一行必须是4的整数倍
            imagedata = new BYTE[width * height];
            fread(imagedata, sizeof(BYTE) * width, height, fpin);
            //显示文件头和信息头
            showBmpHead(bmpHead);
            showBmpInfo(bmpInfo);
            //关闭图片
            fclose(fpin);
        }
        else {
            cout << "图片不存在!" << endl;
            return false;
        }
        return true;
    }
    bool eightBitMap::writeImage() {
        char imageName[30];
        cout << "输入处理后的位图名:";
        cin >> imageName;
        //创建位图文件
        if (fopen_s(&fpout, imageName, "wb")) {
            cout << "创建文件失败!" << endl;
            return false;
        }
        //写入位图类型
        WORD bfBYTE = 0x4d42;
        fwrite(&bfBYTE, 1, sizeof(WORD), fpout);
        //写入文件头和信息头
        fwrite(&bmpHead, 1, sizeof(BITMAPFILEHEADER), fpout);
        fwrite(&bmpInfo, 1, sizeof(BITMAPINFOHEADER), fpout);
        //写入调色板
        fwrite(pallet, sizeof(RGBQUAD), 256, fpout);
        //写入图像像素数据
        fwrite(newimagedata, sizeof(BYTE) * width, height, fpout);
        //关闭图片
        fclose(fpout);
        //释放内存
        delete imagedata;
        delete newimagedata;
        delete pallet;
        return true;
    }
    bool eightBitMap::Operation(int x) {
        if (x == 1)
            return Operation(Template1, 9);
        else if (x == 2)
            return Operation(Template2, 1);
        else if (x == 3)
            return Operation(Template3, 1);
        else {
            cout << "模板调用错误!" << endl;
            return false;
        }
    }
    bool eightBitMap::Operation(int Template[][3], int coefficient) {
        //分配新像素素组的空间
        newimagedata = new BYTE[width * height];
        //进行模板操作
        for (int i = 0; i < height; ++i)
            for (int j = 0; j < width; ++j) {
                if (i == 0 || j == 0 || i == height - 1 || j == width - 1)
                    *(newimagedata + i * width + j) = *(imagedata + i * width + j);
                else {
                    int sum = 0;
                    for (int m = i - 1; m < i + 2; ++m)
                        for (int n = j - 1; n < j + 2; ++n) {
                            sum += (*(imagedata + m * width + n)) * Template[n - j + 1][m - i + 1] / coefficient;
                        }
                    //8位BMP中一个像素值,对应调色板中索引号为该像素值的项所存放的RGB色彩
                    //所以像素值范围为0~255
                    //像素值小于0就取0,大于255就取255
                    sum = (sum >= 0) ? sum : 0;
                    sum = ((sum + *(imagedata + i * width + j)) > 255) ? 255 : sum;
                    *(newimagedata + i * width + j) = sum;
                }
            }
        //保存图片
        if (writeImage()) {
            cout << "
    处理成功!" << endl;
            return true;
        }
        else {
            cout << "
    处理失败!" << endl;
            return false;
        }
    }
    bool eightBitMap::Binarization() {
        //分配新像素素组的空间
        newimagedata = new BYTE[width * height];
        //二值化操作
        for (int i = 0; i < width * height; ++i)
            if (*(imagedata + i) > 128)
                *(newimagedata + i) = 255;
            else
                *(newimagedata + i) = 0;
        //保存图片
        if (writeImage()) {
            cout << "
    处理成功!" << endl;
            return true;
        }
        else {
            cout << "
    处理失败!" << endl;
            return false;
        }
    }
    void eightBitMap::showBmpHead(BITMAPFILEHEADER BmpHead) {
        cout << "
    图片文件头:" << endl;
        cout << "  图片大小:" << BmpHead.bfSize << endl;
        cout << "  保留字_1:" << BmpHead.bfReserved1 << endl;
        cout << "  保留字_2:" << BmpHead.bfReserved2 << endl;
        cout << "  实际位图片数据的偏移字节数:" << BmpHead.bfOffBits << endl;
    }
    void eightBitMap::showBmpInfo(tagBITMAPINFOHEADER BmpInfo) {
        cout << "图片信息头:" << endl;
        cout << "  结构体的长度:" << BmpInfo.biSize << endl;
        cout << "  位图宽:" << BmpInfo.biWidth << endl;
        cout << "  位图高:" << BmpInfo.biHeight << endl;
        cout << "  平面数:" << BmpInfo.biPlanes << endl;
        cout << "  采用颜色位数:" << BmpInfo.biBitCount << endl;
        cout << "  压缩方式:" << BmpInfo.biCompression << endl;
        cout << "  实际位图数据占用的字节数:" << BmpInfo.biSizeImage << endl;
        cout << "  X方向分辨率:" << BmpInfo.biXPelsPerMeter << endl;
        cout << "  Y方向分辨率:" << BmpInfo.biYPelsPerMeter << endl;
        cout << "  使用的颜色数:" << BmpInfo.biClrUsed << endl;
        cout << "  重要颜色数:" << BmpInfo.biClrImportant << endl;
    }
    
    #endif

      在twentyfourbitmap.h里,写下述代码:

    #pragma once
    #ifndef TWENTYFOURBITMAP_H
    #define TWENTYDOURBITMAP_H
    
    #include <iostream>
    #include "BMP.h"
    using namespace std;
    
    class twentyfourBitMap {
        char imageName[30];//图像名
        int width, height;//图像的宽高
        BITMAPFILEHEADER bmpHead;//文件头
        BITMAPINFOHEADER bmpInfo;//信息头
        IMAGEDATA* imagedata = NULL, * newimagedata = NULL;//存储图片像素信息的二维数组
        FILE* fpin, * fpout;//文件指针
    
                           //平滑算子也是通过模板进行处理的,所以可以把平滑处理和锐化处理通过一个函数实现
        int Template1[3][3]{ 1,1,1,1,1,1,1,1,1 };//平滑模板
        int Template2[3][3]{ 0,-1,0,-1,5,-1,0,-1,0 };//laplace锐化模板,4邻域
        int Template3[3][3]{ -1,-1,-1,-1,9,-1,-1,-1,-1 };//laplace锐化模板,8邻域
    public:
        bool readImage();//读取图片
        bool writeImage();//保存图片
        bool Operation(int x);//图像平滑和锐化处理
        bool Operation(int Template[][3], int coefficient);//图像处理
        bool makeGray();//将彩色图转换为灰度图
        bool Binarization();//二值化
        void showBmpHead(BITMAPFILEHEADER BmpHead);//显示文件头
        void showBmpInfo(tagBITMAPINFOHEADER BmpInfo);//显示信息头
    };
    bool twentyfourBitMap::readImage() {
        cout << "输入要读取的图片名:";
        cin >> imageName;
        if (!fopen_s(&fpin, imageName, "rb")) {
            //读取图片类型
            WORD bfType;
            fread(&bfType, sizeof(WORD), 1, fpin);//fread()的使用
            if (bfType != 0x4d42) {
                cout << "该图片不是BMP!" << endl;
                return false;
            }
            //读取文件头和信息头
            fread(&bmpHead, sizeof(tagBITMAPFILEHEADER), 1, fpin);
            fread(&bmpInfo, sizeof(tagBITMAPINFOHEADER), 1, fpin);
            //检查是否是24位位图
            if (bmpInfo.biBitCount != 24) {
                cout << "该图片不是24位!" << endl;
                return false;
            }
            //读取图片的像素信息
            width = bmpInfo.biWidth;
            height = bmpInfo.biHeight;
            width = (width * sizeof(BYTE) + 3) / 4 * 4;//图像的每一行必须是4的整数倍
            imagedata = new IMAGEDATA[width * height];
            fread(imagedata, sizeof(IMAGEDATA) * width, height, fpin);
            //显示文件头和信息头
            showBmpHead(bmpHead);
            showBmpInfo(bmpInfo);
            //关闭图片
            fclose(fpin);
        }
        else {
            cout << "图片不存在!" << endl;
            return false;
        }
        return true;
    }
    bool  twentyfourBitMap::writeImage() {
        char imageName[30];
        cout << "输入处理后的位图名:";
        cin >> imageName;
        //创建位图文件
        if (fopen_s(&fpout, imageName, "wb")) {
            cout << "创建文件失败!" << endl;
            return false;
        }
        //写入位图类型
        WORD bfBYTE = 0x4d42;
        fwrite(&bfBYTE, 1, sizeof(WORD), fpout);
        //写入文件头和信息头
        fwrite(&bmpHead, 1, sizeof(BITMAPFILEHEADER), fpout);
        fwrite(&bmpInfo, 1, sizeof(BITMAPINFOHEADER), fpout);
        //写入图像像素数据
        fwrite(newimagedata, sizeof(IMAGEDATA) * width, height, fpout);
        //关闭图片
        fclose(fpout);
        //释放内存
        delete imagedata;
        delete newimagedata;
        return true;
    }
    bool  twentyfourBitMap::Operation(int x) {
        if (x == 1)
            return Operation(Template1, 9);
        else if (x == 2)
            return Operation(Template2, 1);
        else if (x == 3)
            return Operation(Template3, 1);
        else {
            cout << "模板调用错误!" << endl;
            return false;
        }
    }
    bool  twentyfourBitMap::Operation(int Template[][3], int coefficient) {
        //分配新像素素组的空间
        newimagedata = new IMAGEDATA[width * height];
        //进行模板操作
        for (int i = 0; i < height; ++i)
            for (int j = 0; j < width; ++j) {
                if (i == 0 || j == 0 || i == height - 1 || j == width - 1)
                    *(newimagedata + i * width + j) = *(imagedata + i * width + j);
                else {
                    int sum = 0;
                    for (int m = i - 1; m < i + 2; ++m)
                        for (int n = j - 1; n < j + 2; ++n) {
                            sum += ((*(imagedata + m * width + n)).blue) * Template[n - j + 1][m - i + 1] / coefficient;
                        }
                    //8位BMP中一个像素值,对应调色板中索引号为该像素值的项所存放的RGB色彩
                    //所以像素值范围为0~255
                    //像素值小于0就取0,大于255就取255
                    sum = (sum >= 0) ? sum : 0;
                    sum = (sum + ((*(imagedata + i * width + j)).blue) > 255) ? 255 : sum;
                    //把新RGB值存入新数组
                    (*(newimagedata + i * width + j)).blue = sum;
                    (*(newimagedata + i * width + j)).green = sum;
                    (*(newimagedata + i * width + j)).red = sum;
                }
            }
        //保存图片
        if (writeImage()) {
            cout << "
    处理成功!" << endl;
            return true;
        }
        else {
            cout << "
    处理失败!" << endl;
            return false;
        }
    }
    bool twentyfourBitMap::makeGray() {
        //分配新像素素组的空间
        newimagedata = new IMAGEDATA[width * height];
        //进行灰度化操作
        int sum;
        for (int i = 0; i < width * height; ++i) {
            sum = (*(imagedata + i)).blue +
                (*(imagedata + i)).green +
                (*(imagedata + i)).red;
            (*(newimagedata + i)).blue = (*(newimagedata + i)).green = (*(newimagedata + i)).red = sum / 3;
        }
        //保存图片
        if (writeImage()) {
            cout << "
    处理成功!" << endl;
            return true;
        }
        else {
            cout << "
    处理失败!" << endl;
            return false;
        }
    }
    bool twentyfourBitMap::Binarization() {
        //分配新像素素组的空间
        newimagedata = new IMAGEDATA[width * height];
        //二值化操作
        for (int i = 0; i < width * height; ++i)
            if ((*(imagedata + i)).blue > 128)
                (*(newimagedata + i)).blue =
                (*(newimagedata + i)).green =
                (*(newimagedata + i)).red = 255;
            else
                (*(newimagedata + i)).blue =
                (*(newimagedata + i)).green =
                (*(newimagedata + i)).red = 0;
        //保存图片
        if (writeImage()) {
            cout << "
    处理成功!" << endl;
            return true;
        }
        else {
            cout << "
    处理失败!" << endl;
            return false;
        }
    }
    void  twentyfourBitMap::showBmpHead(BITMAPFILEHEADER BmpHead) {
        cout << "
    图片文件头:" << endl;
        cout << "  图片大小:" << BmpHead.bfSize << endl;
        cout << "  保留字_1:" << BmpHead.bfReserved1 << endl;
        cout << "  保留字_2:" << BmpHead.bfReserved2 << endl;
        cout << "  实际位图片数据的偏移字节数:" << BmpHead.bfOffBits << endl;
    }
    void  twentyfourBitMap::showBmpInfo(tagBITMAPINFOHEADER BmpInfo) {
        cout << "图片信息头:" << endl;
        cout << "  结构体的长度:" << BmpInfo.biSize << endl;
        cout << "  位图宽:" << BmpInfo.biWidth << endl;
        cout << "  位图高:" << BmpInfo.biHeight << endl;
        cout << "  平面数:" << BmpInfo.biPlanes << endl;
        cout << "  采用颜色位数:" << BmpInfo.biBitCount << endl;
        cout << "  压缩方式:" << BmpInfo.biCompression << endl;
        cout << "  实际位图数据占用的字节数:" << BmpInfo.biSizeImage << endl;
        cout << "  X方向分辨率:" << BmpInfo.biXPelsPerMeter << endl;
        cout << "  Y方向分辨率:" << BmpInfo.biYPelsPerMeter << endl;
        cout << "  使用的颜色数:" << BmpInfo.biClrUsed << endl;
        cout << "  重要颜色数:" << BmpInfo.biClrImportant << endl;
    }
    
    #endif

      在main.cpp里,写下述代码:

    #include <iostream>
    #include "BMP.h"
    #include "eghtbitmap.h"
    #include "twentyfourbitmap.h"
    using namespace std;
    int main() {
        int depth = 0;
        cout << "输入要处理的位图深度:";
        cin >> depth;
        if (depth == 8) {
            eightBitMap Image;
            if (Image.readImage()) {
                int answer;
                cout << "
    1.平滑处理"
                    << "
    2.4邻域锐化"
                    << "
    3.8邻域锐化"
                    << "
    4.二值化"
                    << "
    选择处理方式:";
                cin >> answer;
                switch (answer) {
                case 1:
                    Image.Operation(1);
                    break;
                case 2:
                    Image.Operation(2);
                    break;
                case 3:
                    Image.Operation(3);
                    break;
                case 4:
                    Image.Binarization();
                    break;
                default:
                    cout << "错误选择!" << endl;
                }
            }
            else cout << "位图读取错误!" << endl;
        }
        else if (depth == 24) {
            twentyfourBitMap Image;
            if (Image.readImage()) {
                int answer;
                cout << "
    1.平滑处理"
                    << "
    2.4邻域锐化"
                    << "
    3.8邻域锐化"
                    << "
    4.彩色位图灰度化"
                    << "
    5.二值化"
                    << "
    选择处理方式:";
                cin >> answer;
                switch (answer) {
                case 1:
                    Image.Operation(1);
                    break;
                case 2:
                    Image.Operation(2);
                    break;
                case 3:
                    Image.Operation(3);
                    break;
                case 4:
                    Image.makeGray();
                    break;
                case 5:
                    Image.Binarization();
                    break;
                default:
                    cout << "错误选择!" << endl;
                }
            }
            else cout << "位图读取错误!" << endl;
        }
        else cout << "错误深度输入!" << endl;
        cin.get();
        cin.get();
    }

      写好代码后,编译一下。

    运行

      在运行前,需要先知道要操作的图像的位深度。

      

      然后运行代码。(注生成的新的位图,需要把.bmp后缀名加上)。

      示例:

      

       最后,可以代码的文件夹中找到time1.pmg。

              

      左原图,右8邻域处理后的图像。

  • 相关阅读:
    layer 弹出层 回调函数调用 弹出层页面 函数
    jquery 封装页面之间获取值
    ZTree 获取选中的项
    动态拼接SQL 语句
    翻译-使用Spring调用SOAP Web Service
    分享最新的博客到LinkedIn Timeline
    翻译-使用Spring WebService生成SOAP Web Service
    在Gradle中使用jaxb的xjc插件
    Gradle中的buildScript代码块
    健身4个月总结
  • 原文地址:https://www.cnblogs.com/jianle23/p/13753477.html
Copyright © 2020-2023  润新知