【Java编程】【计算机视觉】一种简单的图片加/解密算法
by Li y.c.
一、内容简介
本文介绍一种简单的图片加/解密算法,算法的基本原理十分简单,即逐个(逐行、逐列)地获取图片的像素点颜色值,对其进行一些简单的算数运算操作进行加密,解密过程则相应地为加密运算的逆运算。
二、图片表示基础
在计算机中,图片(我们使用.jpg文件作为示例)在内存中是使用RGB数组进行存储,举例:
以上图片中,每一个像素点用一个rgb值表示,一般为6位16进制数,例如0x123456,这里0x12表示红度(RED)值,0x34表示绿度(GREEN)值,0x56表示蓝度(BLUE)值。注意到,两位16进制数的取值范围为0~255(0x00到0xFF)。
三、R/G/B值的获取
假如我们知道当前像素点的rgb值为0x123456,如何从中获取到分离的r/g/b值呢?通过简单的位运算即可。如以下java代码:
public class RGBTest {
public static void main(String[] args) {
String format1 = String.format("%x", (0x123456 >> 16)); // 获取red值(前两位)
String format2 = String.format("%x", (0x123456 >> 8) & 0xFF); // 获取green值(中间两位)
String format3 = String.format("%x", (0x123456) & 0xFF); // 获取blue值(末两位)
String format = String.format("%x", (0x123456) & 0xFFFFFF); // 不做改变
System.out.println(format1);
System.out.println(format2);
System.out.println(format3);
System.out.println(format);
}
}
运行结果正是我们想要的:
四、获取整张图片的RGB数组
有了以上的基础,我们就可以将整张图片存储为一个RGB表示的数组,为之后的加/解密操作做基础。使用如下的Java代码:
public List<Integer> getRGB(BufferedImage image, int x, int y) {
List<Integer> rgb = new ArrayList<Integer>();
if (image != null && x <= image.getWidth() && y <= image.getHeight()) {
for (int h = 0; h < y; h++) {
for (int w = 0; w < x; w++) {
//获得w,h坐标的颜色
int pixel = image.getRGB(w, h);
rgb.add(pixel);
}
}
}
return rgb;
}
五、加密函数
我们使用简单的加密操作,即把图片的R/G/B值同时加50后模256(防止值溢出造成噪点【可以想见,若某一通道出现进位,后果是灾难性的】):
// 加密函数
public static void Encrypt(Integer[] rgb, int width, int height) {
// 输出文件名
File file = new File("image_out.jpg");
BufferedImage bi = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = (Graphics2D) bi.getGraphics();
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
int color = rgb[w + width * h];
int red = color >> 16;
int green = (color >> 8) & 0xFF;
int blue = color & 0xFF;
int red_ = (red + 50) % 256;
int green_ = (green + 50) % 256;
int blue_ = (blue + 50) % 256;
Color c = new Color(red_ << 16 | green_ << 8 | blue_);
g2.setColor(c);
g2.drawLine(w, h, w + 1, h + 1);
}
}
try {
ImageIO.write(bi, "jpg", file);
} catch (IOException e) {
e.printStackTrace();
}
}
看看加密效果:(以下代码位于主函数中)
// 加密部分:【image_1_1.jpg】---【image_out.jpg】
Image_Encrypt r = new Image_Encrypt();
File f = new File("image_1_1.jpg");
BufferedImage bi = ImageIO.read(f);
int width = bi.getWidth();
int height = bi.getHeight();
List<Integer> l = null;
Integer[] rbg = new Integer[width * height];
l = r.getRGB(bi, width, height);
// 查看rgb值:
// System.out.print(l);
for (int i = 0; i < width * height; i++) {
rbg[i] = l.get(i);
}
Encrypt(rbg, width, height);
加密后的图像:
六、解密函数
解密函数应为加密函数的逆运算,考虑到边缘条件,代码如下:
// 解密函数
public static void Decrypt(Integer[] rgb, int width, int height) {
// 输出文件名
File file = new File("image_out_de.jpg");
BufferedImage bi = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = (Graphics2D) bi.getGraphics();
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
int color = rgb[w + width * h];
int red = color >> 16;
int green = (color >> 8) & 0xFF;
int blue = color & 0xFF;
int red_;
if (red == 255){
red_ = 255;
}
else{
red_ = (red - 50 + 255) % 255;
}
int green_;
if (green == 255){
green_ = 255;
}
else{
green_ = (green - 50 + 255) % 255;
}
int blue_;
if (blue == 255){
blue_ = 255;
}
else{
blue_ = (blue - 50 + 255) % 255;
}
Color c = new Color(red_ << 16 | green_ << 8 | blue_);
g2.setColor(c);
g2.drawLine(w, h, w + 1, h + 1);
}
}
try {
ImageIO.write(bi, "jpg", file);
} catch (IOException e) {
e.printStackTrace();
}
}
解密后的图像却是这样的:
推测是由于边缘条件造成的,修改代码中……
【debug】
我们写一个测试类,看看从0到255的值里面,是否有不满足该逆运算关系的:
public class Test {
public static void main(String[] args){
for (int i = 0;i <= 255;i ++){
int j = (i + 50) % 256;
int j_ = (j - 50 + 256) % 256;
if (i != j_){
System.out.print("errr");
}
}
}
}
输出如下:
【没有报错】……??
基准测试
加/解密均不做任何操作,直接由三通道的值拼凑出rgb数组:
加密函数
// 加密函数
public static void Encrypt(Integer[] rgb, int width, int height) {
// 输出文件名
File file = new File("image_out.jpg");
BufferedImage bi = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = (Graphics2D) bi.getGraphics();
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
int color = rgb[w + width * h];
int red = color >> 16;
int green = (color >> 8) & 0xFF;
int blue = color & 0xFF;
// 基准测试
int red_ = red;
int green_ = green;
int blue_ = blue;
// int red_ = (red + 50) % 256;
// int green_ = (green + 50) % 256;
// int blue_ = (blue + 50) % 256;
Color c = new Color(red_ << 16 | green_ << 8 | blue_);
g2.setColor(c);
g2.drawLine(w, h, w + 1, h + 1);
}
}
try {
ImageIO.write(bi, "jpg", file);
} catch (IOException e) {
e.printStackTrace();
}
}
解密函数
// 解密函数
public static void Decrypt(Integer[] rgb, int width, int height) {
// 输出文件名
File file = new File("image_out_de.jpg");
BufferedImage bi = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = (Graphics2D) bi.getGraphics();
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
int color = rgb[w + width * h];
int red = color >> 16;
int green = (color >> 8) & 0xFF;
int blue = color & 0xFF;
// 基准测试
int red_ = red;
int green_ = green;
int blue_ = blue;
// int red_;
// if (red == 255){
// red_ = 255;
// }
// else{
// red_ = (red - 50 + 256) % 256;
// }
// int green_;
// if (green == 255){
// green_ = 255;
// }
// else{
// green_ = (green - 50 + 256) % 256;
// }
// int blue_;
// if (blue == 255){
// blue_ = 255;
// }
// else{
// blue_ = (blue - 50 + 256) % 256;
// }
Color c = new Color(red_ << 16 | green_ << 8 | blue_);
g2.setColor(c);
g2.drawLine(w, h, w + 1, h + 1);
}
}
try {
ImageIO.write(bi, "jpg", file);
} catch (IOException e) {
e.printStackTrace();
}
}