win32 - GDI+ 高斯模糊的使用
虽然标题中标有GDI+,但其实真正实施的时候并没有用到。
不过GDI+的相关文档有一些关于高斯模糊的api说明,见下面链接:
使用Blur类,你可以将高斯模糊效果应用于位图并指定模糊的性质。将Blur对象的地址传递给Graphics :: DrawImage方法或Bitmap :: ApplyEffect方法。若要指定模糊的性质,请将BlurParams结构传递给Blur对象的Blur :: SetParameters方法。
一般来说,上面的类封装了模糊的算法,所以我下面贴的代码是直接用算法来模糊位图的。
C++ code:
#define _USE_MATH_DEFINES
#include <cmath>
#include <iostream>
#include <fstream> #define NOMINMAX
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#endif #ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif #ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif typedef struct
{
uint8_t r, g, b, a;
} rgb32; #if !defined(_WIN32) && !defined(_WIN64)
#pragma pack(2)
typedef struct
{
uint16_t bfType;
uint32_t bfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
} BITMAPFILEHEADER;
#pragma pack() #pragma pack(2)
typedef struct
{
uint32_t biSize;
int32_t biWidth;
int32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
int16_t biXPelsPerMeter;
int16_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
} BITMAPINFOHEADER;
#pragma pack()
#endif #pragma pack(2)
typedef struct
{
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
} BMPINFO;
#pragma pack() class bitmap
{
private:
BMPINFO bmpInfo;
uint8_t* pixels; public:
bitmap(const char* path);
~bitmap(); void save(const char* path, uint16_t bit_count = 24); rgb32* getPixel(uint32_t x, uint32_t y) const;
void setPixel(rgb32* pixel, uint32_t x, uint32_t y); uint32_t getWidth() const;
uint32_t getHeight() const;
uint16_t bitCount() const;
}; bitmap::bitmap(const char* path) : bmpInfo(), pixels(nullptr)
{
std::ifstream file(path, std::ios::in | std::ios::binary); if (file)
{
file.read(reinterpret_cast<char*>(&bmpInfo.bfh), sizeof(bmpInfo.bfh)); if (bmpInfo.bfh.bfType != 0x4d42)
{
throw std::runtime_error("Invalid format. Only bitmaps are supported.");
} file.read(reinterpret_cast<char*>(&bmpInfo.bih), sizeof(bmpInfo.bih)); if (bmpInfo.bih.biCompression != 0)
{
std::cerr << bmpInfo.bih.biCompression << "\n";
throw std::runtime_error("Invalid bitmap. Only uncompressed bitmaps are supported.");
} if (bmpInfo.bih.biBitCount != 24 && bmpInfo.bih.biBitCount != 32)
{
throw std::runtime_error("Invalid bitmap. Only 24bit and 32bit bitmaps are supported.");
} file.seekg(bmpInfo.bfh.bfOffBits, std::ios::beg); pixels = new uint8_t[bmpInfo.bfh.bfSize - bmpInfo.bfh.bfOffBits];
file.read(reinterpret_cast<char*>(&pixels[0]), bmpInfo.bfh.bfSize - bmpInfo.bfh.bfOffBits); uint8_t* temp = new uint8_t[bmpInfo.bih.biWidth * bmpInfo.bih.biHeight * sizeof(rgb32)]; uint8_t* in = pixels;
rgb32* out = reinterpret_cast<rgb32*>(temp);
int padding = bmpInfo.bih.biBitCount == 24 ? ((bmpInfo.bih.biSizeImage - bmpInfo.bih.biWidth * bmpInfo.bih.biHeight * 3) / bmpInfo.bih.biHeight) : 0; for (int i = 0; i < bmpInfo.bih.biHeight; ++i, in += padding)
{
for (int j = 0; j < bmpInfo.bih.biWidth; ++j)
{ out->b = *(in++);
out->g = *(in++);
out->r = *(in++);
out->a = bmpInfo.bih.biBitCount == 32 ? *(in++) : 0xFF;
++out;
}
} delete[] pixels;
pixels = temp;
}
} bitmap::~bitmap()
{
delete[] pixels;
} void bitmap::save(const char* path, uint16_t bit_count)
{
std::ofstream file(path, std::ios::out | std::ios::binary); if (file)
{
bmpInfo.bih.biBitCount = bit_count;
uint32_t size = ((bmpInfo.bih.biWidth * bmpInfo.bih.biBitCount + 31) / 32) * 4 * bmpInfo.bih.biHeight;
bmpInfo.bfh.bfSize = bmpInfo.bfh.bfOffBits + size; file.write(reinterpret_cast<char*>(&bmpInfo.bfh), sizeof(bmpInfo.bfh));
file.write(reinterpret_cast<char*>(&bmpInfo.bih), sizeof(bmpInfo.bih));
file.seekp(bmpInfo.bfh.bfOffBits, std::ios::beg); uint8_t* out = NULL;
rgb32* in = reinterpret_cast<rgb32*>(pixels);
uint8_t* temp = out = new uint8_t[bmpInfo.bih.biWidth * bmpInfo.bih.biHeight * sizeof(rgb32)];
int padding = bmpInfo.bih.biBitCount == 24 ? ((bmpInfo.bih.biSizeImage - bmpInfo.bih.biWidth * bmpInfo.bih.biHeight * 3) / bmpInfo.bih.biHeight) : 0; for (int i = 0; i < bmpInfo.bih.biHeight; ++i, out += padding)
{
for (int j = 0; j < bmpInfo.bih.biWidth; ++j)
{
*(out++) = in->b;
*(out++) = in->g;
*(out++) = in->r; if (bmpInfo.bih.biBitCount == 32)
{
*(out++) = in->a;
}
++in;
}
} file.write(reinterpret_cast<char*>(&temp[0]), size); //bmpInfo.bfh.bfSize - bmpInfo.bfh.bfOffBits
delete[] temp;
}
} rgb32* bitmap::getPixel(uint32_t x, uint32_t y) const
{
rgb32* temp = reinterpret_cast<rgb32*>(pixels);
return &temp[(bmpInfo.bih.biHeight - 1 - y) * bmpInfo.bih.biWidth + x];
} void bitmap::setPixel(rgb32* pixel, uint32_t x, uint32_t y)
{
rgb32* temp = reinterpret_cast<rgb32*>(pixels);
memcpy(&temp[(bmpInfo.bih.biHeight - 1 - y) * bmpInfo.bih.biWidth + x], pixel, sizeof(rgb32));
}; uint32_t bitmap::getWidth() const
{
return bmpInfo.bih.biWidth;
} uint32_t bitmap::getHeight() const
{
return bmpInfo.bih.biHeight;
} uint16_t bitmap::bitCount() const
{
return bmpInfo.bih.biBitCount;
} void apply_blur(int x, int y, bitmap* bmp, int blurRadius)
{
double blurValue = 0.111;
int r = 0;
int g = 0;
int b = 0; for (int k = y - blurRadius; k <= blurRadius; ++k)
{
for (int l = x - blurRadius; l <= blurRadius; ++l)
{
rgb32* pixel = bmp->getPixel(l, k);
r += blurValue * pixel->r;
g += blurValue * pixel->g;
b += blurValue * pixel->b;
}
} rgb32 pixel = *bmp->getPixel(x, y); pixel.r = r;
pixel.g = g;
pixel.b = b; bmp->setPixel(&pixel, x, y);
}
void blur(bitmap* bmp, int radius); int main(int argc, const char* argv[])
{
bitmap bmp{ "C:\\Users\\strives\\Desktop\\new_2.bmp" };
blur(&bmp, 5);
bmp.save("C:\\Users\\strives\\Desktop\\blurred-panda.bmp");
return 0;
} void blur(bitmap* bmp, int radius)
{
float rs = ceil(radius * 2.57);
for (int i = 0; i < bmp->getHeight(); ++i)
{
for (int j = 0; j < bmp->getWidth(); ++j)
{
double r = 0, g = 0, b = 0;
double count = 0; for (int iy = i - rs; iy < i + rs + 1; ++iy)
{
for (int ix = j - rs; ix < j + rs + 1; ++ix)
{
auto x = min(static_cast<int>(bmp->getWidth()) - 1, max(0, ix));
auto y = min(static_cast<int>(bmp->getHeight()) - 1, max(0, iy)); auto dsq = ((ix - j) * (ix - j)) + ((iy - i) * (iy - i));
auto wght = std::exp(-dsq / (2.0 * radius * radius)) / (M_PI * 2.0 * radius * radius); rgb32* pixel = bmp->getPixel(x, y); r += pixel->r * wght;
g += pixel->g * wght;
b += pixel->b * wght;
count += wght;
}
} rgb32* pixel = bmp->getPixel(j, i);
pixel->r = std::round(r / count);
pixel->g = std::round(g / count);
pixel->b = std::round(b / count);
}
}
}
当然,这些代码是搬得so论坛的,放在此处也是用于以后工作上的参考,毕竟墙的厉害。
- C++ Blur effect on bit map is working but colors are changed
- Algorithm for fast Drop shadow in GDI+
- 最快的高斯模糊(线性时间)
如果想制作位图的蒙版,参考: MaskBlt
也可以使用原始的BitBlt进行颜色的位与操作,见下面案例:
一些其他文档可以学习:
win32 - GDI+ 高斯模糊的使用的更多相关文章
- Windows Graphics Programming Win32 GDI and DirectDraw第六章疑问
<Windows Graphics Programming Win32 GDI and DirectDraw>6.1节中有这样的描述: The Windows NT/2000 graphi ...
- Win32 GDI 非矩形区域剪裁,双缓冲技术
传统的Win32通过GDI提供图形显示的功能,包括了基本的绘图功能,如画线.方块.椭圆等等,高级功能包括了多边形和Bezier的绘制.这样app就不用关心那些图形学的细节了,有点类似于UNIX上的X- ...
- Win32 GDI基础(笔记)
1.GDI名字的意义 GDI Graphic Device Interface,我说不清和GUI有什么区别.可能一种针对设备,一种针对用户而言吧,反正以后都说GDI,也就是Windows的图形编程. ...
- C# [Win32] [GDI+] [API] Load HFONT from Memory
// gdiplusenums.h //-------------------------------------------------------------------------- // Fo ...
- Delphi的Win32的API调用简单介绍
1. 介绍Win32 API和Win32系统.还要讨论Win32系统的功能以及它与16位系统在功能上的几个主要区别.只是让对Win32系统有一个基本的了解.当已经基本了解Win32操作后,就可 ...
- GDI+编程说明及小结
原文地址:http://blog.csdn.net/byxdaz/article/details/5972759 GDI+(Graphics Device Interface Plus图形设备接口加) ...
- GDI编程
图形设备接口(GDI)是一个可执行程序,它接受Windows应用程序的绘图请求(表现为GDI函数调用),并将它们传给相应的设备驱动程序,完成特定于硬件的输出,象打印机输出和屏幕输出.GDI负责Wind ...
- GDI编程小结
图形设备接口(GDI)是一个可运行程序,它接受Windows应用程序的画图请求(表现为GDI函数调用),并将它们传给对应的设备驱动程序,完毕特定于硬件的输出,象打印机输出和屏幕输出.GDI负责Wind ...
- VC++学习之GDI概述
VC++学习之GDI概述 图形设备接口(GDI)是一个可执行程序,它接受Windows应用程序的绘图请求(表现为GDI函数调用),并将它们传给相应的设备驱动程序,完成特定于硬件的输出,象打印机输出和屏 ...
- Delphi中使用GDI+进行绘图(1)
Delphi的VCL类库中,默认使用的是GDI绘图接口,该接口封装了Win32 GDI接口,能够满足基本的绘图功能,但如果要实现更高级的绘图功能,往往比较困难,GDI+是微软在GDI之后的一个图形接口 ...
随机推荐
- [转帖]linux audit审计(7-1)--读懂audit日志
https://www.cnblogs.com/xingmuxin/p/8807774.html auid=0 auid记录Audit user ID,that is the loginuid.当我 ...
- [转帖]Kafka 性能优化与问题深究
Kafka 性能优化与问题深究 一.Kafka深入探究 1.1 kafka整体介绍 1. 1.1 Kafka 如何做到高吞吐.低延迟的呢? Kafka是一个分布式高吞吐量的消息系统,这里提下 Kafk ...
- Python学习之十一_Windows获取硬件信息
Python学习之十一_Windows获取硬件信息 简介 网上找了一些方法简单整理了下,可以快速获取部分信息 包含机器名称等. 以及序列号相关 部分学习来源: https://blog.51cto.c ...
- Nginx编译安装与常用配置模板
Nginx编译安装与常用配置模板 背景 是在受不了每次都是先去百度,找模板了. 这次将几个常用模板整理一下, 以后不管在哪里可以直接使用. 注意: 不能直接用于生产, 可用于测试与POC 第一部分编译 ...
- PG数据库异步流复制
PG数据库异步流复制 背景说明 最近想进行一个数据库高可用课题的研究. 因为之前某种原因,这次选择的是PG数据库. 为了简单起见, 暂时采用PG异步流复制的场景. 这次仅是为了测试, 不考虑高可用绿色 ...
- [转载]关于NSA的EternalBlue(永恒之蓝) ms17-010漏洞利用
2017年5月19日 感谢原作者:http://www.cnblogs.com/cnbluerain/ 好久没有用这个日志了,最近WannaCry横行,媒体铺天盖地的报道,我这 ...
- 【K哥爬虫普法】辛苦钱被中间商抽走八成,还因此锒铛入狱
我国目前并未出台专门针对网络爬虫技术的法律规范,但在司法实践中,相关判决已屡见不鲜,K 哥特设了"K哥爬虫普法"专栏,本栏目通过对真实案例的分析,旨在提高广大爬虫工程师的法律意识, ...
- 人工智能的新篇章:深入了解大型语言模型(LLM)的应用与前景
人工智能的新篇章:深入了解大型语言模型(LLM)的应用与前景 LLM(Large Language Model)技术是一种基于深度学习的自然语言处理技术,旨在训练能够处理和生成自然语言文本的大型模型. ...
- 总结一个问题:csdn发布文章页面为空或者创作内容管理为空
总结一个问题:csdn发布文章页面或者创作内容管理为空 解决方案: 打开chrome浏览器的设置: 点击清除数据: 选择高级里清除数据,一般24小时就可以了,不行就7天
- python使用selenium控制已打开的Chrome浏览器
环境 Python3.11 selenium 4.9.0 Chrome 112.0.5615.138 步骤 为了便于和平常用的Chrome浏览区分,可以先创建一个专门用于开发的Chrome浏览器, 添 ...