Urgent.News

What's breaking now, across thousands of outlets.

Tech

Why your generated Anki image occlusion cards import as plain cloze (and the one-line fix)

I've been building AnkiForge , a web app that turns lecture PDFs and labelled diagrams into Anki cards. For diagrams it makes image occlusion cards: every label on, say, a heart diagram gets hidden, one card per label. Getting the masks in the right place took most of the work. Getting Anki to treat them as real image occlusion took one line of config, and finding out what that line actually does…

AnkiForge is a web application that transforms lecture PDFs and labeled diagrams into Anki cards. The process involves creating image occlusion cards, with each label on a diagram hidden, resulting in a card for each label. The primary challenge lies in positioning the masks correctly. Achieving Anki to recognize these masks as genuine image occlusion required a single line of configuration, though the implications of that line demanded further investigation - which involved delving into Anki's source code twice.

Anki has incorporated image occlusion since version 23.10, although initially, it was available as an add-on called Image Occlusion Enhanced. This add-on came with its own note type and numerous shared decks still utilize this older format. The Anki user forums frequently receive inquiries such as "Anki deck with image occlusion shows no images" from individuals who have imported such decks.

When generating these decks programmatically, the simplest method is to replicate the templates and field names of the built-in Image Occlusion note type. Consequently, a cloze note type is generated, albeit with the misleading name "Image Occlusion." Remarkably, the cards function as intended, reviewing well because the template draws the masks itself.

However, the issue surfaces when a person opens a card to modify a mask. In a desktop view (specifically, aqt/editor_legacy.py from the 26.9 package), Anki determines if it should display the mask editor through the following function:

def current_notetype_is_image_occlusion(self):

bool:

if not self . note:

return False

return ( self . note_type (). get ( originalStockKind , None ) == StockNotetype . OriginalStockKind . ORIGINAL_STOCK_KIND_IMAGE_OCCLUSION )

The critical value determining this is ORIGINAL_STOCK_KIND_IMAGE_OCCLUSION, which equals 6 in a schema 11 collection. When this value is absent, the image occlusion button remains hidden, and the editor displays plain text fields, revealing the raw {{c1::image-occlusion:rect:...}} string where masks should be.

Within a schema 11 collection, note types are stored as JSON in the col table, manifesting as follows in my case:

{

name : "Image Occlusion (AnkiForge)",

type : 1,

// the crucial line

originalStockKind : 6,

flds : [

field("Occlusion", 0,

{ tag : 0, preventDeletion : true }),

field("Image", 1,

{ tag : 1, preventDeletion : true }),

field("Header", 2,

{ tag : 2, preventDeletion : true }),

field("Back Extra", 3,

{ tag : 3, preventDeletion : true }),

field("Comments", 4,

{ tag : 4, preventDeletion : false }),

],

// templates copied from Anki's stock IO note type

}

The field tags and preventDeletion flags are copied from the stock note type, ensuring the generated note matches the built-in one field by field. The occlusion field is a string, precisely copied from each mask. Each mask is represented as a cloze deletion with a shape inside it, where coordinates are fractions of the image size (e.g., {{c1::image-occlusion:rect:left=.2325:top=.1104:width=.1371:height=.0412}}).

The shape coordinates are formatted to four decimal places and then trimmed of leading and trailing zeros. For "Hide all, guess one" mode, each shape is appended with :oi=1. I meticulously matched this output character for character when copying the note into my export, ensuring that a note from my export and the identical note after passing through Anki's editor are indistinguishable.

However, I refrained from testing Anki's parser's tolerance for alternative formats, preferring not to encounter potential issues from users. To validate my approach, I tested the import process using Anki itself, importing the generated .apkg into a fresh collection and subsequently extracting the occlusions via get_image_occlusion_note, Anki's call to load a note's masks.

Following the edit to remove the flag from a test deck, the test still succeeded. This backend parsing confirmed that the absence of the flag does not prevent the parsing of occlusions. However, only the desktop UI reads this flag, so a backend-only test cannot detect its absence. The test has been updated to perform this check directly: the collection is instantiated, the .apkg is imported, and for each note type, the function checks if the note contains the "Occlusion" field.

If successful, it asserts that the note's originalStockKind is 6, signaling that Anki's editor will recognize it as an image occlusion card.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at dev.to →

More in Tech

What jstack Misses When Your Java Service Uses Virtual Threads

Take a Spring Boot service with virtual threads enabled, a HikariCP pool of 10 connections, and roughly 5,000 requests in flight. Latency is climbing.

  • jstack tool may miss virtual threads in JDK 21-25
  • jcmd command with Thread.dumptofile captures virtual threads
  • Analyzing dump files reveals resource causing delays

Whatever You Say Bounces Off Me and Sticks to You

Stephen Nellis and Deepa Seetharaman, reporting Wednesday for Reuters: An OpenAI employee who left the ChatGPT maker for Apple sent more than 100 files about an unreleased product to a personal…

  • OpenAI accuses former employee of leaking confidential product details to Apple.
  • Employee allegedly shared over 100 files to personal account and presented to Apple leaders.
  • OpenAI's lawsuit cites Apple's HomeView product as evidence of AI development.

More from Sunday 18 October →