<?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: Devanshi Bhatt</title>
    <description>The latest articles on DEV Community by Devanshi Bhatt (@devanshibhatt23).</description>
    <link>https://dev.to/devanshibhatt23</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%2F4210677%2F5b0b3df8-0bba-48b5-8570-8633fe39dd83.jpg</url>
      <title>DEV Community: Devanshi Bhatt</title>
      <link>https://dev.to/devanshibhatt23</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devanshibhatt23"/>
    <language>en</language>
    <item>
      <title>Ghost Hand: A Local Handwriting Reader That Flags Its Own Doubts</title>
      <dc:creator>Devanshi Bhatt</dc:creator>
      <pubDate>Thu, 29 Oct 2026 19:02:49 +0000</pubDate>
      <link>https://dev.to/devanshibhatt23/gemma-4-misread-212-of-the-words-on-my-familys-recipe-cards-ghost-hand-caught-714-of-them-4ll4</link>
      <guid>https://dev.to/devanshibhatt23/gemma-4-misread-212-of-the-words-on-my-familys-recipe-cards-ghost-hand-caught-714-of-them-4ll4</guid>
      <description>&lt;p&gt;This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/p&gt;

&lt;p&gt;What I Built&lt;/p&gt;

&lt;p&gt;Ghost Hand is a local web app that reads handwritten family recipes and letters, in English and Spanish, and refuses to guess quietly.&lt;br&gt;
Vision models fail on handwriting in an ugly way. They don't say "I can't read this." They hand you a fluent, confident, wrong word. In a family archive that is the worst possible failure: imagine a teaspoon silently becoming a tablespoon in the only copy of a recipe, or a name changing in a letter nobody can check anymore.&lt;/p&gt;

&lt;p&gt;So Ghost Hand treats disagreement as doubt. It reads each page several times, compares the readings word by word, and flags every word the model isn't consistent about. Flagged words are shown next to the cropped original handwriting, and a family member fixes only those. Human corrections are stored separately from the model's output and from the original image. Corrected cards are placed on an ofrenda (the Día de los Muertos altar) and exported as plain Markdown, JSON and images that open without the app.&lt;/p&gt;

&lt;p&gt;How I read the theme&lt;/p&gt;

&lt;p&gt;The ghost in the machine is the person whose hand wrote the card. The machine's only job is to carry their words forward without putting its own words in their mouth. Día de los Muertos is why the app has an ofrenda, and why Spanish sits beside English.&lt;/p&gt;

&lt;p&gt;Demo&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/f-QubP3T4WM" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The video shows: Wi-Fi switched off on screen, a handwritten card uploaded, flagged words highlighted beside the cropped original, one word corrected, and the card placed on the ofrenda.&lt;/p&gt;

&lt;p&gt;Code&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/devanshibhatt23" rel="noopener noreferrer"&gt;
        devanshibhatt23
      &lt;/a&gt; / &lt;a href="https://github.com/devanshibhatt23/Ghost-Hand" rel="noopener noreferrer"&gt;
        Ghost-Hand
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Ghost Hand 🕯️🌼&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hacktoberfest Open-Source AI Challenge: Week 4 — Ghost in the Machine&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Transcribing handwritten ancestral recipes and letters with local open-weight vision AI and uncertainty guarantees.&lt;/em&gt;&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;Ghost Hand is a local, 100% offline-capable application built to transcribe handwritten family cards, recipes, and letters in &lt;strong&gt;English and Spanish&lt;/strong&gt;. Centered around the spirit of the Mexican Día de los Muertos ofrenda (altar), Ghost Hand is designed to honor the past while ensuring private family memories never leave your local device.&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🎯 The Core Problem &amp;amp; The "Uncertainty-by-Disagreement" Engine&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Standard closed and open multimodal vision models hallucinate quietly when confronted with messy handwriting, cursive loops, or faded ink. In family archives, silent misreadings corrupt recipes and distort ancestral words.&lt;/p&gt;

&lt;p&gt;Ghost Hand implements a &lt;strong&gt;code-enforced guardrail around the model&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Pass Inference with Controlled Seeds&lt;/strong&gt;: Each segmented line is read once at greedy temperature ($T = 0$), followed…&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/devanshibhatt23/Ghost-Hand" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
MIT licensed. It runs locally on macOS, Linux and Windows, or with Docker (docker compose up --build).

&lt;p&gt;How I Built It&lt;/p&gt;

&lt;p&gt;The Open-source AI&lt;br&gt;
• Model: Gemma 4 E4B (gemma4:e4b), an open-weight multimodal model, running locally through Ollama.&lt;br&gt;
• No cloud calls. Every read is local inference. There is no closed API anywhere in the pipeline.&lt;/p&gt;

&lt;p&gt;The idea: Uncertainty by Disagreement&lt;/p&gt;

&lt;p&gt;I don't ask the model how sure it is, because models are bad at reporting that. Instead:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read the page once at temperature 0 (the greedy reading, used as the reference).&lt;/li&gt;
&lt;li&gt;Read it 2 more times at temperature about 0.7 with different seeds.&lt;/li&gt;
&lt;li&gt;Align every sampled reading against the reference with Python's difflib.SequenceMatcher.&lt;/li&gt;
&lt;li&gt;A word's agreement score is votes / N. Below 0.8, the word is flagged.&lt;/li&gt;
&lt;li&gt;A line is also flagged when the sampled readings disagree about how many words it has.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The prompt asks the model to transcribe exactly as written, not to correct spelling, not to guess unclear words, and to keep Spanish accents exact.&lt;br&gt;
The model proposes. The code doubts it.&lt;/p&gt;

&lt;p&gt;The pipeline&lt;br&gt;
photo -&amp;gt; resize + grayscale + contrast -&amp;gt; line crops (OpenCV)&lt;br&gt;
      -&amp;gt; 1 greedy read + N sampled reads (Gemma 4 via Ollama)&lt;br&gt;
      -&amp;gt; word alignment + agreement scores -&amp;gt; flags&lt;br&gt;
      -&amp;gt; review page: flagged words next to the cropped original&lt;br&gt;
      -&amp;gt; human corrections -&amp;gt; ofrenda page + plain-text export&lt;/p&gt;

&lt;p&gt;• Preprocessing: Pillow resizes to at most 1600 px, converts to grayscale and boosts contrast.&lt;/p&gt;

&lt;p&gt;• Line splitting: OpenCV with Otsu thresholding and a horizontal projection profile, so each flagged word can be shown against the line it came from. Pages are read as a whole and mapped back to the line crops, which kept it responsive on a laptop.&lt;/p&gt;

&lt;p&gt;• Three layers on disk: original image, raw model output, human corrections. Nothing the model said is ever overwritten.&lt;/p&gt;

&lt;p&gt;• Front end: plain HTML, CSS and JavaScript with all fonts and assets bundled locally. No CDN.&lt;/p&gt;

&lt;p&gt;• Tests: word-agreement scoring, the evaluation metrics, and a test that checks the front end makes no external network references.&lt;br&gt;
Does it work? I tested various images - with both English and Spanish text, and both well-written and illegible handwriting. The model showed more errors while reading illegible handwriting, and showed nearly zero errors while reading &lt;/p&gt;

&lt;p&gt;One limit I want to be upfront about&lt;/p&gt;

&lt;p&gt;Disagreement catches a model that is inconsistent. It cannot catch a model that is consistently wrong. That is exactly why I report flag recall instead of claiming the flags are a guarantee.&lt;/p&gt;

&lt;p&gt;Why Does Open Innovation Matter?&lt;/p&gt;

&lt;p&gt;• Family papers stay on the family's machine. Letters and recipes are private. With open weights running locally, no photo of a loved one's handwriting is uploaded anywhere. The test suite checks the front end for external references, and the demo video runs with Wi-Fi off.&lt;/p&gt;

&lt;p&gt;• The archive must outlive the app. A tool that preserves memories can't depend on a service that might change its terms, raise its price or disappear. Local weights plus plain-text export mean the transcriptions survive with no vendor involved.&lt;/p&gt;

&lt;p&gt;• The method needs many cheap reads. Uncertainty by disagreement reads every page 3 times, with controlled seeds. Locally that costs nothing per read and is exactly repeatable. Through a metered API, every page would cost more and the exact model could change underneath the results.&lt;/p&gt;

&lt;p&gt;• Anyone can reproduce it. The seeds, the threshold, the prompt and the evaluation code are all in the repo. A reader can rerun the evaluation on their own laptop.&lt;/p&gt;

&lt;p&gt;Where a closed model would probably be better&lt;/p&gt;

&lt;p&gt;A much larger closed model may well read messy handwriting more accurately than a small local one. For this use, I would still choose the open one: the cost of a slightly worse first guess is small, because every doubtful word goes in front of a human, while the cost of uploading a family's private papers is not small.&lt;/p&gt;

&lt;p&gt;Prize Categories&lt;/p&gt;

&lt;p&gt;• Gemma: Gemma 4 E4B is the core of the project and does all the reading.&lt;br&gt;
• Github&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
