目录
1.引言
元旦临近,我的桌面收到了这样一张图片
这张图片非常与众不同,它会在操作者对它进行截图操作时自动销毁,也不能进行复制粘贴等操作,同时还无法通过正常方式的到图片的地址,好在它是完全按照我们规定的二维码生成方式生产的。虽然我们在上一篇博客中介绍了二维码识别的方法,但那种方式只适用于无边框、且能够得到图片地址的二维码,而上图中的二维码则无法识别。难道就没办法了吗?通过这篇博客我们将逐步解开这张二维码的神秘面纱,或许其中会有惊喜呢。
2.功能要求
*创建框体,点击窗体上的按钮实现打开和生成功能
*参考美颜相机的图片打开方式,我们可以用JFileChooser方法选择文件来打开该图片。
*打开图片,我们要将图片红框内的图片像素取出并进行识别
*通过对话框和面板将二维码中的信息输出
3.创建窗体
3.1.代码
public class ReaderPro extends JFrame {
public ReaderPro(){
setSize(500,500);
setTitle("二维码识别PRO");
setDefaultCloseOperation(3);
setLocationRelativeTo(null);
FlowLayout f1=new FlowLayout();
setLayout(f1);
JButton btnopen=new JButton("打开");
JButton btn=new JButton("生成");
add(btnopen);
add(btn);
setVisible(true);
}
public static void main(String[] args) {
new ReaderPro();
}
}
3.2.运行效果
4.功能代码
4.1.generateStr()方法
generateStr()方法实现将存储二维码图片像素的二维数组转换为字符串,原理与上一篇博课所讲的相同。见java学习—二维码识别
代码:
public String generateStr(int[][]arr){
int size=arr.length/16;
int strLength=arr[0].length/size;
String codeStr="";
for(int i=size/2;i< arr[0].length;i+=size){
for(int j=size/2 ;j<arr.length;j+=size){
int rgb=arr[j][i];
int red=(rgb>>16)&255;
int green=(rgb>>8)&255;
int blue=rgb&255;
int gray=(red+green+blue)/3;
if(gray<255/2){
codeStr+='1';
}else{
codeStr+='0';
}
}
}
String str="";
String str16="";
for(int i=0;i<codeStr.length();i++){
char ch=codeStr.charAt(i);
str16+=ch;
if(i!=0&&i%16==15){
int bi=Integer.parseInt(str16,2);
str+=(char)bi;
str16="";
}
}
return str;//返回字符串内容
}
4.2.getArr()方法
getArr()方法实现将图片中红色框中二维码取出并单独存放在二维数组arr中,并将arr作为返回值返回。
代码
public int[][] getArr(BufferedImage img){
int w=img.getWidth();//读取缓存图片的宽度
int h=img.getHeight();//读取缓存图片的高度
int[][] arr=new int[w][h];//创建二维数组存储原来图片的像素值
for(int i=0;i< arr.length;i++){
for(int j=0;j<arr[0].length;j++){
arr[i][j]=img.getRGB(i,j);//获取img(i,j)上的rgb值
}
}
//定义变量实现边框定位
int flag=1,x1 = 0,y1=0,x2=0,y2=0;//flag为1表示为遍历到红色边框,边框左上坐标(x1,y1),边框右上坐标(x2,y2)
for(int i=0;i<w;i++){
for(int j=0 ;j<h;j++){
int rgb=arr[i][j];
int red=(rgb>>16)&255;
int green=(rgb>>8)&255;
int blue=rgb&255;
//判断该像素是否为红色
if(red==255&&green==0&&blue==0){
//若flag=1,则说明该坐标为左上
if(flag==1){
x1=i;
y1=j;
flag=0;
}
//若为红色像素则更新x2,y2值
x2=i;
y2=j;
}
}
}
//计算红色边框内二维码的长宽
int wi=x2-x1-2;
int hi=y2-y1-2;
int [][] newArr=new int[hi][wi];//创建二维数组存储标准二维码像素值
//遍历并将arr对应位置像素值存储到newArr中
for(int i=0;i<hi;i++){
for(int j=0;j<wi;j++){
newArr[i][j]=arr[j+x1+2][i+y1+2];
}
}
return newArr;//将生成的二维码数组返回
}
4.3.debug()方法
在我们们编写代码的过程中,最后读取的二维码内容很可能会乱码,很大一部分原因是我们getArr()方法读取的二维码范围或方向不对。那只要将读取的红色框中的二维码保存下来不就可以直观的知道哪里有误了吗。通过debug()方法实现该功能,将二维码图片保存到指定路径下。
public void debug(int[][] arr){
BufferedImage arrImg=new BufferedImage(arr[0].length,arr.length,2);//根据arr大小创建缓存图像
Graphics bg=arrImg.getGraphics();
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr[0].length;j++){
Color c1=new Color(arr[i][j]);//获取像素颜色
bg.setColor(c1);//设置画笔颜色
bg.fillRect(j,i,1,1);//在缓存图片对应位置填充
}
}
try {
ImageIO.write(arrImg, "PNG", new File("img/qrcode.png"));//保存
} catch (IOException m) {
throw new RuntimeException(m);
}
}
5.创建监听器
有了窗体和核心功能代码,接下来要做的就是将窗体与对应功能结合起来,实现点击上面不同的按钮实现不同的功能
5.1.创建ProListener监听器
public class ProListener implements ActionListener {
BufferedImage img;//创建缓存图片对象
@Override
public void actionPerformed(ActionEvent e) {
String btntxt=e.getActionCommand();//获取按钮上的文字
//按钮上的文字为打开
if(btntxt.equals("打开")){
JFileChooser chooser=new JFileChooser();//创建JFileChooser对象实现打开文件
FileNameExtensionFilter filter=new FileNameExtensionFilter("JPG & PNG Images", "jpg", "png", "jpeg");//创建FileNameExtensionFilter对象筛选图片文件
chooser.setFileFilter(filter);//添加文件筛选器
int returnVal=chooser.showOpenDialog(null);//下文讲解
//如果文件正常打开
if(returnVal==JFileChooser.APPROVE_OPTION){
System.out.println("You choose this file"+chooser.getSelectedFile().getName());//输出提示信息
String path=chooser.getSelectedFile().getPath();//获取图片路径
File file=new File(path);//创建文件对象
try {
img= ImageIO.read(file);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
else if(btntxt.equals("生成")) {
//如果未读取图片
if(img==null){
JOptionPane.showMessageDialog(null,"请先选择一张二维码!");//提示
}else{
int[][] arr=getArr(img);//调用getArr()方法获可识别的二维码数组
debug(arr);//调试,将arr中存储图片保存
String str=generateStr(arr);//将arr中信息转换为字符串
System.out.println(str);//输出
JOptionPane.showMessageDialog(null,"二维码包含的信息为:"+str);//同时用对话框提示
}
}
}
}
下面讲解一下以下代码
int returnVal=chooser.showOpenDialog(null);
1.showOpenDialog(null):这是JFileChooser类的一个方法,用于显示文件选择对话框并等待用户选择,该方法返回一个整数,表示用户所做的选择。
*.如果用户选择了文件并点击了打开,其返回值为JFileChooser.APPROVE_OPTION。
*.如果用户取消了对话框,其返回值为JFileChooser.CANCEL_OPTION。
*.如果发生错误,则返回其他值。
2. int returnVal:这是一个整数类型的变量,用于存储showOpenDialog()方法的返回值,通过检测这个返回值,可以知道用户是否选择了文件、取消了对话框或者发生错误
5.2.将监听器添加到按钮上
先在ReaderPro下创建一个监听器对象
ProListener lis=new ProListener();
在lis添加到按钮上
btnopen.addActionListener(lis);
btn.addActionListener(lis);
6.运行结果
6.1.debug结果
在我们给定的路径下存储了一张提取的红色框中的二维码图片
这样就可以利用 generateStr()方法将二维码中的信息读取转换为字符串了
6.2.解码结果
解码结果
7.结语
通过以上代码我们将上面一张二维码图片的信息解密出来。
2024年就要到来,也希望大家能在新的一年里学业有成,达到新的高峰。