图像处理————将图片处理为像素风格和黑白风格

像素

每一张图片的内容都是由一个小方格组成,每一个小方格都有自己的位置信息和颜色信息,这样的小方格就称为像素点,而像素点是图片的最小单位,这样的一个像素矩阵组成的图形就是我们所看到的图片。

分辨率也是用像素来作为单位的,通常是以一对数字出现,例如1024×768,但是也有单独出现的情况,例如相机。

像素也决定图像清晰度。像素分得越小,画面的总像素就越多,图像也就越清晰。

修改图片的像素值

由于图片的像素点是携带有信息的,其中就包括位置信息和颜色信息,而颜色信息一般是用rgb格式(rgb格式:称为光学三原色, 肉眼看到的颜色都可以用三原色混合叠加而成,r为红色,g为绿色,b为蓝色,
在计算机里用0到255的值来表示每种颜色的值),当然也有其他模式如十六进制格式。

如果我们通过文件流形式读取到图片的每个像素的颜色值,那么我们就可以对其进行修改,实现各种各样的效果。例如:通过一种算式将每个像素转为只有黑和白两种颜色,这个就是ps里的黑白效果的实现,ps里面
很多的效果我们都可以自己编码实现,但是我们首先得知道其中的转化的算式是怎么来的,原理如何。由于图片里的像素信息是按矩阵的形式来放的,所以我们理所应当的可以用二维数组来获取像素的位置信息,知
道大小位置信息后,我们就可以自己实现图片裁剪功能。

获取像素值方式

js: js的方式可以用第三方框架,别人写好的js,或者是将图像绘制在canvas里,然后通过canvas的getImageData()方法获取,获取到的是rgba模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
// 通过js获取图像数据
var canvas_obj = document.getElementById("myCanvas"); // 获取canvas标签对象
var ctx = canvas_obj.getContext("2d"); // 设置在画布上绘图的环境
var img_obj = document.getElementById("img"); // 获取img标签对象
ctx.drawImage(img_obj, 0, 0); // 将图片绘制到画布上
var imgData_obj = ctx.getImageData(0,0,canvas_obj.width,canvas_obj.height); // 获取画布上的图像像素矩阵
var imgData = imgData_obj.data; // 获取到的数据为一维数组,包含图像的RGBA四个通道数据
// console.log(imgData.length);

// 将获取到的图像数据去除A通道
var imgArr = [];
for(var i=0; i<imgData.length; i += 4){
imgArr.push(imgData[i], imgData[i+1], imgData[i+2])
}
// console.log(imgArr)

</script>

java: java也有很多别人写好的框架和工具类,也可以用自带的BufferedImage类读取。

1
2
3
4
5
File file  = new File("......文件路径");

BufferedImage bufImg = ImageIO.read(file);

//然后就可以使用BufferedImage类的getWidth(),getHeight(),getRGB(i, j)方法

c++: 由于c++自己实现过于麻烦,所以大多数人都是使用opencv的方法或者是gdiplus.h里的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
GdiplusStartupInput gdiplusstartupinput;
ULONG_PTR gdiplustoken;
GdiplusStartup(&gdiplustoken, &gdiplusstartupinput, NULL);

wstring infilename(L"1.jpg");
string outfilename("color.txt");

Bitmap* bmp = new Bitmap(infilename.c_str());
UINT height = bmp->GetHeight();
UINT width = bmp->GetWidth();
cout << "width " << width << ", height " << height << endl;

Color color;
ofstream fout(outfilename.c_str());

for (UINT y = 0; y < height; y++)
for (UINT x = 0; x < width ; x++) {
bmp->GetPixel(x, y, &color);
fout << x << "," << y << ";"
<< (int)color.GetRed() << ","
<< (int)color.GetGreen() << ","
<< (int)color.GetBlue() << endl;
}

fout.close();

delete bmp;
GdiplusShutdown(gdiplustoken);

灰度化和二值化

灰度化: 对于每个像素的rgb值例如:(255,125,122),这个表示r=255,g=125,b=122,我们通过某种算法让其中r=g=b三值相等,这个过程就叫灰度化,这个相等的值叫灰度值。一般灰度化算法使用两种:
1. 使用平均值算法,就是(r+b+g)/3
2. r * 0.3+ g * 0.59 +b * 0.11

阈值:阈值又叫临界值,不管你的图片是什么样的彩色,它最终都会把图片当黑白图片处理,也就是说你设定了一个阈值之后,它会以此值作标准,凡是比该值大的颜色就会转换成白色,低于该值的颜色就转换成黑色,所以最后的结果是,你得到一张黑白的图片。阈值的作用就是得到一张对比度不同的图片。

二值化: 其实就是将超过阈值的灰度值转为白色,低于阈值的设置为黑色的过程。

像素化: 对于像素值除了rgb其实还有一个a值,a代表透明度值,我们不对rgb值进行灰度化和二值化,只对透明度值进行二值化,这样我们就可以获得颜色分界明显的图片,也就是像素图,这个过程叫像素化。

样例代码

使用js来写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var thresholdConvert = function(ctx, imageData, threshold, mode) {
var data = imageData.data;
for (var i = 0; i < data.length; i += 4) {
var red = data[i];
var green = data[i + 1];
var blue = data[i + 2];
var alpha = data[i + 3];

// 灰度计算公式
var gray = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 *data[i + 2];

var color = gray >= threshold ? 255 : 0;

data[i] = (mode == 0 && color == 0) ? red : color; // red
data[i + 1] = (mode == 0 && color == 0) ? green : color; // green
data[i + 2] = (mode == 0 && color == 0) ? blue : color; // blue
data[i + 3] = alpha >= threshold ? 255 : 0; // 去掉透明
}
ctx.putImageData(imageData, 0, 0);
};

参考链接

https://blog.csdn.net/weixin_44554475/article/details/102478494

https://www.cnblogs.com/Toya/p/11431992.html

https://github.com/chuiliu/the-pixel-art