<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Matiwos Kebede</title>
    <description>The latest articles on DEV Community by Matiwos Kebede (@matiwoskebede).</description>
    <link>https://dev.to/matiwoskebede</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4034674%2F37ca6405-eae5-4b7f-bd84-3c167e2555d8.jpg</url>
      <title>DEV Community: Matiwos Kebede</title>
      <link>https://dev.to/matiwoskebede</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/matiwoskebede"/>
    <language>en</language>
    <item>
      <title>Replacing a Slow QR + Edge Detection Pipeline with a Fast ArUco-Based Vision System</title>
      <dc:creator>Matiwos Kebede</dc:creator>
      <pubDate>Wed, 22 Jul 2026 11:15:11 +0000</pubDate>
      <link>https://dev.to/matiwoskebede/replacing-a-slow-qr-edge-detection-pipeline-with-a-fast-aruco-based-vision-system-28ko</link>
      <guid>https://dev.to/matiwoskebede/replacing-a-slow-qr-edge-detection-pipeline-with-a-fast-aruco-based-vision-system-28ko</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Clear the Lineup&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Project Overview
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/MatiwosKebede/openscanvisionnd" rel="noopener noreferrer"&gt;OpenScanVision&lt;/a&gt; is an offline Android computer vision scanning system designed for fast and accurate scanning of structured voting cards.&lt;/p&gt;

&lt;p&gt;The application uses the device camera to capture a card, detect its position, correct perspective distortion, decode QR information, and analyze OMR (Optical Mark Recognition) bubbles to produce accurate scan results.&lt;/p&gt;

&lt;p&gt;The system uses computer vision techniques including ArUco marker detection, homography transformation, QR decoding, and optimized OMR bubble analysis to achieve reliable real-time scanning on mobile devices.&lt;/p&gt;

&lt;h1&gt;
  
  
  Bug Fix or Performance Improvement
&lt;/h1&gt;

&lt;p&gt;The original OpenScanVision scanning pipeline used QR-based positioning combined with edge detection and homography transformation to detect, rotate, and wrap the card.&lt;/p&gt;

&lt;p&gt;Although the approach worked, it had major performance and accuracy issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scan processing took &lt;strong&gt;7000ms+ on Samsung A11&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Edge detection was slow and required heavy image processing&lt;/li&gt;
&lt;li&gt;Card boundary detection was unreliable with different angles and positions&lt;/li&gt;
&lt;li&gt;Incorrect perspective correction reduced QR decoding and OMR bubble detection accuracy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main challenge was improving both speed and accuracy at the same time.&lt;/p&gt;

&lt;p&gt;The goal was to replace the bottleneck in the detection pipeline and create a fast, reliable real-time scanner.&lt;/p&gt;

&lt;h1&gt;
  
  
  Code
&lt;/h1&gt;

&lt;p&gt;Repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/MatiwosKebede/openscanvisionnd" rel="noopener noreferrer"&gt;https://github.com/MatiwosKebede/openscanvisionnd&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Main optimization commits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0f0ba7b&lt;/code&gt; — Implemented ArUco finder&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;555d0c0&lt;/code&gt; — Improved ArUco position detection&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;709b008&lt;/code&gt; — Added capture preview with overlay and perspective transformation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;961f8f5&lt;/code&gt; — Implemented high accuracy ArUco and OMR bubble detection&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1d5834b&lt;/code&gt; — Improved QR decoding and accuracy&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  My Improvements
&lt;/h1&gt;

&lt;p&gt;I replaced the original QR + edge detection based card localization pipeline with a four-corner ArUco marker-based computer vision pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Original Pipeline
&lt;/h2&gt;

&lt;p&gt;Camera Frame&lt;br&gt;
      ↓&lt;br&gt;
QR Detection&lt;br&gt;
      ↓&lt;br&gt;
Edge Detection&lt;br&gt;
      ↓&lt;br&gt;
Card Boundary Detection&lt;br&gt;
      ↓&lt;br&gt;
Homography Transformation&lt;br&gt;
      ↓&lt;br&gt;
Card Wrapping&lt;br&gt;
      ↓&lt;br&gt;
QR + OMR Processing&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
The original approach had two major problems:

1. Edge detection created a large performance bottleneck.
2. Card boundary estimation was not reliable enough for accurate perspective correction.

This caused slow scanning and inconsistent recognition.


## Optimized Pipeline


Camera Frame
      ↓
Four-Corner ArUco Detection
      ↓
Accurate Card Corner Localization
      ↓
Homography Transformation
      ↓
Card Wrapping
      ↓
QR Decoding
      ↓
OMR Bubble Detection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Technical Improvements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Replaced slow edge-based card localization with ArUco marker detection&lt;/li&gt;
&lt;li&gt;Added four ArUco markers on the card corners for stable position tracking&lt;/li&gt;
&lt;li&gt;Improved rotation and perspective correction&lt;/li&gt;
&lt;li&gt;Improved card wrapping accuracy using reliable corner coordinates&lt;/li&gt;
&lt;li&gt;Reduced unnecessary image processing operations&lt;/li&gt;
&lt;li&gt;Improved QR decoding reliability after accurate alignment&lt;/li&gt;
&lt;li&gt;Improved OMR bubble detection accuracy after consistent transformation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main engineering challenge was balancing speed and accuracy.&lt;/p&gt;

&lt;p&gt;During development, some optimizations increased speed but reduced accuracy. The final pipeline achieved both high performance and reliable recognition.&lt;/p&gt;

&lt;h1&gt;
  
  
  Results
&lt;/h1&gt;

&lt;p&gt;The optimization transformed the scanner from:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7000ms+ → &amp;lt;140ms&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;on a Samsung A11 device.&lt;/p&gt;

&lt;p&gt;The final system achieved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster real-time scanning&lt;/li&gt;
&lt;li&gt;Accurate card localization&lt;/li&gt;
&lt;li&gt;Reliable rotation correction&lt;/li&gt;
&lt;li&gt;Accurate perspective transformation&lt;/li&gt;
&lt;li&gt;High QR decoding accuracy&lt;/li&gt;
&lt;li&gt;High OMR bubble detection accuracy&lt;/li&gt;
&lt;li&gt;Better handling of different card positions and angles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This improvement transformed OpenScanVision from a slow and unreliable scanning pipeline into a fast and robust mobile computer vision system.&lt;/p&gt;

&lt;h1&gt;
  
  
  Best Use of Sentry
&lt;/h1&gt;

&lt;p&gt;Not submitting for this category.&lt;/p&gt;

&lt;h1&gt;
  
  
  Best Use of Google AI
&lt;/h1&gt;

&lt;p&gt;Not submitting for this category.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>opensource</category>
      <category>computervision</category>
    </item>
    <item>
      <title>How I Built a High‑Performance OMR and QR Scanner for Android (Open‑Source)</title>
      <dc:creator>Matiwos Kebede</dc:creator>
      <pubDate>Sat, 18 Jul 2026 04:05:46 +0000</pubDate>
      <link>https://dev.to/matiwoskebede/how-i-built-a-high-performance-omr-and-qr-scanner-for-android-open-source-2ik</link>
      <guid>https://dev.to/matiwoskebede/how-i-built-a-high-performance-omr-and-qr-scanner-for-android-open-source-2ik</guid>
      <description>&lt;h1&gt;
  
  
  How I Built a High‑Performance OMR Scanner for Android (Open‑Source)
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Scanning printed voting cards, surveys, and bubble sheets on Android has always been a challenge. Most existing solutions are either expensive, require an internet connection, or are not accurate enough for real‑world use.&lt;/p&gt;

&lt;p&gt;That's why I built &lt;strong&gt;OpenScanVision&lt;/strong&gt; – an open‑source Android library that combines Optical Mark Recognition (OMR) and QR code decoding, working entirely offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;When I started this project, I needed a scanner that could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detect a card in real‑time from a camera feed.&lt;/li&gt;
&lt;li&gt;Correct perspective distortion (cards are rarely perfectly aligned).&lt;/li&gt;
&lt;li&gt;Accurately extract filled bubbles (even in uneven lighting).&lt;/li&gt;
&lt;li&gt;Decode QR codes to identify the card.&lt;/li&gt;
&lt;li&gt;Work offline – no internet connection required.&lt;/li&gt;
&lt;li&gt;Be easy to integrate into any Android app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Existing libraries were either closed‑source, inaccurate, or tightly coupled to specific UI frameworks. I wanted something modular, accurate, and open.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: OpenScanVision
&lt;/h2&gt;

&lt;p&gt;OpenScanVision is an Android library that solves these problems with a clean, modular architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;Kotlin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image Processing&lt;/td&gt;
&lt;td&gt;OpenCV (contrib)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QR Decoding&lt;/td&gt;
&lt;td&gt;Google ML Kit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Camera&lt;/td&gt;
&lt;td&gt;CameraX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI (Sample App)&lt;/td&gt;
&lt;td&gt;Jetpack Compose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Publishing&lt;/td&gt;
&lt;td&gt;JitPack&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  How It Works – The Scanning Pipeline
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Frame Acquisition
&lt;/h3&gt;

&lt;p&gt;CameraX provides YUV frames at a configurable resolution (default 640×360). The Y (luminance) plane is extracted into an OpenCV grayscale &lt;code&gt;Mat&lt;/code&gt; for tracking.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. ArUco Marker Tracking
&lt;/h3&gt;

&lt;p&gt;The card has 4 ArUco markers (IDs 0–3) printed in the corners. OpenCV detects these markers in real‑time. A Kalman filter smooths the tracking and predicts marker positions when they are temporarily occluded.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Homography &amp;amp; Perspective Correction
&lt;/h3&gt;

&lt;p&gt;Once all 4 markers are stable, a homography matrix is computed. This maps the markers to their reference positions, allowing the card to be warped to a canonical template (850×540 pixels).&lt;/p&gt;

&lt;h3&gt;
  
  
  4. QR Decoding
&lt;/h3&gt;

&lt;p&gt;The QR code is cropped from the &lt;strong&gt;original camera frame&lt;/strong&gt; using the homography – not from the warped image. This preserves sharpness for ML Kit. The crop is enhanced (contrast, denoise, resize) before decoding.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. OMR Extraction
&lt;/h3&gt;

&lt;p&gt;The warped image is preprocessed with CLAHE (contrast enhancement) and a median blur (denoise). Each bubble is sampled using a weighted disk. A per‑group z‑score classification determines which bubbles are filled. An inner‑core fill ratio rejects false positives from paper grain or printed outlines.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Strict Capture Logic
&lt;/h3&gt;

&lt;p&gt;Auto‑capture triggers &lt;strong&gt;only&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All 4 markers are stable.&lt;/li&gt;
&lt;li&gt;A valid QR code with a recognised prefix (e.g., &lt;code&gt;VX&lt;/code&gt;, &lt;code&gt;AGN&lt;/code&gt;) is decoded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This eliminates false positives and ensures every scan is high‑quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The library is split into two modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openscanvision/
├── openscanvision-core/          # Core library (OMR + QR engine)
│   └── src/main/java/.../core/
│       ├── OpenScanVision.kt     # Public API facade
│       ├── ScanOptions.kt        # Builder pattern config
│       ├── ScanResult.kt         # Sealed result class
│       └── internal/             # Implementation (hidden)
├── sample/                       # Reference app (demo)
└── tools/                        # Supporting utilities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core library has &lt;strong&gt;zero UI dependencies&lt;/strong&gt; – no Compose, no CameraX, no Android Views. This means you can use it in any Android project, even headless services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration Example
&lt;/h2&gt;

&lt;p&gt;Adding OpenScanVision to your project takes just a few lines:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Add JitPack to your repositories
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// settings.gradle.kts&lt;/span&gt;
&lt;span class="nf"&gt;dependencyResolutionManagement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;repositories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;mavenCentral&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;maven&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://jitpack.io"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Add the dependency
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/build.gradle.kts&lt;/span&gt;
&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.github.MatiwosKebede:OpenScanVision:openscanvision-core:v1.0.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Scan a card
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Initialize once&lt;/span&gt;
&lt;span class="nc"&gt;OpenScanVision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Scan a frame&lt;/span&gt;
&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;scanCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bitmap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenScanVision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scanFromFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitmap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;ScanResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Success&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Filled: ${result.filledIndices}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Confidence: ${result.confidence}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"QR: ${result.qrPayload}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;ScanResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed: ${result.javaClass.simpleName}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance &amp;amp; Accuracy
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Average latency per frame&lt;/td&gt;
&lt;td&gt;&amp;lt; 150 ms on modern devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accuracy (well‑printed cards)&lt;/td&gt;
&lt;td&gt;&amp;gt; 99%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;False positive rate&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;False negative rate&lt;/td&gt;
&lt;td&gt;&amp;lt; 2%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;The roadmap for 2025 includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi‑frame averaging for improved accuracy.&lt;/li&gt;
&lt;li&gt;Scan history with Room database.&lt;/li&gt;
&lt;li&gt;Export results as CSV.&lt;/li&gt;
&lt;li&gt;QR‑only / OMR‑only scanning modes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contribute
&lt;/h2&gt;

&lt;p&gt;OpenScanVision is MIT‑licensed and open to contributions. If you're interested:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Star the repo on GitHub ⭐&lt;/li&gt;
&lt;li&gt;Try the sample app and report issues.&lt;/li&gt;
&lt;li&gt;Open a Pull Request with improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building OpenScanVision taught me a lot about computer vision, Android optimisation, and open‑source maintainership. I hope it helps other developers build better scanning apps.&lt;/p&gt;

&lt;p&gt;If you have questions, feedback, or ideas – open an issue on GitHub or leave a comment below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/MatiwosKebede/openscanvision" rel="noopener noreferrer"&gt;MatiwosKebede/openscanvision&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏 Acknowledgments
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://opencv.org/" rel="noopener noreferrer"&gt;OpenCV&lt;/a&gt; – image processing and homography.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developers.google.com/ml-kit" rel="noopener noreferrer"&gt;Google ML Kit&lt;/a&gt; – QR code scanning.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.android.com/training/camerax" rel="noopener noreferrer"&gt;CameraX&lt;/a&gt; – camera lifecycle.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.android.com/jetpack/compose" rel="noopener noreferrer"&gt;Jetpack Compose&lt;/a&gt; – modern UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built with dedication by &lt;a href="https://github.com/MatiwosKebede" rel="noopener noreferrer"&gt;Matiwos Kebede&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  📌 What to Do Next
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Copy the article above.&lt;/li&gt;
&lt;li&gt;Go to &lt;a href="https://dev.to"&gt;Dev.to&lt;/a&gt; → Click &lt;strong&gt;"New Post"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Paste the content.&lt;/li&gt;
&lt;li&gt;Add tags: &lt;code&gt;android&lt;/code&gt;, &lt;code&gt;kotlin&lt;/code&gt;, &lt;code&gt;opencv&lt;/code&gt;, &lt;code&gt;omr&lt;/code&gt;, &lt;code&gt;scanning&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Publish!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This article will attract developers who are looking for an OMR, QR and OMR and QR together solution or want to learn about Android + OpenCV. 🚀&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>omr</category>
      <category>openscanvision</category>
    </item>
  </channel>
</rss>
