按照这些做还是截不了屏

您的位置:
阿里根据截图查到泄露者,这样的技术是如何做到的?
来源:知乎
原文出处:
【导读】:在月饼事件中,阿里开除了 5 位用脚本秒杀月饼的技术人员。后有员工对外泄露了内网通报的截图,泄露者已被查出并处理。后续有媒体报道提到,阿里对员工的访问界面加了一层肉眼无法识别的东西。有网友对这个技术好奇,在知乎提问:
在月饼事件中的新闻中提到。阿里对员工访问的界面做了一定的处理。貌似这不是简单的水印。这种处理是什么,是怎么做到的呢?
本文通过一个的实验,简要介绍频域手段添加数字盲水印的方法,并进一步验证其抗攻击性。在上述实验的基础上,总结躲避数字盲水印的方法。(多图预警)
本文分为五个部分,第一部分综述;第二部分频域数字盲水印制作原理介绍;第三部分盲水印攻击性实验;第四部分总结;第五部分附录(源代码)。
本文提供的一种实现“阿里通过肉眼无法识别的标识码追踪员工”的技术手段。通过看其他答主的分析,阿里可能还没用到频域加水印的技术。
在原答案中,存在措辞欠妥之处,对此表示由衷的歉意。
相对于空域方法,频域加盲水印的方法隐匿性更强,抵抗攻击能力更强。这类算法解水印困难,你不知道水印加在那个频段,而且受到攻击往往会破坏图像原本内容。本文简要科普通过频域手段添加数字盲水印。对于web,可以添加一个背景图片,来追踪截图者。
所谓盲水印,是指人感知不到的水印,包括看不到或听不见(没错,数字盲水印也能够用于音频)。其主要应用于音像作品、数字图书等,目的是,在不破坏原始作品的情况下,实现版权的防护与追踪。
添加数字盲水印的方法简单可分为空域方法和频域方法,这两种方法添加了冗余信息,但在编码和压缩情况不变的情况下,不会使原始图像大小产生变化(原来是10MB添加盲水印之后还是10MB)。
空域是指空间域,我们日常所见的图像就是空域。空域添加数字水印的方法是在空间域直接对图像操作(之所以说的这么绕,是因为不仅仅原图是空域,原图的差分等等也是空域),比如将水印直接叠加在图像上。
我们常说一个音有多高,这个音高是指频率;同样,图像灰度变化强烈的情况,也可以视为图像的频率。频域添加数字水印的方法,是指通过某种变换手段(傅里叶变换,离散余弦变换,小波变换等)将图像变换到频域(小波域),在频域对图像添加水印,再通过逆变换,将图像转换为空间域。相对于空域手段,频域手段隐匿性更强,抗攻击性更高。
所谓对水印的攻击,是指破坏水印,包括涂抹,剪切,放缩,旋转,压缩,加噪,滤波等。数字盲水印不仅仅要敏捷性高(不被人抓到),也要防御性强(抗打)。就像Dota的敏捷英雄往往是脆皮,数字盲水印的隐匿性和鲁棒性是互斥的。(鲁棒性是抗攻击性的学术名字)
二、频域制作数字盲水印的方法
信号是有频率的,一个信号可以看做是无数个不同阶的正弦信号的的叠加。
上式为傅里叶变换公式,?(t) 是指时域信号(对于信号我们说时域,因为是与时间有关的,而图像我们往往说空域,与空间有关),ω 是指频率。想要对傅里叶变换有深入了解的同学,建议看一下《信号与系统》或者《数字信号处理》的教材,里面系统介绍了傅里叶变换、快速傅里叶变换、拉普拉斯变换、z变换等。
简而言之,我们有方法将时域信号转换成为频域,同样,我们也能将二维信号(图像)转换为频域。在上文中提到,图像的频率是指图像灰度变换的强烈情况。关于此方面更系统的知识,参见冈萨雷斯的《图像处理》。
下面以傅里叶变换为例,介绍通过频域给图像添加数字盲水印的方法。注意,因为图像是离散信号,我们实际用的是离散傅里叶变换,在本文采用的都是二维快速傅里叶变换,快速傅里叶变换与离散时间傅里叶变换等价,通过蝶型归并的手段,速度更快。下文中傅里叶变换均为二维快速傅里叶变换。
上图为叠加数字盲水印的基本流程。编码的目的有二,一是对水印加密,二控制水印能量的分布。以下是叠加数字盲水印的实验。这是原图像,尺寸300*240 (不要问我为什么不用Lena,那是我前女友),
之后进行傅里叶变换,下图变换后的频域图像,
这是我想加的水印,尺寸200*100,
这是我编码后的水印,编码方式采用随机序列编码,通过编码,水印分布到随机分布到各个频率,并且对水印进行了加密,
将上图与原图的频谱叠加,可见图像的频谱已经发生了巨大的变化,
之后,将叠加水印的频谱进行傅里叶逆变换,得到叠加数字水印后的图像,
肉眼几乎看不出叠加水印后的图像与原图的差异,这样,数字盲水印已经叠加到图像中去。实际上,我们是把水印以噪声的形式添加到原图像中。下图是在空域上的加水印图与原图的残差(调整了对比度,不然残差调小看不见),
可以看出,实际上上述方法是通过频域添加冗余信息(像噪声一样)。这些噪声遍布全图,在空域上并不容易破坏。
最终,均方误差(MSE)为0.0244
信噪比(PSNR)为64.2dB那么,为什么频谱发生了巨大的变化,而在空域却变化如此小呢?这是因为我们避开了图像的主要频率。下图是原图频谱竖过来的样子,其能量主要集中在低频。
水印提取是水印叠加的逆过程,
经提取后,我们得到如下水印,问:为什么水印要对称呢?嘿嘿,大家想想看。
三、攻击性实验
本部分进行攻击性实验,来验证通过频域手段叠加数字盲水印的鲁棒性。
1.进行涂抹攻击,这是攻击后的图片:
再进行水印提取:
2.进行剪切攻击,就是网上经常用的截图截取一部分的情况:
进行循环补全:
提取水印:
3.伸缩攻击(这个实验明码做的,水印能量较高,隐匿性不强):
提取水印(水印加的不好,混频挺严重的):
4.旋转攻击(明码):
提取水印:
5.JPEG压缩后(这个实验我好像是拿明码做的,能量主要加在了高频):
提取结果:
6.PS 4像素马赛克/均值滤波等,攻击后图像(这是我女朋友吗?丑死了):
提取水印后图像:
截屏后我手动抠出要测试的图像区域,并且抽样或者插值到原图尺寸:
测试结果:
8. 亮度调节(明码):
水印提取:
9.色相调节(明码):
水印提取:
10.饱和度调节(明码):
11.对比度(明码):
12.评论区用waifu2x去噪后图片:
13.美图秀秀,我对我女票一键美颜,美白,磨皮,加腮红,加唇彩(有一种很羞耻的感觉,捂脸):
提取水印:
14.对于背景纯色的图其实也是无所谓的
能量系数为10时加水印图片:觉得太显噪就把能量系数调低,不过水印的隐秘性和鲁棒性是互斥的
最终提取出的水印:
15.我用将RGB&600的像素设置成为(0,255,0)来模拟PS魔术手,
提取水印为:
16.屏摄,好吧,这个实验我做哭了
实验结果:
我把水印能量系数调整到2000都没有用。
屏摄之后与原图信噪比为4dB左右,我用多抽样滤波的方式试过,滤不掉屏摄引入的噪声。屏摄不仅引入了椒盐噪声,乘性噪声,还有有规律的雪花纹理(摩尔纹)。
基于频域的盲水印方法隐藏性强,鲁棒性高,能够抵御大部分攻击。但是,对于盲水印算法,健壮性和隐匿性是互斥的。
本文方法针对屏摄不行,我多次实验没有成功,哪位大神可以做一下或者讨论讨论。还有二值化不行,这是我想当然的,觉得肯定不行所以没做实验。其他的我试了试,用给出的方法调整一下能量系数都可以。我想大家最关心的是什么最安全,不会被追踪。
不涉及图像的都安全,比如拿笔记下来。涉及图像的屏摄最安全,截屏十分不安全。
=====彩蛋====
我在上图明码写入了信息。为了抵抗jpg压缩,我水印能量较高,并且因为没有编码,能量分布不均。图中规律性纹路,就是你懂的。嘿嘿,你懂的,解开看看吧。
在答案中给出了上图隐写的内容,(雾)。
%% 傅里叶变换加水印源代码
%% 运行环境 Matlab2010a
alpha = 1;
%% read data
im = double(imread('gl1.jpg'))/255;
mark = double(imread('watermark.jpg'))/255;
figure, imshow(im),title('original image');
figure, imshow(mark),title('watermark');
%% encode mark
imsize = size(im);
TH=zeros(imsize(1)*0.5,imsize(2),imsize(3));
TH1(1:size(mark,1),1:size(mark,2),:) =
M=randperm(0.5*imsize(1));
N=randperm(imsize(2));
save('encode.mat','M','N');
for i=1:imsize(1)*0.5
for j=1:imsize(2)
TH(i,j,:)=TH1(M(i),N(j),:);
% symmetric
mark_ = zeros(imsize(1),imsize(2),imsize(3));
mark_(1:imsize(1)*0.5,1:imsize(2),:)=TH;
for i=1:imsize(1)*0.5
for j=1:imsize(2)
mark_(imsize(1)+1-i,imsize(2)+1-j,:)=TH(i,j,:);
figure,imshow(mark_),title('encoded watermark');
%imwrite(mark_,'encoded watermark.jpg');
%% add watermark
FA=fft2(im);
figure,imshow(FA);title('spectrum of original image');
FB=FA+alpha*double(mark_);
figure,imshow(FB); title('spectrum of watermarked image');
FAO=ifft2(FB);
figure,imshow(FAO); title('watermarked image');
%imwrite(uint8(FAO),'watermarked image.jpg');
RI = FAO-double(im);
figure,imshow(uint8(RI)); title('residual');
%imwrite(uint8(RI),'residual.jpg');
xl = 1:imsize(2);
yl = 1:imsize(1);
[xx,yy] = meshgrid(xl,yl);
figure, plot3(xx,yy,FA(:,:,1).^2+FA(:,:,2).^2+FA(:,:,3).^2),title('spectrum of original image');
figure, plot3(xx,yy,FB(:,:,1).^2+FB(:,:,2).^2+FB(:,:,3).^2),title('spectrum of watermarked image');
figure, plot3(xx,yy,FB(:,:,1).^2+FB(:,:,2).^2+FB(:,:,3).^2-FA(:,:,1).^2+FA(:,:,2).^2+FA(:,:,3).^2),title('spectrum of watermark');
%% extract watermark
FA2=fft2(FAO);
G=(FA2-FA)/
for i=1:imsize(1)*0.5
for j=1:imsize(2)
GG(M(i),N(j),:)=G(i,j,:);
for i=1:imsize(1)*0.5
for j=1:imsize(2)
GG(imsize(1)+1-i,imsize(2)+1-j,:)=GG(i,j,:);
figure,imshow(GG);title('extracted watermark');
%imwrite(uint8(GG),'extracted watermark.jpg');
%% MSE and PSNR
C=double(im);
RC=double(FAO);
MSE=0; PSNR=0;
for i=1:imsize(1)
for j=1:imsize(2)
MSE=MSE+(C(i,j)-RC(i,j)).^2;
MSE=MSE/360.^2;
PSNR=20*log10(255/sqrt(MSE));
%% attack test
%% attack by smearing
%A = double(imread('gl1.jpg'));
%B = double(imread('attacked image.jpg'));
attack = 1-double(imread('attack.jpg'))/255;
figure,imshow(attack);
FAO_ = FAO;
for i=1:imsize(1)
for j=1:imsize(2)
if attack(i,j,1)+attack(i,j,2)+attack(i,j,3)&0.5
FAO_(i,j,:) = attack(i,j,:);
figure,imshow(FAO_);
%extract watermark
FA2=fft2(FAO_);
G=(FA2-FA)*2;
for i=1:imsize(1)*0.5
for j=1:imsize(2)
GG(M(i),N(j),:)=G(i,j,:);
for i=1:imsize(1)*0.5
for j=1:imsize(2)
GG(imsize(1)+1-i,imsize(2)+1-j,:)=GG(i,j,:);
figure,imshow(GG);title('extracted watermark');
%% attack by cutting
FAO_ = FAO;
FAO_(:,s2*imsize(2)+1:imsize(2),:) = FAO_(:,1:int32((1-s2)*imsize(2)),:);
figure,imshow(FAO_);
%extract watermark
FA2=fft2(FAO_);
G=(FA2-FA)*2;
for i=1:imsize(1)*0.5
for j=1:imsize(2)
GG(M(i),N(j),:)=G(i,j,:);
for i=1:imsize(1)*0.5
for j=1:imsize(2)
GG(imsize(1)+1-i,imsize(2)+1-j,:)=GG(i,j,:);
figure,imshow(GG);title('extracted watermark');
%%小波变换加水印,解水印大家按照加的思路逆过来就好
%% read data
im = double(imread('gl1.jpg'))/255;
mark = double(imread('watermark.jpg'))/255;
figure, imshow(im),title('original image');
figure, imshow(mark),title('watermark');
%% RGB division
im=double(im);
mark=double(mark);
imr=im(:,:,1);
markr=mark(:,:,1);
img=im(:,:,2);
markg=mark(:,:,2);
imb=im(:,:,3);
markb=mark(:,:,3);
%% parameter
%% wavelet tranform and add watermark
[Cwr,Swr]=wavedec2(markr,1,'haar');
[Cr,Sr]=wavedec2(imr,2,'haar');
% add watermark
Cr(1:size(Cwr,2)/16)=...
Cr(1:size(Cwr,2)/16)+r*Cwr(1:size(Cwr,2)/16);
while k&=size(Cr,2)/size(Cwr,2)-1
Cr(1+size(Cr,2)/4+k*size(Cwr,2)/4:size(Cr,2)/4+...
(k+1)*size(Cwr,2)/4)=Cr(1+size(Cr,2)/4+...
k*size(Cwr,2)/4:size(Cr,2)/4+(k+1)*size(Cwr,2)/4)+...
r*Cwr(1+size(Cwr,2)/4:size(Cwr,2)/2);
Cr(1+size(Cr,2)/2+k*size(Cwr,2)/4:size(Cr,2)/2+...
(k+1)*size(Cwr,2)/4)=Cr(1+size(Cr,2)/2+...
k*size(Cwr,2)/4:size(Cr,2)/2+(k+1)*size(Cwr,2)/4)+...
r*Cwr(1+size(Cwr,2)/2:3*size(Cwr,2)/4);
Cr(1+3*size(Cwr,2)/4+k*size(Cwr,2)/4:3*size(Cwr,2)/4+...
(k+1)*size(Cwr,2)/4)=Cr(1+3*size(Cr,2)/4+...
k*size(Cwr,2)/4:3*size(Cr,2)/4+(k+1)*size(Cwr,2)/4)+...
r*Cwr(1+3*size(Cwr,2)/4:size(Cwr,2));
Cr(1:size(Cwr,2)/4)=Cr(1:size(Cwr,2)/4)+r*Cwr(1:size(Cwr,2)/4);
% for green
[Cwg,Swg]=WAVEDEC2(markg,1,'haar');
[Cg,Sg]=WAVEDEC2(img,2,'haar');
Cg(1:size(Cwg,2)/16)=...
Cg(1:size(Cwg,2)/16)+g*Cwg(1:size(Cwg,2)/16);
while k&=size(Cg,2)/size(Cwg,2)-1
Cg(1+size(Cg,2)/4+k*size(Cwg,2)/4:size(Cg,2)/4+...
(k+1)*size(Cwg,2)/4)=Cg(1+size(Cg,2)/4+...
k*size(Cwg,2)/4:size(Cg,2)/4+(k+1)*size(Cwg,2)/4)+...
g*Cwg(1+size(Cwg,2)/4:size(Cwg,2)/2);
Cg(1+size(Cg,2)/2+k*size(Cwg,2)/4:size(Cg,2)/2+...
(k+1)*size(Cwg,2)/4)=Cg(1+size(Cg,2)/2+...
k*size(Cwg,2)/4:size(Cg,2)/2+(k+1)*size(Cwg,2)/4)+...
g*Cwg(1+size(Cwg,2)/2:3*size(Cwg,2)/4);
Cg(1+3*size(Cg,2)/4+k*size(Cwg,2)/4:3*size(Cg,2)/4+...
(k+1)*size(Cwg,2)/4)=Cg(1+3*size(Cg,2)/4+...
k*size(Cwg,2)/4:3*size(Cg,2)/4+(k+1)*size(Cwg,2)/4)+...
g*Cwg(1+3*size(Cwg,2)/4:size(Cwg,2));
Cg(1:size(Cwg,2)/4)=Cg(1:size(Cwg,2)/4)+g*Cwg(1:size(Cwg,2)/4);
% for blue
[Cwb,Swb]=WAVEDEC2(markb,1,'haar');
[Cb,Sb]=WAVEDEC2(imb,2,'haar');
Cb(1:size(Cwb,2)/16)+b*Cwb(1:size(Cwb,2)/16);
while k&=size(Cb,2)/size(Cwb,2)-1
Cb(1+size(Cb,2)/4+k*size(Cwb,2)/4:size(Cb,2)/4+...
(k+1)*size(Cwb,2)/4)=Cb(1+size(Cb,2)/4+...
k*size(Cwb,2)/4:size(Cb,2)/4+(k+1)*size(Cwb,2)/4)+...
g*Cwb(1+size(Cwb,2)/4:size(Cwb,2)/2);
Cb(1+size(Cb,2)/2+k*size(Cwb,2)/4:size(Cb,2)/2+...
(k+1)*size(Cwb,2)/4)=Cb(1+size(Cb,2)/2+...
k*size(Cwb,2)/4:size(Cb,2)/2+(k+1)*size(Cwb,2)/4)+...
b*Cwb(1+size(Cwb,2)/2:3*size(Cwb,2)/4);
Cb(1+3*size(Cb,2)/4+k*size(Cwb,2)/4:3*size(Cb,2)/4+...
(k+1)*size(Cwb,2)/4)=Cb(1+3*size(Cb,2)/4+...
k*size(Cwb,2)/4:3*size(Cb,2)/4+(k+1)*size(Cwb,2)/4)+...
b*Cwb(1+3*size(Cwb,2)/4:size(Cwb,2));
Cb(1:size(Cwb,2)/4)=Cb(1:size(Cwb,2)/4)+b*Cwb(1:size(Cwb,2)/4);
%% image reconstruction
imr=WAVEREC2(Cr,Sr,'haar');
img=WAVEREC2(Cg,Sg,'haar');
imb=WAVEREC2(Cb,Sb,'haar');
imsize=size(imr);
FAO=zeros(imsize(1),imsize(2),3);
for i=1:imsize(1);
for j=1:imsize(2);
FAO(i,j,1)=imr(i,j);
FAO(i,j,2)=img(i,j);
FAO(i,j,3)=imb(i,j);
figure, imshow(FAO); title('watermarked image');
(转载请保留)
互联网的一些事,已超50万小伙伴关注!欢迎大家对侵犯版权等不合法和不健康的内容进行监督和举报。
内容来自互联网,如牵涉版权,请及时联系本站删除,邮箱:查看: 857|回复: 4
不同零部件定义相同的视角
主题帖子积分
助工, 积分 457, 距离下一级还需 143 积分
助工, 积分 457, 距离下一级还需 143 积分
大家好,我在用Inventor建立了几个结构类似的装配部件,我现在要对每一个截图,在PPT中展示。现在遇到一个问题,如何让这些不同部件完全按照同一个视角显示在屏幕上,然后输出图片。
我查了帮助文档,对于同一个零部件,利用“创建或编辑视图表达”这个功能,可以实现重复打开同一个零部件时,总是按照自定义的那个视角打开。但是不同的零部件之间,似乎没有办法用同一个视角来观察。请高人明示!!
先行谢过啊^_^
主题帖子积分
助工, 积分 457, 距离下一级还需 143 积分
助工, 积分 457, 距离下一级还需 143 积分
期待高人应助啊
主题帖子积分
助工, 积分 457, 距离下一级还需 143 积分
助工, 积分 457, 距离下一级还需 143 积分
我找到了SolidWorks2013开始,已经有这个功能,但是我不太会用,而且我已有模型就是在Inventor中创建的,请陈伯雄老师,能否给个权威的答案呢?
SolidWorks操纵视图-新功能.jpg (35 KB, 下载次数: 0)
11:22 上传
主题帖子积分
副教授, 积分 10818, 距离下一级还需 39182 积分
副教授, 积分 10818, 距离下一级还需 39182 积分
本人也打算转向&&SW& &目前正在计划中&&把现有的一些没有完成的项目 做完&&就准备 。。。&&inventor&&的交流用户太少了。。&&真遇到 棘手的问题了&&也没人能解决。。。
主题帖子积分
年近古稀,尚未糊涂
您说的“不同的零部件之间,似乎没有办法用同一个视角来观察”的需求,我还没想明白原因。但是我认为您一定有一个“同一个视角”的概念,只是还没展开说明。
所以我想问:
您所说的“同一个视角”,是基于原始坐标系?还是基于模型上的某结构?还是依据您认为看着合适的角度?
年近古稀,尚未糊涂
主题帖子积分
高级工程师, 积分 6174, 距离下一级还需 3826 积分
高级工程师, 积分 6174, 距离下一级还需 3826 积分
我似乎有些明白(不敢说很确定),老陈在4楼对楼主的提示的用意。& &
虽说我该“观棋不语”,但本人今天手贱,有些痒痒,故冒昧揣测,老陈在启发引导楼主,其期望的结果,与这个图标有关
00000.png (1 KB, 下载次数: 0)
20:27 上传
,至于中间的过程,本人省去 N 多步,不便细说。因为这中间过程的 N 多步的步骤,也许正是老陈想要启发、引导楼主去做的,我不便点破,(也许我已经说得太多了)。&&
此法,本人亲测可用。
当我们遥望美丽的星空时,我们是在看宇宙的过去,……
德高望重,为人师表奖
优秀斑竹奖
Powered bylcd12864如何产生图画&呵呵&根据这个我做成功了
看到工具箱旁边那个LCD12864很久没用了(当初买回来用的时候只是简单地测试了一下),于是萌生了重新写一次接口程序的想法(而且这次要给它加个图片显示的功能),好,说做就做,就用Atmega16和ICCAVR来做吧,最近这MCU和平台用得比较熟练。
马上从书堆里把当初打印出来的中文datasheet给翻了出来,依葫芦画瓢地写了个初始化程序。好,OK。编译通过。于是又写了一个可以自定义从XY坐标值开始输出显示的函数,再次编译,也通过,OK。于是呼马上写了四行简单的字符烧到单片机上试了一下,嘿嘿,一次通过。如下图:
后来在进一步测试的时候也出了点小问题。就是我是使用USBISP烧写器把程序烧写进AVR的(此时实验板由USBISP烧写器供电),想要实现从第一行的第一个字符开始连续显示""。刚烧写完程序后能看到LCD12864上正常显示"",但是把烧写器从实验板上断开连接,单独用USB给实验板供电的时候,LCD的第一行只是显示"",第一个字符消失了……,左思右想地弄了一个多小时后,终于把问题给解决了,就是把初始化程序的延时适当增加了些,真是奇怪。刚开始一直想不通为什么在烧写器供电的情况下就正常显示,而换到USB供电后就出了问题。后来再想想,估计是跟供电有关。在使用USBISP烧写器供电的时候,LCD的背光灯明显比用USB供电的时候来得亮,而且对比度也高很多,看来是因为换到USB供电后,供电不怎么充足,以至于LCD在上电初始化的时候花上了更多的时间去初始化(因为供电低了,功率小了,跑起来有点力不从心,用的时间就久了嘛……我是觉得可以这样去理解的)
接下来呢,就到了有点难度的画图了。当初刚买到12864的时候只是简单测试了字符显示功能,除了因为画图还不需要用到,另外一个原因就是那datasheet上关于画图那部分的内容不怎么看得懂……。现在重新拿起来看,依然一头雾水……。马上上网百度了一下“12864
显示图片”,看到了不少的例子程序,可是……就是没看到有关于这部分功能实现的详细思路和讲解……下载下来的那些程序,基本上没注释,不是说晦涩难懂,但是至少看起来一团糟,让人家不想继续看下去……于是还是硬着头皮去啃那datasheet。上面对于画图这部分的内容是这样讲解的:
在仔细研究了上面关于它的 X啊 Y啊
那些坐标的定位啊写满了哪些地址会自增啊
哪些不会啊什么的,最后感觉脑袋里有了一种朦胧的概念……哟西,反正不会弄坏,就先随便写个程序试试。
于是乎,嘀咕嘀咕……捣鼓捣鼓……反反复复又弄了一个多小时后,终于摸清了它显示的规律……
LCD12864实现画图功能的思路:
首先,画图指令属于扩充指令集,要使用这些指令必须在12864初始化之后写命令字(0x34)进入扩充指令集设定状态。
接着要做的事就是指定我们的图片要从哪里写入(即写入的XY坐标,这个是最关键,也是最难理解的部分)。因为我们这里是显示一整个画面的图片,所以我们就从12864的第一个点开始显示。那这个点的坐标是怎么定位的呢?我们往这个点写入数据后,要是接着再写数据,那坐标值会怎样变化呢?首先我们要弄清楚12864究竟是怎么把数据写入到GDRAM(绘图显示RAM)中去的。12864(ST7920驱动芯片)把屏幕分成上下两部分(如上图中把垂直坐标分成了两部分的00~1F)。当我们把坐标值写给LCD后(怎么写后面会说),ST7920控制芯片对LCD屏幕的控制过程可以用下面的图片来表示:(后来发现下面那幅图片有点问题……它这里在水平坐标上的00到0F,应该理解为是同一面的,也就是在12864上,水平坐标00到0F处于同一面,而不是上下屏的关系,其实大家只要看箭头,明白控制芯片是按什么顺序写GDRAM的就可以了^_^)
如图片上所标注,在向GDRAM中写入要显示的图片时,我们先指定从X:00、Y:00处(也就是第①处)开始写入数据(如何指定后面会说明),我们先在第①处写图形数据(按照图片所标注,第15位在最左边,第0位在最右边,即在写入的时候LCD会先写高位字节,接着再写低位字节),接着LCD会自动把坐标定位到同一行第②处的开头,此时我们可以接着告诉LCD在这里写入图形数据,依此类推,当我们写满16次后,第00行(包括上半屏和下半屏的)就全写满了。那么我们接下去写入数据会出现什么情况呢?答案是LCD又自动从第00行的第①处重新开始写了。这是因为ST7920控制芯片设计出来就这样,在写入的时候它只会在水平方向(X轴上)地址自增,并且在增加到0F地址之后就会变成00地址从头开始写。从这里我们可以明白,每次写满一行(共16部分)后就必须在程序里人为地把垂直方向(Y轴)的地址加1,不然就会造成只是在同一行重复写入的现象(俺前面试验了好多次都是这个问题)。
如何在写入的时候定位初始XY坐标呢?依据datasheet,进行坐标设定的时候首先设垂直地址,接着设定水平地址,这两个指令是连续写入LCD的(就是进入扩充指令集设定状态后,只需要RS引脚置低电平,RW引脚置低电平,接着连续写入上面两个命令,垂直地址在前,列地址在后就可以了),我们先来看看关于设定GDRAM地址的指令:
从上面的表格我们可以看到,垂直(列)地址由AC6~AC0指定,我们是从00列开始,那自然就是AC6~AC0全为0喽(这里有个问题需要注意,因为我们在写图片数据的时候是一个字节一个字节连续写入的,所以指定列地址的时候就必须为8的整数倍,呵呵,每个字节有8位嘛),那最终写给LCD的指令是如何的呢?就是上面表格紫色字体的部分,例如我是从00列开始写,AC6~AC0为全0,那就是1AC6AC5AC4AC3AC2AC1AC0,也就是1000
0000(二进制),即0x80(十六进制)。写完列地址就开始写水平(行)地址了,行地址由AC3~AC0指定,我们从00行开始,所以就是全0,就是AC1AC0,也就是1000
0000,即0x80。
于是,整个画图的过程可以这样描述:&&&&&
1.初始化LCD(如果之前已经有先进行过初始化,则这一步省略)
2.给LCD写指令0x34,进入扩充指令集设定状态
3.设定GDRAM的列地址和行地址
4.按正确规律(每写满一行,行地址要用程序手动加1)连续写入图形数据
5.给LCD写指令0x36,打开绘图开关(这样做写入的图形才能正常显示)
6.给LCD写指令0x30,返回基本指令集设定状态(也就是正常的字体显示状态)
我们在用C语言设计画图程序时,可以这样写(大家如果对写屏的过程不了解,可以把通过改一下下面的程序,例如把写下半屏的函数给屏蔽掉,然后烧写到单片机上看实际效果,这样可以帮助你更好理解这个程序):
void LCD_DrawPic(uchar flash
pic[64][16]) //定义一个函数,用来向整个屏幕画图
{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
//图片数据为数组形式,格式为64行16列,正好对应12864的屏幕
i,j;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
//图片数据如何生成请看下面
LCD_Write(lcdcmd,0x34);
//笔者自定义的函数,功能为向LCD写入命令0x34,即进入扩充指令集设定状态
for(i=0;i&32;i++)&&&&&
//定义32次循环,先写半屏数据(半屏共32行)
LCD_Write(lcdcmd,0x80|i);&&&&
//0b写列地址&&&&&&&
LCD_Write(lcdcmd,0x80);&&&&
//0b写行位置
for(j=0;j&16;j++)&&&&&&&&&&&&&&&&&&&
//每行共有16部分
&&&&&&&&&&&
LCD_Write(lcddata,pic[i][j]);//笔者自定义的函数,功能为向LCD写入数据
for(i=0;i&32;i++)&&&
//再写剩下的半屏数据
&&&&&&&&&&&
LCD_Write(lcdcmd,0x80|i);&&&&
//0b写列位置
&&&&&&&&&&&
LCD_Write(lcdcmd,0x88);&&&&
//0b写行位置
&&&&&&&&&&&&
for(j=0;j&16;j++)
&&&&&&&&&&&&
&&&&&&&&&&&&&&&
LCD_Write(lcddata,pic[i+32][j]);
&&&&&&&&&&&
LCD_Write(lcdcmd,0x36);//向LCD写入命令0x36,即打开绘图开关,此时显示图形
LCD_Write(lcdcmd,0x30);//向LCD写入命令0x30,返回基本指令集设定状态
那用来做图片的数据应该如何准备呢?这时候我们就要用到字模软件了,网上有很多这方面的软件,在这里我们使用晓奇工作室的LCMZIMO这个软件来作说明。
首先选一幅图片,然后用图像编辑软件截取128*64像素大小,然后转换成单色BMP图片。
调整大小为128*64
调整为单色128*64 BMP格式图片
接着使用LCM字模软件打开图片,按照图示的顺序依次设定,打开准备好的图片,然后点参数确认,最后点数据保存,则图片被转换为64行16列的数组数据:
转换后所得到的数据:
/////////////////////////////////////////////////////////////////////////
Bitmap点阵数据表&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
C:\..\桌面\tp2.bmp,横向取模左高位,数据排列:从左到右从上到下&&
// 图片尺寸: 128 *
64&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
/////////////////////////////////////////////////////////////////////////
unsigned char code nBitmapDot[]
=&&&&&&&&&&&&&&&&&
// 数据表,这里“nBitmapDot[]
”修改为pic1[64][16]即可(pic1名字可自定义)
……(图形数据省略)
接下来就是把这个数组和画图函数整合到你的程序里去然后就可以画出精美的图片啦!
附:程序效果演示视频:
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。}

我要回帖

更多关于 如何做屏幕截图 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信