# QR Code 與條碼識讀｜從圖片或鏡頭掃 QR Code

把鏡頭對準碼，或者把拍下來的圖丟進來。讀這件事只發生在這裡，別處都沒有。

> 從照片、截圖或鏡頭讀取 QR Code，在開啟之前就看清這個連結到底通向哪裡。EAN、UPC、Code 128、Code 39 和 ITF 條碼也能讀。什麼都不上傳。

本頁是一個完全在你的瀏覽器裡執行的互動式工具，地址是 https://abox.tools/zh-TW/qr-barcode-reader/。你交給它的任何內容都不會被上傳。下面是這個頁面用文字對該工具的全部說明；要使用它，請開啟這個地址。

## 你的圖片和圖片裡的碼**不會上傳**。這裡根本沒有伺服器。

讀一個碼是對畫素做算術，而畫素本來就在這裡。找到符號、把拍攝角度校正回來、把掩碼解掉、用裡德-所羅門把損壞的地方補上、再把位元讀回來，這些全都發生在這一頁裡兩千來行你能讀的 JavaScript 中。**鏡頭是同一個承諾，不是它的例外：**一幀畫面以畫素的形式進到這個分頁，被看一眼，然後就沒了。什麼都不錄，什麼都不留，而且這一頁壓根就沒有任何能把東西送出去的網路功能。

- ✗ 不上傳
- ✗ 不用帳號
- ✗ 不做記錄
- ✓ 斷網可用
- ✓ 開源

## 怎樣不上傳圖片就讀出 QR Code

1. **把圖給它。** 把照片或截圖拖到那個框上，或者直接貼上一張，或者按鏡頭那個按鈕。一次好幾張也行，每張各讀各的，各有各的答案。已經在你螢幕上的碼，截個圖是最快也最可靠的方法，因為那裡既沒有鏡頭，也沒有角度，更沒有光。
2. **讓整個符號連同留白一起進畫面。** 碼周圍那圈白是碼的一部分：識讀器就是靠它知道符號到哪裡為止的。裁到方塊邊緣的照片，是一個碼讀不出來的最常見原因，沒有之一。讓碼佔到畫面一半左右差不多正好；再靠近，四個角就跑到畫面外面去了。
3. **在決定任何事情之前，先把地址讀一遍。** 去掃海報、停車繳費機或者信件上的碼，圖的就是弄清楚它通向哪裡；而這恰恰是手機鏡頭真正不讓你做的那一件事。這裡主機單獨佔一行。要不是你預想中的那個名字，你要的東西其實已經到手了，也就沒什麼好開啟的了。
4. **把提示當回事，尤其是那些不響的。** 光禿禿的 `http://` 地址、用看起來不是那套文字的字母寫的名字、短連結服務，或者地址裡 `@` 前面的任何東西——每一樣都會在它出現的地方被點名。它們單拿出來誰也證明不了什麼，但在你過去之前，每一樣都值十秒鐘。
5. **讀不出來，先改光，別先改別的。** 幾乎所有失敗都是閾值的問題：橫在中間的反光、壓在一角的陰影，或者以某個恰好把背光收進來的角度拍下的螢幕。挪個位置讓反光別落在碼上，或者把鏡頭的燈開啟。還不行就正對著拍一張平的照片，把那張丟進來——一張靜止的圖會得到比即時畫面細緻得多的搜尋。
6. **答案看起來不對勁，就看看取樣出來的那張圖。** 把「這一個是怎麼讀出來的」展開，看那個小格子。它是這一頁認為這個碼長什麼樣，從採到的模組重新畫回去的結果。一個把符號讀錯、又修補成了某種像模像樣東西的識讀器，只會在那裡露出馬腳，別處都不會。

## 說得更細的版本

[怎麼做一個 QR Code，並證明它掃得出來](https://abox.tools/zh-TW/guides/make-a-qr-code-and-prove-it-scans/): 產生 QR Code，再用同一個網站的識別器驗證：一字不差的內容、真正的連結，按印刷尺寸、用一張照片，在批次印刷之前。全在瀏覽器裡，什麼都不上傳。

## 工具箱裡還有

- [雜湊與校驗和](https://abox.tools/zh-TW/hash-checksum/): 把下載來的檔案和釋出方公佈的那串數字對一下，不用把它發給任何人。
- [密碼與密碼短語產生器](https://abox.tools/zh-TW/password-generator/): 在這裡產生，由你自己的瀏覽器產生，不發往任何地方。什麼都不儲存，也沒有歷史記錄。
- [JSON 格式化](https://abox.tools/zh-TW/json-formatter/): JSON、XML、HTML、CSS 和 YAML，格式化或轉換。沒有一個字被貼進別人的伺服器。
- [YAML 轉 JSON](https://abox.tools/zh-TW/yaml-to-json/): 兩個方向都有，而且會告訴你各自的代價。這些都不會貼進別人的伺服器。

## 常見問題

### 圖片會被上傳到什麼地方去嗎？

不會，從圖裡讀出來的東西也不會。圖片在這一頁的畫布上解碼，然後就在那裡被讀，用的是這個網站提供的 JavaScript。這個工具壓根沒有任何網路功能——它從不去取東西，也從不發東西——而這一頁的 `Content-Security-Policy` 把它可以聯絡的地址一個不落地寫了出來，其中沒有一個是我們的。最簡單的證明就是斷網：它照樣能用。

### 鏡頭會錄下什麼嗎？

不會。鏡頭的一幀以畫素的形式進到這個分頁，畫到畫布上，被看一眼，大約十分之一秒後就被下一幀蓋掉。沒有東西寫到硬碟上，也沒有東西留下來。你一按停止，流就斷了；分頁退到後臺時會斷，你離開這一頁時也會斷。而值得信的指示是你鏡頭上的那盞燈，因為沒有哪一個網頁能把它關掉。

### 為什麼它只把連結給我看，不直接開啟？

因為有用的正是這一步。QR Code 是一個你讀不出來的地址，貼在停車繳費機碼上的那張貼紙之所以管用，靠的就是這一點：等你知道它去了哪裡，你已經在那裡了。這裡整串字元原樣印出來，主機被拎出來單獨佔一行，開啟是另一個按鈕，讀完之後你自己按。這是多一次點選，而這一次點選正是這個格式一直都缺的。

### 它能讀什麼？

QR Code 從版本 1 到 40 全都能讀，四個糾錯等級都能讀，數字、字母數字、位元組和漢字模式都能讀，ECI 字符集和屬於一組連拼符號的碼會被報出來，而不是悄悄扔掉。條紋那一邊：EAN-13、EAN-8、UPC-A、UPC-E、ITF-14、交叉二五碼、Code 128 和 Code 39。Data Matrix、PDF417、Aztec 和 MaxiCode 它不讀。

### 我的碼讀不出來，是哪裡不對？

十次裡有九次是三件事裡的一件。白色留白被裁掉了，而識讀器要靠它知道符號到哪裡為止。碼有一塊被反光或陰影蓋住了，於是沒有哪個閾值能把深色方塊和淺色方塊分開。或者碼在畫面裡太小，模組只有一兩個畫素寬。換個光，讓碼佔到畫面一半左右，正對著拍而不是斜著拍。

### 破損的或被擋住一部分的碼，它能讀嗎？

多半能，而這是這個格式按設計在工作，不是我們這邊有什麼本事。每個 QR Code 都帶著裡德-所羅門校驗資料，用 H 等級做出來的符號丟掉大約 30% 的模組，仍然能被原樣重建。這一頁會在「這一個是怎麼讀出來的」裡說明它修了多少個碼字，你能看出當時有多懸。它不做的事情是猜：損壞程度超過校驗能扛住的符號，會被報成讀不出來，而不是給你一個錯答案。

### 為什麼它說不出一個 bit.ly 連結通向哪裡？

因為要弄清楚就得去問 bit.ly，而那是一次網路請求。這一頁別的所有說法都立在「這裡沒有任何程式碼會去聯絡任何東西」上面，為這件事偷偷開個口子，比這個答案本身虧得多。所以短連結服務的名字會被點出來，它藏起來的東西則老老實實地留作未知。你真想解開，就把它貼到一個願意去問的地方。

### 掃 QR Code 這件事本身安全嗎？

掃是安全的。照著它做才是風險，而且是真實的風險：貼在真碼上面的假碼，在停車繳費機上、在餐館桌上、在快遞取件單上，已經常見到有了專門的名字。它們之所以管用，是因為沒人能靠看把一個碼讀出來。讀它但不開啟它——也就是這一頁在做的事——把那點便宜整個拿掉了，而看一眼主機所花的十秒鐘就是全部的防守。

### 每個結果下面那個小格子是什麼？

是這一頁真正從你的圖裡採到的那些模組，按一個模組一格重新畫出來的。它放在那裡，是為了讓讀出來的結果可以用眼睛核對，而不是只能相信：要是那個格子看起來就是你拍的那個碼，上面的答案就來自正確的畫素。幾乎沒有識讀器給你看這個，而這正是「能查的工具」和「只能信的工具」之間的差別。

### 一張圖裡有好幾個碼，它都能讀嗎？

眼下一張圖一個。一次拖進來好幾張，每張各讀各的；開著鏡頭慢慢移，它會一個接一個地讀，把每個還沒見過的新碼留下來。一張照片裡拍下一整頁碼，那是該去裁圖，或者把鏡頭一個一個對過去的場合。

### 為什麼它給我的條碼起了個跟我要的不一樣的名字？

因為條碼不帶著自己的名字走。UPC-A 就是首位為零的 EAN-13，ITF-14 就是十四位、校驗位對得上的交叉二五碼，而 Code 128 在數字模式下不像別的任何東西。這一頁報的是那些條紋說了什麼，加上校驗位確認了什麼——符號自己知道的也就這麼多。

### 斷網能用嗎？

能。把頁面載入一次，然後斷網，它照樣能用，鏡頭也一樣。這同時也是證明什麼都沒被上傳的最簡單辦法：一個要把你的圖送出去解碼的識讀器，在你拔線的那一刻就停了。

## 這條隱私承諾怎麼核實

- **圖片永遠不會被上傳，鏡頭也不例外。** 你丟進來的照片會在這一頁的畫布上解碼，然後就在那裡被讀。鏡頭的一幀是同一件事，只是每秒發生三十次：畫到那塊畫布上，被看一眼，再被下一幀蓋掉。沒有哪一幀被錄下來，也沒有哪一幀留下來；你按下停止時鏡頭的燈滅掉，整件事就到此為止。
- **這裡沒有任何東西去取任何東西。** `src/` 裡沒有 `fetch`，沒有 `XMLHttpRequest`，也沒有 `sendBeacon`；而這一頁的 `Content-Security-Policy` 連一個這個源可以傳送的地址都沒留下，就算真有那樣的程式碼也發不出去。這也正是這一頁沒法告訴你一個短連結最後通向哪裡的原因：要弄清楚就得去問，而它不問。
- **它把地址給你看。它從不替你開啟。** 印出來的 QR Code 是一個誰也讀不出來的地址，而這正是在它上面貼一張貼紙對某些人划得來的原因。這裡什麼都不開啟。整串字元原樣印出來給你看，你真正會落到的主機被單獨拎出來放一行，那些讓一個地址看起來像另一個地址的花招也在它們出現的地方被點名：`@` 前面的使用者名稱、用形狀和我們的字母一模一樣的另一套文字寫的名字、還有轉跳。
- **它連採到的樣子也一併給你看。** 每個 QR Code 結果下面都有一張圖，是這一頁真正從你照片裡讀到的那些模組。如果它看起來就是你掃的那個碼，上面的答案就站得住；如果它看起來像雪花點，那就站不住。一個只把一串字元丟給你就完事的識讀器，沒法這樣查。
- **谷歌載入了什麼，又拿不到什麼。** 廣告和統計指令碼來自谷歌，捐助按鈕來自 Buy Me a Coffee。它們誰也拿不到一張圖片、一幀畫面，或者從裡面讀出來的任何東西。把畫素變成字元的每一行程式碼都由這個源自己提供，也都在儲存庫裡擺著。
- **它斷網也能用。** 把網斷掉，這個工具一點不變，鏡頭也一樣，因為它裡面本來就沒有過一步走網路。這是所有證明裡最簡單的一個。

**你可以自己核對。**上面這些不用你信。這個頁面是儲存庫裡的模板和配置產生出來的，構建指令碼你能讀，也能自己跑一遍，跑出來的結果就提交在 `dist` 分支上。拿實際發給你的內容，跟你自己從原始碼構建出來的結果對一遍就知道了： https://github.com/A-Box-of-Tools/website

最值得先讀的幾個檔案：`config/site.toml` 裡是 Content-Security-Policy，其中 `src/binarize.js` 和 `src/detect.js` 負責在照片裡找到符號，也就是閾值、定點陣圖形和透視變換；`src/qr-decode.js` 負責把它讀回來；`src/reed-solomon.js` 負責修補讀錯的地方；`src/linear.js` 負責那些帶條紋的；而 `src/camera.js` 就是這一頁裡所有碰到鏡頭的行。
