由于之前在网上看到的关于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]

由此我们可以知道:

  1. Mat里面彩色图片的每个像素都按照B、G、R的图层顺序存储的
  2. 存储的每个单位的值都介于0~255之间
  3. 一行中的所有像素以及每个像素的三个图层之间都以逗号分开
  4. 行与行之间以分号隔开

得知上述信息后就好办了~为了方便解释,我们将上面得到的矩阵用二进制的形式展示一下:

[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.reshape(1, 1));
}template<typename _Tp>
cv::Mat convertVectorToMat(vector<_Tp> v, int channels, int rows) {cv::Mat mat = cv::Mat(v);cv::Mat dest = mat.reshape(channels, rows).clone();dest.convertTo(dest, CV_8U);return dest;
}void showImageLSBWatermark(cv::Mat image, int num) {cv::Mat dst_img;if (num > 7 || num < 0) num = 0;vector<int> v = convertMatToVector<int>(image);vector<int> u;int series = pow(2, num);int j = 0, k = 0;for (int i = 0; i < v.size() / 3; ++i) {j = 0, k = 0;v[3*i] / series % 2 == 0 ? ++j : ++k;v[3*i+1] / series % 2 == 0 ? ++j : ++k;v[3*i+2] / series % 2 == 0 ? ++j : ++k;u.push_back(j > k ? 0 : 255);}dst_img = convertVectorToMat(u, 1, image.rows);cv::imshow("dst_img", dst_img);
}template<typename _Tp>
vector<_Tp> drawWatermarkOnImage(vector<_Tp> v, vector<_Tp> w, int num) {if (num > 7 || num < 0) return v;int series = pow(2, num);for (int i = 0; i < v.size(); ++i) {if (v[i] / series % 2 != 0) v[i] -= series;v[i] += pow(2, num) * w[i];}return v;
}cv::Mat imageLSB(cv::Mat src_img, cv::Mat mrk_img, int num) {if (src_img.size() != mrk_img.size()) {cv::copyMakeBorder(mrk_img, mrk_img, 0, src_img.rows - mrk_img.rows,0, src_img.cols - mrk_img.cols,cv::BORDER_WRAP, Scalar::all(0));}cv::Mat dst_img;vector<int> src_v = convertMatToVector<int>(src_img);vector<int> mrk_v = convertMatToVector<int>(mrk_img);for (int i = 0; i < mrk_v.size(); ++i) {mrk_v[i] = mrk_v[i] < 120 ? 0 : 1 ;}src_v = drawWatermarkOnImage(src_v, mrk_v, num);dst_img = convertVectorToMat(src_v, 1 , src_img.rows);return dst_img;
}int main (int argc, char* argv[]) {if (argc < 2) {cout << "Please choose enc/show" << endl;return -1;}if (strcmp(argv[1], "enc") == 0) {if (argc < 5) {cout << "Please enter src_img && watermark && rank(0~7)" << endl;return -1;}cv::Mat img_src = imread(argv[2],cv::IMREAD_COLOR);cv::Mat img_mrk = imread(argv[3],cv::IMREAD_GRAYSCALE);std::vector<cv::Mat> imgs;cv::split(img_src, imgs);int num = atoi(argv[4]);//给三个图层分别添加,也可一选择其中一个加水印for (int i = 0; i < 3; ++i) {imgs[i] = imageLSB(imgs[i], img_mrk, num);}cv::Mat img_dst;cv::merge(imgs, img_dst);cv::imshow("src",img_src);cv::imshow("dst",img_dst);cout << "\033[32m[TIPS]: WRITING IMAGE \033[0m" << endl;//无损压缩,其他格式保存会失真cv::imwrite("LSB.bmp", img_dst); }  else if (strcmp(argv[1], "show") == 0) {if (argc < 4) {cout << "Please enter src_img && rank(0~7)" << endl;return -1;}cv::Mat img_src = imread(argv[2],cv::IMREAD_COLOR);cv::imshow("src", img_src);int num = atoi(argv[3]);showImageLSBWatermark(img_src, num);}cv::waitKey(0);return 0;
}

三、测试:

水印图:

原图:

在最低位嵌入水印(±1):

在倒数第二位嵌入水印(±3):

在倒数第三位嵌入水印(±7):

在倒数第四位嵌入水印(±15):


第三位时已经有肉眼明显可见的水印样子出来了,第四位的时候更加明显,第二位时需要仔细看才能看的到而第一位基本看不到了
而以上所有图片,把加水印的那一层提取出来,都能得到很清晰的水印:

目前图片格式为bmp,将图片通过PS转换为jpg格式后提取到的水印为:
(此处使用的是在倒数第二位加水印)

将图片缩放后测试水印:


对图片进行裁切后测试:

四、总结:

优点:

1.添加水印后即不影响图片的整体美观,又能留住水印里的信息
2.对图片进行裁切、涂改编辑后,保留的原图部分依然能提取到清晰的水印信息

缺点:

1.对图片进行压缩编辑后,水印信息会受到较大干扰
2.水印信息可以在后期进行擦除,可以很容易的被破坏

C++:图片数字水印-基于OpenCV+LSB相关推荐

  1. python打开摄像头获取图片_Python基于opencv调用摄像头获取个人图片的实现方法

    接触图像领域的应该对于opencv都不会感到陌生,这个应该算是功能十分强劲的一个算法库了,当然了,使用起来也是很方便的,之前使用Windows7的时候出现多该库难以安装成功的情况,现在这个问题就不存在 ...

  2. python利用opencv去除图片logo_python 基于opencv去除图片阴影

    一.前言 如果你自己打印过东西,应该有过这种经历.如果用自己拍的图片,在手机上看感觉还是清晰可见,但是一打印出来就是漆黑一片.比如下面这两张图片: 因为左边的图片有大片阴影,所以打印出来的图片不堪入目 ...

  3. php配置辨别图片形式,基于OpenCV的PHP图像人脸辨别技术(转载)

    当前位置:我的异常网» 图形/图像 » 基于OpenCV的PHP图像人脸辨别技术(转载) 基于OpenCV的PHP图像人脸辨别技术(转载) www.myexceptions.net  网友分享于:20 ...

  4. 图片处理——基于openCV实现美颜相机

    今天是2017年最后一晚,希望大家元旦前夕玩得开心,准备迎接2018全新的一年,活出程序员的态度. 最近发现有些女孩在朋友圈发的自拍照肤白貌美,甚至头上魔幻般地长出猫耳朵.猫鼻子.猫胡须,各种调皮搞怪 ...

  5. 基于OpenCv的照片美化工具的设计与实现

    随着我们对美好事物的向往,追求更美的自己,尤其是在手机拍照时,更能体现,现在不同的手机拍出的效果截然不同,像华为手机拍照拍出的就特别的清晰,opp手机拍出的效果就非常的柔和,像小米手机拍出的效果就非常 ...

  6. 基于MatLab实现LSB(最低有效位)算法完成图片数字水印隐写功能

    文章目录 前言 一.图像处理基础 二.LSB数字隐写算法 三.LSB数字隐写算法实现 四.数字隐写和提取过程 总结 前言 已经好久没写博客了,最近是在有些忙,但是忙里偷闲写一篇新学的知识点,所以准备写 ...

  7. OpenCV2学习笔记(十四):基于OpenCV卡通图片处理

    得知OpenCV有一段时间.除了研究的各种算法的内容.除了从备用,据导游书籍和资料,尝试结合链接的图像处理算法和日常生活,第一桌面上(随着摄像头)完成了一系列的视频流处理功能.开发平台Qt5.3.2+ ...

  8. java图片降噪_Java基于opencv实现图像数字识别(四)—图像降噪

    Java基于opencv实现图像数字识别(四)-图像降噪 我们每一步的工作都是基于前一步的,我们先把我们前面的几个函数封装成一个工具类,以后我们所有的函数都基于这个工具类 这个工具类呢,就一个成员变量 ...

  9. python头像转卡通_Python实现将照片变成卡通图片的方法【基于opencv】

    本文实例讲述了Python实现将照片变成卡通图片的方法.分享给大家供大家参考,具体如下: 之前的文章介绍了使用Photoshop将照片变成卡通图片,今次介绍用代码来实现这项任务,可以就此探查各种滤镜的 ...

最新文章

  1. 单例模式——Singleton
  2. 我看team work
  3. Linux 的文件权限与目录配置
  4. 科大星云诗社动态20210124
  5. Linux从入门到精通——磁盘与目录的容量(du、df)
  6. RTFM? 如何写一本值得一读的手册
  7. java 反射 本类_Java 反射 Class类
  8. Android Phonebook编写联系人UI加载及联系人保存流程(六)
  9. 深度学习自学(十六):caffe-sphereface识别代码编译问题-caffe sudo make test报错
  10. 关于yolov3在训练自己数据集时容易出现的bug集合,以及解决方法
  11. 软件工程师和程序员到底有多大的区别?
  12. 【bat】一个脚本文件,关闭IE,重置IE,配置IE,设置IE的ActionX等选项.并自动管理员身份运行
  13. 【Android】Android 集成芯烨云打印机实现打印票据
  14. SEO-老域名的选择
  15. C++的游戏--贪吃蛇
  16. TC限速原理和TC限速的实现
  17. Qt图形视图框架:视图增加标尺
  18. AI:人工智能技术层企业简介(更新中)
  19. Master和Slave是什么意思
  20. 自动驾驶传感器产业链

热门文章

  1. ServerSocket通过构造方法绑定端口
  2. 宋鸿兵 - 货币战争5(2014年3月20日)
  3. 企查查在哪查实缴_如何查询一家企业的注册资金是实缴的还是认缴的?
  4. Python学习笔记(一)
  5. 让手机支持OTG,不看绝对后悔! - 我也做一回搬运工,解决RFID读卡器OTG支持问题
  6. Java——Shape类
  7. 博弈论学习(二)——完全信息静态博弈
  8. 追女孩的九大禁忌!!!
  9. 电商购物平台——书籍管理系统Java贯穿项目图形用户界面
  10. Python 股票分析入门