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

有话在先

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

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

摆好龙门阵

  visual studio 2019 + mfc + photo(bmp)。可以参考微软的官方文档https://docs.microsoft.com/en-us/windows/win32/api/_gdi/

  如何把 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 << "\n处理成功!" << endl;
        return true;
    }
    else {
        cout << "\n处理失败!" << 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 << "\n处理成功!" << endl;
        return true;
    }
    else {
        cout << "\n处理失败!" << endl;
        return false;
    }
}
void eightBitMap::showBmpHead(BITMAPFILEHEADER BmpHead) {
    cout << "\n图片文件头:" << 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 << "\n处理成功!" << endl;
        return true;
    }
    else {
        cout << "\n处理失败!" << 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 << "\n处理成功!" << endl;
        return true;
    }
    else {
        cout << "\n处理失败!" << 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 << "\n处理成功!" << endl;
        return true;
    }
    else {
        cout << "\n处理失败!" << endl;
        return false;
    }
}
void  twentyfourBitMap::showBmpHead(BITMAPFILEHEADER BmpHead) {
    cout << "\n图片文件头:" << 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 << "\n1.平滑处理"
                << "\n2.4邻域锐化"
                << "\n3.8邻域锐化"
                << "\n4.二值化"
                << "\n选择处理方式:";
            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 << "\n1.平滑处理"
                << "\n2.4邻域锐化"
                << "\n3.8邻域锐化"
                << "\n4.彩色位图灰度化"
                << "\n5.二值化"
                << "\n选择处理方式:";
            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邻域处理后的图像。

 

posted @ 2020-10-12 19:35  望星草  阅读(673)  评论(0编辑  收藏  举报