由于之前在网上看到的关于LSB的方法大都是以MATLAB||Python写的,于是博主基于其思路改编后得出以下的内容:
一、原理:
1.首先准备一张彩色的图片(2px*2px)
为了方便展示,我用PS准备了一张(2px * 2px)的图片,图片的每个像素里的RGB值分别为:
位置 | R | G | B |
---|---|---|---|
[0,0] | 10 | 100 | 200 |
[0,1] | 200 | 100 | 10 |
[1,0] | 200 | 10 | 100 |
[1,1] | 100 | 10 | 200 |
图片格式保存为bmp(无损压缩),保存jpg时会对图片内容产生压缩
2.在终端上查看这张图片
需要注意的是这里查看的代码不是cv::imshow()
,而是用cout
int main(int argc, char* argv[]) {
if (argc < 2) return -1;
cv::Mat img = imread(argv[1],cv::IMREAD_COLOR);
cout << img << endl;
return 0;
}
$ ./a.out test.bmp
[200, 100, 10, 10, 100, 200;
100, 10, 200, 200, 10, 100]
由此我们可以知道:
- 在Mat里面彩色图片的每个像素都按照B、G、R的图层顺序存储的
- 存储的每个单位的值都介于0~255之间
- 一行中的所有像素以及每个像素的三个图层之间都以逗号分开
- 行与行之间以分号隔开
得知上述信息后就好办了~为了方便解释,我们将上面得到的矩阵用二进制的形式展示一下:
[11001000, 01100100, 00001010, 00001010, 01100100, 11001000;
01100100, 00001010, 11001000, 11001000, 00001010, 01100100]
到了这一步就可以来愉快的解释LSB了,假设我们把每个单元里的最低有效位都归零,然后再按照需求填入我们想要的信息
如果改变的是最后一位,那在最终的数值上只是±1,对图片的颜色而言,肉眼几乎不会感到有什么变化。即便修改的是倒数第二位,最终的数值上也就±3,感觉还可以(在最后测试的时候会放上效果图)。修改倒数第三位的话,最终的数值±5,以此类推……
3.准备一张水印图片(黑白)
为了方便演示,我将水印图和原图的大小设为一样的了。下方的代码不用担心水印图和原图尺寸不一致的问题~
假设上图是我要加的水印,信息只有黑白两色,那么我们可以将黑色定义为0;白色定义为1。然后把信息写入原图当中~
4.为原图添加水印信息
原图:
[11001000, 01100100, 00001010, 00001010, 01100100, 11001000;
01100100, 00001010, 11001000, 11001000, 00001010, 01100100]
水印图:
[00000001, 00000001, 00000001, 00000000, 00000000, 00000000;
00000000, 00000000, 00000000, 00000001, 00000001, 00000001]
原图 + 水印图:
[11001001, 01100101, 00001011, 00001010, 01100100, 11001000;
01100100, 00001010, 11001000, 11001001, 00001011, 01100101]
原图:
添加水印后的图片:
还原之后的水印:
这样水印的信息就已经添加到了图片里面,但肉眼几乎看不出有什么区别。
需要注意的是,图片的保存格式最好选择bmp格式,若以其它格式保存(jpeg/png)会压缩失真。
失真后的图片水印也不会像之前那么清楚,这也是这个方法测试下来的一大缺点!
二、源码C:
/*************************************************************************
> File Name: 001_waterMarkLSB.cpp
> Author: PeterShen
> Mail: peter704678976@126.com
> Created Time: 2019年08月25日 星期日 00时00分09秒
************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <typeinfo>
#include <opencv2/core/utility.hpp>
#include <opencv2/video/tracking.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include "opencv2/imgproc/types_c.h"
using namespace cv;
template<typename _Tp>
vector<_Tp> convertMatToVector(const Mat &mat) {
return (vector<_Tp>)(mat.<