Halo, Habr!
Seseorang modern bertemu dengan barcode setiap hari, bahkan tanpa memikirkannya. Ketika kami membeli produk di supermarket, kode mereka dibaca tepat dengan bantuan barcode. Juga parsel, barang di gudang, dan sebagainya dan sebagainya. Namun, sedikit orang yang tahu cara kerjanya.
Bagaimana mengatur barcode, dan apa yang dikodekan dalam gambar ini?

Mari kita coba mencari tahu, pada saat yang sama kita akan menulis decoder dari kode-kode tersebut.
Pendahuluan
Penggunaan barcode memiliki sejarah panjang. Upaya otomatisasi pertama dimulai pada 1950-an, sebuah paten untuk pembaca kode diperoleh pada tahun 1952. Insinyur yang terlibat dalam pemilahan mobil di rel ingin menyederhanakan proses. Idenya jelas - untuk menyandikan nomor menggunakan strip dan membacanya menggunakan fotosel. Pada tahun 1962, kode mulai secara resmi digunakan untuk mengidentifikasi mobil-mobil di jalur kereta api Amerika (sistem
KarTrak ), pada tahun 1968 lampu sorot diganti dengan sinar laser, yang meningkatkan akurasi dan mengurangi ukuran pembaca. Pada tahun 1973, format "Kode Produk Universal" muncul, dan pada tahun 1974 produk pertama dijual menggunakan pemindai kode (permen karet Wrigley adalah Amerika Serikat;) di supermarket. Pada tahun 1984, sepertiga dari toko menggunakan pita, tetapi di Rusia mereka mulai digunakan sekitar tahun 90-an.
Cukup banyak kode yang berbeda digunakan untuk tugas yang berbeda, misalnya, urutan "12345678" dapat direpresentasikan dengan cara seperti itu (dan bukan itu saja):

Mari kita mulai parsing bitwise. Selanjutnya, semua yang dijelaskan di bawah ini akan merujuk pada formulir "Code-128" - hanya karena formatnya cukup sederhana dan mudah. Mereka yang ingin bereksperimen dengan spesies lain dapat membuka
generator online dan melihatnya sendiri.
Pada pandangan pertama, barcode tampaknya hanya urutan acak dari garis, pada kenyataannya, strukturnya jelas diperbaiki:

1 - Ruang kosong yang diperlukan untuk mengidentifikasi dengan jelas awal kode
2 - Mulai simbol. Untuk Code-128, 3 opsi dimungkinkan (disebut A, B dan C): 11010000100, 11010010000 atau 11010011100, mereka berhubungan dengan tabel kode yang berbeda (untuk lebih jelasnya, lihat
Wikipedia ).
3 - Sebenarnya kode berisi data yang kita butuhkan
4 - Checksum
5 - Stop simbol. Untuk Code-128, ini adalah 1100011101011.
6 (1) - Ruang kosong.
Sekarang tentang bagaimana bit dikodekan. Semuanya sangat sederhana di sini - jika Anda mengambil lebar garis tertipis sebagai "1", maka garis lebar ganda akan memberikan kode "11", triple "111", dan seterusnya. Ruang kosong akan menjadi "0" atau "00" atau "000" sesuai dengan prinsip yang sama. Mereka yang ingin dapat membandingkan kode awal dalam gambar untuk memastikan bahwa aturan diikuti.
Sekarang Anda dapat memulai pemrograman.
Dapatkan urutan bit
Pada prinsipnya, ini adalah bagian yang paling sulit, dan tentu saja, secara algoritmik, ini dapat diimplementasikan dengan berbagai cara. Saya tidak yakin algoritma di bawah ini optimal, tetapi cukup untuk studi kasus.
Untuk memulai, muat gambar, rentangkan lebarnya, ambil garis horizontal dari tengah gambar, ubah menjadi b / w dan muat sebagai array.
from PIL import Image import numpy as np import matplotlib.pyplot as plt image_path = "barcode.jpg" img = Image.open(image_path) width, height = img.size basewidth = 4*width img = img.resize((basewidth, height), Image.ANTIALIAS) hor_line_bw = img.crop((0, int(height/2), basewidth, int(height/2) + 1)).convert('L') hor_data = np.asarray(hor_line_bw, dtype="int32")[0]
Pada barcode, "1" sesuai dengan hitam, dan dalam RGB, sebaliknya, 0, sehingga array harus dibalik. Pada saat yang sama, kami menghitung nilai rata-rata.
hor_data = 255 - hor_data avg = np.average(hor_data) plt.plot(hor_data) plt.show()
Kami memulai program untuk memastikan bahwa barcode dimuat dengan benar:

Sekarang Anda perlu menentukan lebar satu "bit". Untuk melakukan ini, kami menyoroti awal urutan awal "1101", merekam momen-momen transisi grafik melalui garis tengah.
pos1, pos2 = -1, -1 bits = "" for p in range(basewidth - 2): if hor_data[p] < avg and hor_data[p + 1] > avg: bits += "1" if pos1 == -1: pos1 = p if bits == "101": pos2 = p break if hor_data[p] > avg and hor_data[p + 1] < avg: bits += "0" bit_width = int((pos2 - pos1)/3)
Kami hanya merekam transisi melalui tengah, sehingga kode "1101" akan ditulis sebagai "101", tetapi ini cukup bagi kami untuk mengetahui lebarnya dalam piksel.
Sekarang decoding yang sebenarnya. Kami menemukan transisi berikutnya melalui tengah, dan menentukan jumlah bit yang termasuk dalam interval. Karena kecocokannya tidak mutlak (kode mungkin sedikit melengkung atau melebar), kami menggunakan pembulatan.
bits = "" for p in range(basewidth - 2): if hor_data[p] > avg and hor_data[p + 1] < avg: interval = p - pos1 cnt = interval/bit_width bits += "1"*int(round(cnt)) pos1 = p if hor_data[p] < avg and hor_data[p + 1] > avg: interval = p - pos1 cnt = interval/bit_width bits += "0"*int(round(cnt)) pos1 = p
Saya tidak yakin apakah ini pilihan terbaik, mungkin ada cara yang lebih baik, mereka yang ingin dapat menulis di komentar.
Jika semuanya dilakukan dengan benar, maka kita mendapatkan urutan output berikut:
11010010000110001010001000110100010001101110100011011101000111011011
01100110011000101000101000110001000101100011000101110110011011001111
00010101100011101011
Decoding
Di sini, pada prinsipnya, tidak ada kesulitan. Karakter dalam
Kode-128 dikodekan dengan kode 11-bit, yang memiliki 3 varietas (A, B dan C) dan dapat menyimpan pengkodean karakter yang berbeda atau angka dari 00 hingga 99.
Dalam kasus kami, awal dari urutan adalah 11010010000, yang sesuai dengan "Kode B". Itu sangat rusak untuk secara manual mengemudi di semua kode dari Wikipedia, sehingga tabel hanya disalin dari browser dan penguraiannya juga dilakukan dengan Python (petunjuk: ini tidak diperlukan untuk produksi).
CODE128_CHART = """ 0 _ _ 00 32 S 11011001100 212222 1 ! ! 01 33 ! 11001101100 222122 2 " " 02 34 " 11001100110 222221 3 # # 03 35 # 10010011000 121223 ... 93 GS } 93 125 } 10100011110 111341 94 RS ~ 94 126 ~ 10001011110 131141 103 Start Start A 208 SCA 11010000100 211412 104 Start Start B 209 SCB 11010010000 211214 105 Start Start C 210 SCC 11010011100 211232 106 Stop Stop - - - 11000111010 233111""".split() SYMBOLS = [value for value in CODE128_CHART[6::8]] VALUESB = [value for value in CODE128_CHART[2::8]] CODE128B = dict(zip(SYMBOLS, VALUESB))
Sekarang hal yang paling sederhana tetap ada. Kami memecah urutan bit kami menjadi blok 11 karakter:
sym_len = 11 symbols = [bits[i:i+sym_len] for i in range(0, len(bits), sym_len)]
Akhirnya, kami membentuk garis dan menampilkannya di layar:
str_out = "" for sym in symbols: if CODE128A[sym] == 'Start': continue if CODE128A[sym] == 'Stop': break str_out += CODE128A[sym] print(" ", sym, CODE128A[sym]) print("Str:", str_out)
Saya tidak akan memberikan jawaban untuk apa yang dikodekan dalam tabel, biarkan itu menjadi pekerjaan rumah bagi pembaca (menggunakan program yang sudah jadi untuk smartphone akan dianggap curang :).
Kode juga tidak menerapkan verifikasi CRC, mereka yang ingin dapat melakukannya sendiri.
Tentu saja, algoritmanya tidak sempurna, dan ditulis dalam setengah jam. Untuk tujuan yang lebih profesional, ada perpustakaan yang sudah jadi, misalnya
pyzbar . Kode yang menggunakan perpustakaan seperti itu hanya akan mengambil 4 baris:
from pyzbar.pyzbar import decode img = Image.open(image_path) decode = decode(img) print(decode)
(pertama Anda perlu menginstal perpustakaan dengan memasukkan perintah pip install pyzbar)
Tambahan :
vinograd19 menulis dalam komentar tentang penghitungan CRC:
Sejarah digit periksa menarik. Itu muncul secara evolusioner.
Digit periksa diperlukan untuk menghindari penguraian kode yang salah. Jika barcode 1234, dan itu diakui sebagai 7234, maka Anda perlu validasi yang akan mencegah penggantian 1 oleh 7. Validasi mungkin tidak akurat sehingga setidaknya 90% dari angka yang tidak valid ditentukan terlebih dahulu.
Pendekatan 1: Mari kita ambil jumlahnya. Sehingga sisa pembagian dengan 10 adalah 0. Nah, yaitu, 12 karakter pertama membawa muatan informasi, dan digit terakhir dipilih sehingga jumlah digit dibagi dengan 10. Dekode urutannya, jika jumlahnya tidak dapat dibagi sepuluh, itu berarti diterjemahkan dengan bug dan perlu Anda lakukan ini sekali lagi. Misalnya, kode 1234 valid. 1 + 2 + 3 + 4 = 10. Kode 1216 juga valid, tetapi 1218 tidak.
Ini menghindari masalah dengan otomatisasi. Namun, pada saat membuat barcode, ada kemunduran dalam bentuk mengetikkan angka pada tombol. Dan ada kasus buruk: jika Anda mengubah urutan dua digit, checksum tidak berubah, dan ini buruk. Artinya, jika barcode 1234 dipalu sebagai 2134, checksum akan menyatu, tetapi kami memasukkan nomor yang salah. Ternyata urutan angka yang salah adalah kasus umum jika Anda mengetuk tombol dengan cepat.
Pendekatan 2. Baiklah, mari kita buat jumlahnya sedikit lebih rumit. Sehingga angka di tempat genap dihitung dua kali. Kemudian, ketika mengubah urutan, jumlah pasti tidak akan menyatu dengan yang diinginkan. Misalnya, kode 2364 valid (2 + 3 + 3 + 6 + 4 + 4 = 20), dan kode 3264 tidak valid (3+ 2 + 2 + 6 + 4 + 4 = 19). Tapi di sini ada contoh mengemudi yang buruk. Beberapa keyboard sedemikian rupa sehingga sepuluh digit disusun dalam dua baris. baris pertama adalah 12345 dan di bawahnya baris kedua kedua adalah 67890. Jika, alih-alih tombol “1”, tekan tombol “2” di sebelah kanan, checksum akan mencegah entri yang salah. Tetapi jika alih-alih tombol “1”, tekan tombol “6” di bawah, maka itu mungkin tidak memperingatkan. Bagaimanapun, 6 = 1 + 5, dan dalam kasus ketika angka ini berada di tempat genap ketika menghitung checksum, kita memiliki 2 * 6 = 2 * 1 + 2 * 5. Artinya, checksum meningkat tepat 10, sehingga digit terakhirnya tidak berubah. Misalnya, checksum dalam kode 2134 dan 2634 adalah sama. Kesalahan yang sama akan terjadi jika kita menekan 7 bukannya 2, bukannya 3 tekan 8, dan seterusnya.
Pendekatan ke-3. Ok, mari kita ambil penjumlahan lagi, hanya angka di tempat genap yang akan diperhitungkan ... tiga kali. Artinya, kode 1234565 valid, karena 1 + 2 * 3 + 3 + 4 * 3 + 5 + 6 * 3 +5 = 50.
Metode yang dijelaskan telah menjadi standar untuk menghitung checksum EAN13 dengan beberapa koreksi: jumlah digit telah ditetapkan dan sama dengan 13, di mana yang ke-13 adalah checksum yang sama. Angka-angka di tempat ganjil dihitung tiga kali, pada yang genap - sekali.Kesimpulan
Seperti yang Anda lihat, bahkan hal yang sederhana seperti barcode memiliki banyak hal menarik di dalamnya. By the way, hack kehidupan lain bagi mereka yang telah membaca sampai di sini - teks di bawah barcode (jika ada) sepenuhnya menggandakan kontennya. Hal ini dilakukan agar dalam kasus kode yang tidak dapat dibaca, operator dapat memasukkannya secara manual. Jadi untuk mengetahui isi barcode biasanya sederhana - lihat saja teks di bawahnya.
Seperti yang disarankan dalam komentar, yang paling populer dalam perdagangan adalah kode EAN-13, bit coding sama di sana, dan mereka yang ingin dapat
melihat struktur karakter
sendiri .
Jika pembaca tidak kehilangan minat, Anda dapat mempertimbangkan kode QR secara terpisah.
Terima kasih atas perhatian anda