# DIPxuzz **Repository Path**: sun-zhenxing/dipxuzz ## Basic Information - **Project Name**: DIPxuzz - **Description**: 图像处理课程使用的 MFC 框架 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2022-09-21 - **Last Updated**: 2025-06-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 数字图像处理(2022 课程) ## 本项目的意义 - [x] 避免学校课程的期末复习耽误大量时间 - [x] 避免将核心的精力放到学校课程上 - [x] 演示高校的课程如何浪费学生的时间,进行没有营养的教学 - [x] 演示没有软件工程化的代码如何地难以维护 - [x] 演示 MFC 的应用程序如何难以阅读、不跨平台、不开放且特征古老 - [x] 简单入门数字图像处理 尽量每次提交都是一次新的作业,并包含一些作业的做法,以便考试能快速参考。 下面是课程的内容,可以说你只要会复制粘贴代码这门课就过了(CV 工程师课程)。 ## 函数对照表 | 序号 | 功能 | 函数 | 对应菜单 | | :---: | ------------------------------------------- | ---------------------- | ------------------------------------ | | 1 | [绘制形状](#1-绘制形状) | `OnSecondmenu()` | `FirstMenu => SecondMenu` | | 2 | [过滤颜色为灰度](#2-过滤颜色为灰度) | `OnColorToGray()` | `文件 => 彩色->灰度图` | | 3 | [灰度变换(图像增强)](#3-灰度变换图像增强) | `OnGraytrans()` | `点运算 => 灰度变换` | | 4 | [读水印(灰度)](#4-水印操作) | `OnReadMark()` | `应用2 => Mark` | | 5 | [打水印(灰度)](#4-水印操作) | `OnWatermarkCombine()` | `应用2 => Combine` | | 6 | [读水印(彩色)](#4-水印操作) | `OnWatermark1()` | `应用2 => MarkColor` | | 7 | [打水印(彩色)](#4-水印操作) | `OnWatermark2()` | `应用2 => CombineColor` | | 8 | [信息隐藏](#8-信息隐藏) | `OnHide()` | `应用 => 信息隐藏` | | 9 | [将隐藏的信息解析](#8-信息隐藏) | `OnFind()` | `应用 => 信息解析` | | 10 | [添加噪声(高斯、椒盐)](#10-添加噪声) | `OnAddnoise()` | `点运算 => Addnoise` | | 11 | [去除噪声(均值、中值)](#11-去除噪声) | `OnFilter()` | `点运算 => 滤波` | | 12 | [直方图均衡](#12-直方图均衡) | `OnPointEqua()` | `点运算 => 直方图均衡` | | 13 | [阈值分割(制作剪影)](#13-阈值分割) | `OnDetectThreshold()` | `图像分析 => 阈值分割` | | 14 | [边缘检测](#14-边缘检测) | `OnEdgeSobel()` | `图像分析 => 边缘检测 => Sobel 算子` | | 15 | [车道线检测](#14-边缘检测) | `OnLanedetection()` | `应用 => 车道线检测` | | 16 | [水果检测](#16-水果检测) | `OnFruitlocation()` | `应用 => 水果检测` | | 17 | [人脸马赛克](#17-人脸马赛克) | `OnMosaic()` | `应用 => Mosaic` | | 18 | [测量身高和臂长](#18-测量身高的臂长) | `OnMeasureHL()` | `应用 => 测量` | | 19 | [图像平移](#19-图像变换) | `OnGeomTran()` | `几何变换 => 图像平移` | | 20 | [图像缩放](#19-图像变换) | `OnGeomZoom()` | `几何变换 => 图像缩放` | | 21 | [图像旋转](#19-图像变换) | `OnGeomRota()` | `几何变换 => 图像旋转` | | 22 | [瘦脸瘦身](#22-瘦脸瘦身) | `OnThinbody()` | `应用 => 瘦身` | ## 1. 绘制形状 第一节课主要是入门课程,下面演示如何在图片上绘制形状、文字等信息: ```c++ void CDipXuzzView::OnSecondmenu() { CClientDC dc(this); for (int i = 0; i < 100; i++) dc.SetPixel(i, i, RGB(255, 0, 0)); dc.MoveTo(100, 100); dc.LineTo(100, 200); dc.Ellipse(100, 160, 200, 200); dc.TextOutA(150, 150, "demo demo demo demo demo....."); } ``` ## 2. 过滤颜色为灰度 修改颜色数值可以得出不同的颜色,改了之后记得改回来: ```c++ gray = (unsigned char)((*ired) * 0.299 + (*igreen) * 0.588 + (*iblue) * 0.114); ``` 可能的修改方式: ```c++ gray = (*ired) * 0.90 + (*igreen) * 0.1 - (*iblue) * 0.5; if (gray > 255) gray = 255; if (gray < 0) gray = 0; ``` ## 3. 灰度变换(图像增强) 修改增强方法: $$ y = \frac{x^2}{5} + 20 $$ ```c++ temp = (*lpSrc) * (*lpSrc) / 5.0 + 20; if (temp > 255) temp = 255; if (temp < 0) temp = 0; ``` 可能修改的方式: ```c++ // y = a * x^2 temp = 255 - (*lpSrc - 255) * (*lpSrc - 255) / 255.0; if (temp > 235) temp = 255; if (temp < 40) temp = 40; ``` ```c++ temp = max(0, min(255, 2.7 * lpSrc + 55)); ``` ```c++ if (temp > 128 && temp <= 255) { if (*lpSrc < 192) temp = (*lpSrc) / 3.0; else temp = (*lpSrc - 192) * 3 + 64; temp = 255 - sqrt((float)255 * 255 - temp * 255); if (temp > 255) *lpSrc = 255; else if (temp < 0) *lpSrc = 0; else *lpSrc = temp; } else if (temp >= 0 && temp < 128) { temp = (*lpSrc - 100) * 5; if (temp > 255) *lpSrc = 255; else if (temp < 0) *lpSrc = 0; else *lpSrc = temp; } else { if (temp > 255) *lpSrc = 255; else if (temp < 0) *lpSrc = 0; else *lpSrc = temp; } ``` ```c++ if (temp > 255) { temp = 255; } else if (temp > 150) { temp = temp * 2.5 + 100; } else if (temp > 100) { temp = (temp + 100) * 3.5; } else if (temp > 50) { temp = temp * 6 * log(i + j); } else if (temp < 0) { temp = 0; } else { temp = temp * 10; } ``` ## 4. 水印操作 水印图都做成正方形。 修改打码位置: ```c++ int xcoor = 190; int ycoor = 110; ``` 修改打码透明度: ```c++ if (*lpMark < 128) { *lpBase = (*lpBase) * 0.2 + (*lpMark) * 0.8; } ``` 灰度水印: 1. 打开水印图 2. `文件 => 彩色->灰度图` 3. `应用2 => Mark` 4. 打开原图 5. `文件 => 彩色->灰度图` 6. `应用2 => Combine` 彩色水印: 1. 打开水印图 2. `应用2 => MarkColor` 3. 打开原图 4. `应用2 => CombineColor` ## 8. 信息隐藏 可以修改隐藏的位置: ```c++ int coorx = 220; int coory = 220; ``` 步骤: 1. 打开要隐藏的图片 2. `文件 => 彩色->灰度图` 3. `应用2 => Mark` 4. 打开原图 5. `文件 => 彩色->灰度图` 6. `应用 => 信息隐藏` 解析这张图片: 1. `应用 => 信息解析` ## 10. 添加噪声 修改 `flag`: ```c++ int flag = 0; ``` 为 `0` 是高斯噪声,为 `1` 时是椒盐噪声。 ## 11. 去除噪声 修改 `flag`: ```c++ int flag = 0; ``` 为 `0` 时是均值滤波,用于去除高斯噪声,为 `1` 时是中值滤波,用于去除椒盐噪声。 ## 12. 直方图均衡 如果不需要画出目标图像,注释: ```c++ for (i = 0; i < lWidth; i++) { for (j = 0; j < lHeight; j++) { lpSrc = (unsigned char*)lpDIBBits + lLineBytes * (lHeight - 1 - j) + i; *lpSrc = Map[*lpSrc]; dc.SetPixel(lWidth + 10 + i, j, RGB(*lpSrc, *lpSrc, *lpSrc)); } } ``` 如果不需要绘制坐标,将 `if (1)` 改为 `if (0)` 。 如果要修改坐标的位置,修改: ```c++ ox = 1450; oy = 800; ``` ## 13. 阈值分割 修改阈值: ```c++ int Th = 175; ``` 阈值越大图片越白。 ## 14. 边缘检测 只会用到 Sobel 算子,只需要应用即可。 ## 16. 水果检测 水果检测可以用于检测其他对象,下面是检测上下两个对象的框架,可以修改参数以适配问题: ```c++ void CDipXuzzView::OnFruitlocation() { CClientDC dc(this); //画笔 CPen pen(PS_SOLID, 3, RGB(0, 0, 255)); // CPen* pOldPen = dc.SelectObject(&pen); CDipXuzzDoc* pDoc = GetDocument(); LPSTR lpDIB; LPSTR lpDIBBits; LONG lLineBytes; LONG lWidth, lHeight; lpDIB = (LPSTR)::GlobalLock((HGLOBAL)pDoc->GetHDIB()); lWidth = ::DIBWidth(lpDIB); // DIB 宽度 lHeight = ::DIBHeight(lpDIB); // DIB 高度 lLineBytes = WIDTHBYTES(lWidth * 8 * 3); //真彩色一个象素为3个字节 lpDIBBits = (LPSTR)lpDIB + sizeof(BITMAPINFOHEADER); unsigned char *ired, *igreen, *iblue; int i, j; int x = 0, y = 0; //中心坐标 int count = 0; //点的总数 for (i = 0; i < lWidth; i++) { for (j = 0; j < lHeight / 2; j++) { ired = (unsigned char*)lpDIBBits + lLineBytes * (lHeight - 1 - j) + i * 3 + 2; igreen = (unsigned char*)lpDIBBits + lLineBytes * (lHeight - 1 - j) + i * 3 + 1; iblue = (unsigned char*)lpDIBBits + lLineBytes * (lHeight - 1 - j) + i * 3 + 0; /* if (*ired > 194 && *igreen < 150 && *iblue < 150) { *ired = *iblue = *igreen = 0; }*/ if (*ired > 170 && *igreen > 170 && *iblue > 170) { //*ired = *iblue = *igreen = 0;//把想找到的图片变成黑色 count++; //获取所要图像的总点数,就是面积 x += i; //获取图像的长 y += j; //获取图像的宽 } } } x = x / count; //获取中心坐标 y = y / count; int length = sqrt((float)count) * 1.3; //得到变成,系数是为了把框变大完全框住 int left, right, top, botton; //获取所要区块的上下左右坐标 left = x - length / 2; right = x + length / 2; top = y - length / 2; botton = y + length / 2; //绘制方框 dc.MoveTo(left, top); //移动到左上角 dc.LineTo(left, botton); //移动到左下角,画线 //边缘检测 dc.LineTo(right, botton); //移动到右上角,画线 dc.LineTo(right, top); //移动到右下角,画线 dc.LineTo(left, top); //移动到左上角,画线 x = 0; y = 0; count = 0; for (i = 0; i < lWidth; i++) { for (j = lHeight / 2; j < lHeight; j++) { ired = (unsigned char*)lpDIBBits + lLineBytes * (lHeight - 1 - j) + i * 3 + 2; igreen = (unsigned char*)lpDIBBits + lLineBytes * (lHeight - 1 - j) + i * 3 + 1; iblue = (unsigned char*)lpDIBBits + lLineBytes * (lHeight - 1 - j) + i * 3 + 0; /* if (*ired > 194 && *igreen < 150 && *iblue < 150) { *ired = *iblue = *igreen = 0; }*/ if (*ired > 170 && *igreen > 170 && *iblue > 170) { //*ired = *iblue = *igreen = 0;//把想找到的图片变成黑色 count++; //获取所要图像的总点数,就是面积 x += i; //获取图像的长 y += j; //获取图像的宽 } } } x = x / count; //获取中心坐标 y = y / count; length = sqrt((float)count) * 1.3; //得到变成,系数是为了把框变大完全框住 left, right, top, botton; //获取所要区块的上下左右坐标 left = x - length / 2; right = x + length / 2; top = y - length / 2; botton = y + length / 2; //绘制方框 dc.MoveTo(left, top); //移动到左上角 dc.LineTo(left, botton); //移动到左下角,画线 dc.LineTo(right, botton); //移动到右上角,画线 dc.LineTo(right, top); //移动到右下角,画线 dc.LineTo(left, top); //移动到左上角,画线 /* //画圆 int r = sqrt(count / 3.14);//求圆的半径 for (float a = 0; a < 2 * 3.14; a+=0.01) { //dc.LineTo(x+r * cos(a), y+r * sin(a)); dc.SetPixel(x + r * cos(a), y + r * sin(a),RGB(255,255,0)); dc.SetPixel(x + (r - 1) * cos(a), y + (r - 1) * sin(a), RGB(255, 255, 0)); } ::GlobalUnlock((HGLOBAL)pDoc->GetHDIB()); pDoc->SetModifiedFlag(TRUE); //pDoc->UpdateAllViews(NULL); */ } ``` 取消画圆这一段的注释,然后注释上面划线的语句,可以对目标画圆。 ## 17. 人脸马赛克 修改参数可以适配其他对象。 下面的代码可以对目标区域划线: ```c++ cx = cx / count; cy = cy / count; int length = sqrt((float)count) * 0.5; l = cx - length; r = cx + length; b = cy + length; t = cy - length; dc.MoveTo(l, t); dc.LineTo(r, t); dc.LineTo(r, b); dc.LineTo(l, b); dc.LineTo(l, t); ``` ## 18. 测量身高的臂长 步骤: 1. 打开要隐藏的图片 2. `文件 => 彩色->灰度图` 3. `图像分析 => 阈值分割` 4. `应用 => 测量` 如果不容易调出,或者需要修改比例,修改: ```c++ float calibation = 90.0 / 33.3; ``` ## 19. 图像变换 图像平移距离: ```c++ if (i - 70 >= 0 && j - 50 >= 0) { *lpSrc = temp[(j - 50) * lWidth + (i - 70)]; } ``` 图像缩放比例: ```c++ float fXZoomRatio = 2.0 / 1; float fYZoomRatio = 2.0 / 1; ``` 图像旋转角度: ```c++ float fRotateAngle = -3.54 * PI / 180; ``` ## 22. 瘦脸瘦身 直接应用。