5.29. ברקודים וקודי Data Matrix

שתי משפחות קוד נוספות משלימות את מפענחי המצלמה. ברקודים חד-ממדיים – הפסים בצד של קופסת דגני בוקר, צמיד בית חולים, תווית משלוח – הם הסמלים הקריאים-במכונה הוותיקים ביותר שעדיין בשימוש יומיומי. קודי Data Matrix הם דו-ממדיים כמו קודי QR, אך צפופים יותר באותו גודל מטען וכוונו לסימון תעשייתי – סימן היצרן החרוט בלייזר על לוח מעגלים ולא הכרזה על קיר. מודול image כולל מפענח ייעודי לכל אחד, המכסה את היישומים התעשייתיים, הקמעונאיים והמלאיים שקודי ה-2D הצרכניים מעולם לא הגיעו אליהם ממש.

5.29.1. ברקודים 1D

ברקוד חד-ממדי מקודד את המטען שלו כרצף של פסים אנכיים ברוחבים משתנים, הנקראים משמאל-לימין (או מלמעלה-למטה עבור קודים בעלי כיוון אנכי). הרוחבים מתכמתים לאחד מקבוצה קטנה של ערכים, ורצף הרוחבים מאיית תווים בכל סמליות שהמדפסת בחרה: מספרי עבור קוד מוצר UPC, אלפאנומרי עבור מספר חלק במחסן, או טקסט שרירותי עבור תווית Code 128.

find_barcodes() סורק את הפריים אחר ברקודים 1D בכל אחת מהסמליות הנתמכות ומחזיר רשימה של אובייקטי תוצאה BarCode:

codes = img.find_barcodes()

for c in codes:
    img.draw_rectangle(c.rect, color=(0, 255, 0))
    print(c.payload, c.type, c.quality)

המפענח סורק הן אופקית והן אנכית על פני הפריים בקריאה אחת, כך שברקוד מודפס בכל זווית נמצא במעבר אחד מבלי שהאפליקציה תצטרך לסובב את הקלט. roi מגביל את החיפוש; אין פרמטרי כוונון אחרים – המפענח עצמאי.

הסמליות הנתמכות מכסות את המשפחות הצרכניות והתעשייתיות הנפוצות. הקבוצה הקמעונאית היא image.EAN2, image.EAN5, image.EAN8, image.UPCE, image.UPCA, image.EAN13 (הקודים המספריים באורך קבוע על רוב האריזות הצרכניות), image.ISBN10, ו-image.ISBN13 (אותן משפחות שנועדו מחדש עבור ספרים). הקבוצה הכללית היא image.I25 (Interleaved 2 of 5, נפוץ בתוויות משלוח), image.CODABAR (בשימוש בספריות ובבנקי דם), image.CODE39, image.CODE93, ו-image.CODE128 (סמליות אלפאנומריות באורך משתנה לטקסט שרירותי). משפחת קצה-המדף image.DATABAR (RSS-14) ו-image.DATABAR_EXP (RSS-Expanded) משלימות את הרשימה.

כל זיהוי נושא את אוצר המילים של התיבה התוחמת – x, y, w, h, rect, corners – ואת ה-payload המפוענח כמחרוזת. type הוא קבוע הסמליות מהרשימה לעיל, שאפליקציה בודקת כאשר אכפת לה ספציפית איזו משפחה פוענחה (למשל קבלת EAN13 בלבד עבור אפליקציית סורק-מכולת).

שני השדות שחשובים לסינון הם rotation ו-quality. rotation הוא זווית הברקוד במישור-התמונה ברדיאנים: המפענח מתמודד עם סיבובים שרירותיים, אך קוד במורד הזרם שרוצה להציג את הזיהוי בצורה נקייה עשוי לרצות לסנן החוצה קודים שחוזרים נטויים מעבר לסף כלשהו.

quality הוא ספירת הפענוח: מספר קווי הסריקה שפענחו בהצלחה את אותו מטען. המפענח רץ על פני כל שורה (ועמודה) של הפריים שחותכת את הברקוד, ומגדיל את המונה בכל פעם שהפענוח מצליח. ברקוד מודפס בפוקוס חד ובתאורה טובה מניב quality בעשרות; ברקוד מוסתר חלקית או מרוח עשוי לפענח על קו סריקה אחד או שניים בלבד ולדווח quality של 1 – 2. סינון החוצה של זיהויים מתחת ל-quality > 5 משליך פענוחים שגויים חולפים של קו-סריקה-בודד ללא עלות לזיהויים האמיתיים.

אפליקציית ברקוד 1D היא קטנה. לכוד פריים, קרא ל-find_barcodes(), עבור על הרשימה המוחזרת, סנן לפי c.type ו-c.quality, והעבר את c.payload דרך UART או USB לכל שלב במורד הזרם שמתעד או רושם את הסריקה.

5.29.2. Data Matrix

קוד Data Matrix הוא סמל 2D שמקודד את המטען שלו כרשת של תאים שחורים ולבנים, באופן שבו קוד QR עושה. הוא נבדל מקוד QR בשני היבטים מעשיים: הוא קטן יותר באותו גודל מטען (הקידוד צפוף יותר) והוא מכוון לשימוש תעשייתי ולא לשימוש צרכני (שבו קודי QR שולטים). הדפוסים החרוטים בלייזר בחלקי מתכת ברצפת מפעל, התוויות המודפסות על אריזות מעגלים משולבים, הסימנים המונחים על מזרקים רפואיים – כל אלו הם בדרך כלל Data Matrices, ולא קודי QR.

find_datamatrices() סורק את הפריים אחר קודי Data Matrix ומחזיר רשימה של אובייקטי תוצאה DataMatrix:

codes = img.find_datamatrices()

for c in codes:
    img.draw_rectangle(c.rect, color=(0, 255, 0))
    print(c.payload, c.rows, c.columns)

roi מגביל את החיפוש בדרך הרגילה. כפתור הכוונון היחיד הספציפי-למפענח הוא effort, מספר שלם השולט בכמה קשה המפענח עובד כדי למצוא התאמה. ערכים גבוהים יותר משפרים זיהוי של קודים חיוורים, פגומים או אלכסוניים במחיר של קצב הפריימים; ערכים נמוכים יותר רצים מהר יותר אך עשויים לפספס קודים שהמאמץ הגבוה יותר היה מוצא. ערכים מתחת ל-160 בערך נכשלים בפועל בזיהוי; ערכים מעל 240 בערך נותנים תשואה פוחתת. ברירת המחדל של 200 היא איזון סביר עבור תמונה ברורה, ונקודת ההתחלה הנכונה עבור אפליקציה חדשה היא ברירת המחדל פלוס או מינוס 20 בהתאם לשאלה אם המטרות נקיות (נמוך יותר) או פגומות (גבוה יותר).

כל זיהוי נושא את אוצר המילים של התיבה התוחמת ואת ארבע הפינות שזוהו, את ה-payload המפוענח, ואת ה-rotation במישור-התמונה ברדיאנים. מטא-נתוני הפריסה מתארים את הגודל והצפיפות של הסמל שהמפענח קרא: rows ו-columns הם ספירות התאים של רשת הנתונים; capacity הוא המספר המרבי של תווי מטען שהסמל יכול היה לשאת בגודל זה; padding הוא כמה מאותם משבצות נותרו לא בשימוש (capacity - len(payload)).

שדות הפריסה שימושיים עבור אפליקציות שצריכות לאמת את הפורמט של סימן חרוט ולא את התוכן שלו. מערכת מעקב חלקים עשויה לדרוש שכל הסימנים יהיו קודי 12 על 12 עם לכל היותר שני תווי ריפוד; זיהוי שחזר בגודל 8 על 8 (סמל קטן יותר מכפי שהמפרט דורש) או עם 10 תווי ריפוד (ריק ברובו) מסומן לסימון מחדש.

5.29.3. מתי לבחור במה

במקום שבו QR מול AprilTag הסתכם בסוג מטען (נתונים שרירותיים מול מזהה קטן), ברקודים מול קודי Data Matrix מסתכמים בצפיפות פיזית ותעשייה.

כאשר האפליקציה פונה לצרכן והקודים כבר קיימים בשטח – מצרכים, ספרים, תוויות משלוח, ספרי ספרייה – המזהה הנכון הוא find_barcodes(). הקודים שהאפליקציה קוראת הודפסו כדי שמערכת אחרת תקרא אותם, והסמליות הקמעונאיות התקניות הן מה שאותה מערכת ציפתה לו.

כאשר האפליקציה תעשייתית והקודים מודפסים עבור האפליקציה – מעקב מלאי ברצפת מפעל, קודי אצווה חרוטים על חלקים, סימני עקיבות על מכשירים רפואיים – המזהה הנכון הוא find_datamatrices() או find_qrcodes(), בהתאם לשאלה אם האפליקציה זקוקה לצפיפות הגבוהה יותר של Data Matrix או לתמיכת הכלים הרחבה יותר של QR.

חופן של אפליקציות מערבב את כל ארבעת המזהים בצינור אחד. מצלמת בדיקת אריזות עשויה להריץ מעבר find_barcodes() עבור ה-UPC המודפס, מעבר find_qrcodes() עבור קוד QR של משלוח על אותה קופסה, ומעבר find_datamatrices() עבור קוד חלק חרוט, הכול על אותו פריים שנלכד; שלוש רשימות התוצאה מתואמות לפי מיקום התיבה התוחמת ומדווחות כרשומת זיהוי יחידה. עלותו של כל מזהה מתווספת, כך שאפליקציות שעושות זאת בדרך כלל מצמצמות כל מעבר עם roi מתאים במקום לחפש בפריים המלא עבור כל סוג של קוד.