其实对于目前的形式来说,虽然像 U 盘、固态硬盘、甚至光盘这些信息储存介质(设备)的容量越来越高,但是不得不说这些设备的可靠性依然像悬着的一块石头,虽然这块石头确实牢牢的粘在天花板上,但是毕竟是粘上去的,总有可能会突然掉下来。
随着现在各种数码信息生成应用的广泛普及,我们每天都会产生大量的数据,以至于我们时常忽视了信息备份的必要性。现在手机内部存储 200 多 GB ,但是依然很快就能被占满,比电脑还要容易占满,很难想象在未来,如果这些海量的数据因为设备的不可靠性而丢失,那将是一个多么可惜的事情?
不是任何数据都值得备份,相比于备份所有数据,备份数据之中的重点等则是很重要的。可能就是其中自己喜欢的照片,然而将它放在手机中,甚至云盘上都不能称之为可靠,手机会坏,比如我之前使用的手机 huawei nove 2s,在我换了新手机后,因为同学的手机被老师没收了我便借给他,但是因为他玩心很大,嘻嘻哈哈中将衣服和我的手机同时甩入天空,然后手机粉身碎骨,其内部数据虽然部分有备份,但终究是损失了很多,尤其是大量的珍贵照片。云盘也不可靠,在七八年前,那时设备很简单,家里有废旧手机若干、几个 GB 的简单内存卡数张、还有一个慢如黄牛的电脑一台,它们带着我走过了很多年,储存了我很多的数据,当然最重要的是那些珍贵的照片。于是我就全存储到了 360 云盘上,因为存储介质在当时及其昂贵,一个蔫蔫数 GB 的内存卡就是好几天的饭钱,所以我几乎没有其他的备份,电脑的硬盘也好像很容易坏,总之我的数据没有备份,但是我未曾想到的是,360 云盘居然嗝屁了。。。然后那些过去的回忆,全都毁于一旦。虽然那时有抢救下载服务,但是终归错过了。
所以我很想找到一个比较可靠的储存方式。我看到了 A4 纸,家里有打印机,也有一个坏掉的喷墨打印机可作为一个扫描仪。于是就想使用打印的方式使用一定方式将二进制数据打印到纸上,进而实现数据的备份,毕竟肉眼可看到数据的细节,心里会很安心。去年末我曾经找到一个叫 PaperBack 的软件来使用该方式打印到纸上,然后使用扫描仪就能还原数据,但是因为需要打印机与扫描仪共同配合使用,所以始终没有完整尝试过。于是想自制一个。
最开始我想到了 vscode 的 hex editor 插件,可以编辑二进制文件,但是由于自己缺乏相关知识,所以无法从得知二进制数据然后制作二进制文件,然后我想到了基于二进制的 base64 ,然后就自制了两个测试网页。
复制图片就能转为 base64 源码:https://www.ccgxk.com/123.html
base64 与文件互转工具:https://www.ccgxk.com/124.html
因为 base64 不仅可以将图片转换为 ASCII 码,而且也能将任何文件转化,并且还能反向进行还原成文件下载。所以问题就简化成在纸上以适当方式转化为一串长长的 ASCII 码。
然而如何能识别纸上的呢?我想到了 github 的南极源代码那个计划,他们使用的 QR 码刻在胶卷上来记录信息,我或许也可以如此?但是在网上找了很久也没找到他们行动的细节,他们报道里的 QR 码是指的日本那个传统上的 QR 码还是通指的 QR 码呢?我不知道。不过在我的测试下,发现 QR 码是为方便扫描而生的,对于简单的数据会产生很巨大的图像,因此并不适用于我的计划。虽然还做了个页面吧。能生成复杂程度级别最低的 QR 码,https://www.ccgxk.com/125.html ,如果用 QR 码来记录信息,那数据量将难以想象。
于是又尝试自己研究识别图像的方式,我知道有个 openvc 库,专门专注于图片领域,但是吧,不会,自己就研究吧,就弄了个,做了一半才知道这个还是有点难度的。
源码在此处,感兴趣的小伙伴可以自己尝试一番
代码语言:javascript复制<style>
* {
box-sizing: border-box
}
table {
border: red 1px solid;
}
td {
padding: 0;
border: 0;
width: 3px;
height: 3px;
}
.the_black{
background: black;
}
</style>
<body>
<p class="image">
<img src="./img08.png" width="100px" id="img" />
</p>
<input type="number" value="230" id="blackThreshold" />
<button onclick="drawTableImg()">点击转换</button>
<canvas id="myCanvas" width="250" style="display: none;"></canvas>
<div id="tableDiv"></div>
<body>
<script>
const dom = document.getElementById("myCanvas"); // canvas画布
let colorData = new Array;
let canvasWidth = dom.width; // 获取 canvas 元素上的宽度,以在转 2 维数组时作为分行依据
let imgDom = document.getElementById("img");
function drawTableImg(){
let threshold = document.getElementById("blackThreshold").value; // 获取黑色阈值(0~255)
getImageData(dom, imgDom.src).then((data)=>{
let colorData = data;
let colorData2d = dataTo2d(colorData, canvasWidth, threshold); // 一维转单色、二维
// 绘制
outTable(colorData2d);
})
}
/**
* 获取图片源像素信息
*/
function getImageData(dom, url){
const ctx = dom.getContext("2d"); // 设置在画布上绘图的环境
const image = new Image();
image.src = url;
let imgH = document.getElementById("img").height;
dom.height = dom.width * (imgH/100);
//获取画布宽高
const w = dom.width;
const h = dom.height ;
return new Promise((resolve)=>{
image.onload = function(){
ctx.drawImage(image, 0, 0 ,w,h); // 将图片绘制到画布上
const imgData = ctx.getImageData(0,0,w,h); // 获取画布上的图像像素
resolve(imgData.data) // 获取到的数据为一维数组,包含图像的 RGBA四个通道数据
ctx.clearRect(0,0,w,h);
}
})
}
/**
* 把颜色数组改成一维数组
*/
function getColor(array, threshold){
let result = new Array();
let nCutTimes = array.length / 4;
for (let index = 0; index < nCutTimes; index ) {
let key = array[index * 4];
result[index] = (key > threshold) ? 0 : 1;
}
return result;
}
/**
* 改成二维数组(图片平铺)
* @param array 数组(颜色数据)
* @param width 图宽(宽度像素值)
*/
function to2dArray(array, width){
let arrLen = array.length;
let result = new Array();
result[0] = new Array(); // 初始化第一行
for (let index = 0,key = 0,line = 0; index < arrLen; index ) {
out("index" index " key" key " line" line);
result[line][key] = array[index];
if(key === (width - 1)){ // 如果到每一行的最后一个元素了,就另起一行(申请新数组,行号变量加一,key 清零)
line ;
if(width * (line 1) > arrLen) break; // 如果新的一行大于图像的高度,则退出循环
result[line] = new Array();
key = 0;
}else{
key
}
}
return result;
}
/**
* 渲染输出成黑白表格
*/
function outTable(array2d){
out(array2d);
let outTableData = `<table>`;
for(let i = 0 ; i < array2d.length; i ){
outTableData = `</tr>`;
for(let j = 0 ; j < array2d[i].length; j ){
if(array2d[i][j] == 1){
outTableData = `<td class="the_black"></td>`;
}else{
outTableData = `<td></td>`;
}
}
outTableData = `</tr>`;
}
outTableData = `</table>`; // 渲染完毕
document.getElementById("tableDiv").innerHTML = outTableData; // 输出
}
/**
* 将图片源代码数据转化为二维数组
*/
function dataTo2d(array, width, threshold){
array = getColor(array, threshold);
array = to2dArray(array, width);
return array;
}
最终因为时间不足,暂时放弃了。