Friday, November 13, 2020

Syllable-based analysis of OCR errors

In my previous analysis of OCR errors in the context of extracted headwords for volume-1 of the Abridged Myanmar Dictionary, I’d broken up both the raw erroneous headwords and corrected headwords into characters (Unicode code points) and used the setdiff() function to find the differences in the characters for each pair. This approach seemed to be standard for a word based language like English. The mechanism of this approach works for a Syllable based language like our Myanmar language also, but sometimes the result looked somewhat strange to me. So I tried out another way to look at the difference and therefore the correction made in terms of syllable(s) as shown in the examples below:

Extract raw OCR’ed headwords and corrected headwords

To create syllables for original OCR’ed headword and corrected headword, we extract these words first:

library(stringr)
errList.rd1 <- c()
NElist.rd1 <- c()
DRlist.rd1 <- c()
errChrDiff.rd1 <- c()
missErr.rd1 <- c()

# Nov.2, 2020
rawHW1 <- c()
corrHW1 <- c()

j <- 1
n <- 1
Q <- 0
r <- 1
tmp <- ""
for (i in 1:length(Z0)){
  # if marked for error *
  if (grepl("[*]", Z0[i])){
    if (tmp != "") {
      p <- paste0("<",i-1,">")
      DRlist.rd1[r] <- paste(p, tmp)
      r <- r+1
    }
    tmp <- Z0[i]
    Q <- i
  # not marked for error (= correct or corrected + new entry) with - or none 
  }else{
    # if corrected + new entry (none)
    if (grepl("[\u1000-\u104f]$", Z0[i])){
       if (Q == 0|Q>1 & i != Q+1) {  #new entry, test
        m <- paste0("<",i,">")
        NElist.rd1[n] <- paste(m, Z0[i])
        n <- n+1
      }else{    #Q > 0, current not marked at all, last element marked
        if (tmp != "") {   #corrected
          k <- paste0("<",i-1,">", tmp)
          # utf8::utf8_print(paste(k, Z0[i], sep = " ==> "))
          errList.rd1[j] <- paste(k, Z0[i], sep = " ==> ")

          tmp0 <- gsub("[^\u1000-\u104f]", "", tmp)
          eD1 <- intToUtf8(setdiff(utf8ToInt(tmp0), utf8ToInt(Z0[i])))
          eD2 <- intToUtf8(setdiff(utf8ToInt(Z0[i]), utf8ToInt(tmp0)))
          u <- paste0("<",i-1,">")
          v <- paste0("<",i,">")
          errChrDiff.rd1[j] <- paste0(u, "Raw= ", tmp0, " (Extra= ", eD1, ", ", "Missed= ", eD2, "); ", v, "Correct= ", Z0[i], collapse = "")
          missErr.rd1[j] <- eD2
          
          rawHW1[j] <- tmp0
          corrHW1[j] <- Z0[i]
          
          tmp <- ""
          j <- j+1
          tmp0 <- ""
        }
        Q <- 0
      }
    }else{   #(1)with - mark, raw correct; (2)if last mark *, it is dropped
      if (tmp != "") {
        p <- paste0("<",i-1,">")
        DRlist.rd1[r] <- paste(p, tmp)
        r <- r+1
        tmp <- ""
      }
    }
  }
}
#utf8::utf8_print(errList.rd1)
cat((rawHW1)[1:10])
က ကညို ကာကာ ကတေ ကဒ် ကတိုး ကောက် ကောက်စပ် က် ကန
cat((corrHW1)[1:10])
ကစော့ ကညို့ ကတူး ကတောကမျော ကတော့ ကတိုးခွာ ကတောက် ကတောက်စပ် ကတွင်းပေါက် ကနဖေါ့

Convert original headwords and corrected headwords into syllables

First we tokenize headwords into Unicode code points using tokens() function of quanteda package.

library(quanteda)
rawHW1.t <- tokens(rawHW1, what="character")
corrHW1.t <- tokens(corrHW1, what="character")
length(rawHW1.t)
[1] 742
length(corrHW1.t)
[1] 742
head(rawHW1.t)
Tokens consisting of 6 documents.
text1 :
[1] "က"

text2 :
[1] "က" "ညို"

text3 :
[1] "က" "ာ" "က" "ာ"

text4 :
[1] "က"  "တေ"

text5 :
[1] "က"       "ဒ\u103a"

text6 :
[1] "က" "တို" "း"
head(corrHW1.t)
Tokens consisting of 6 documents.
text1 :
[1] "က"  "စေ" "ာ့" 

text2 :
[1] "က" "ညို့"

text3 :
[1] "က" "တူ" "း"

text4 :
[1] "က"        "တေ"       "ာ"        "က"        "မ\u103bေ" "ာ"       

text5 :
[1] "က"  "တေ" "ာ့" 

text6 :
[1] "က"       "တို"       "း"       "ခ\u103d" "ာ"      

With the syllable segmentation of raw headwords (round-1) we create the syllables of a given headword with OR condition to be used as pattern for gsub() function.

rawHW1_syll <- c()
M <- length(rawHW1.t)
for (k in 1:M) {
    rawHW1_syll.k <- list()
    TEMP <- rawHW1.t[[k]][1]
    j <- 1
    L <- length(rawHW1.t[[k]])
      if (L == 1) {
      rawHW1_syll.k[[j]] <- TEMP
      # rawHW1_syll[[k]] <- unlist(rawHW1_syll.k)
      #rawHW1_syll[k] <- paste0(rawHW1_syll.k, collapse = " ")
      rawHW1_syll[[k]] <- unlist(rawHW1_syll.k)
      next
      }else{ #L > 1
        for(i in 2:L) {
            y <- grepl("[\u102b-\u102c\u1038-\u1039\u103a]",rawHW1.t[[k]][i])
            if (y == TRUE){
                TEMP <- paste0(TEMP,rawHW1.t[[k]][i])
            } else { 
                my.1 <- grepl("[\u1040-\u1049]", rawHW1.t[[k]][i])
                my.0 <- grepl("[\u1040-\u1049]", rawHW1.t[[k]][i-1])
                if (my.1 == TRUE){
                    if (my.0 == TRUE){
                        TEMP <- paste0(TEMP,rawHW1.t[[k]][i])
                    } else {
                        rawHW1_syll.k[[j]] <- TEMP
                        j <- j+1
                        TEMP <- rawHW1.t[[k]][i]
                    }
                } else {
                    if (my.0 == TRUE){
                        rawHW1_syll.k[[j]] <- TEMP
                        j <- j+1
                        TEMP <- rawHW1.t[[k]][i]
                    } else {
                        # for stacked consonant
                        if (grepl("[\u1039]",rawHW1.t[[k]][i-1])==TRUE){
                            TEMP <- paste0(TEMP,rawHW1.t[[k]][i])
                        } else {
                            rawHW1_syll.k[[j]] <- TEMP
                            j <- j+1
                            TEMP <- rawHW1.t[[k]][i]
                        }
                    }
                }
            }
        }
        if (i == L){
            rawHW1_syll.k[[j]] <- TEMP
        }
      }
   rawHW1_syll[[k]] <- paste0(unlist(rawHW1_syll.k),collapse = "|")
}
rawHW1_syll[1:10]
[[1]]
[1] "က"

[[2]]
[1] "က|ညို"

[[3]]
[1] "ကာ|ကာ"

[[4]]
[1] "က|တေ"

[[5]]
[1] "ကဒ\u103a"

[[6]]
[1] "က|တိုး"

[[7]]
[1] "ကောက\u103a"

[[8]]
[1] "ကောက\u103a|စပ\u103a"

[[9]]
[1] "က\u103a"

[[10]]
[1] "က|န"

Syllable segmentation of corrected headwords (round-1):

corrHW1_syll <- c()
M <- length(corrHW1.t)
for (k in 1:M) {
    corrHW1_syll.k <- list()
    TEMP <- corrHW1.t[[k]][1]
    j <- 1
    L <- length(corrHW1.t[[k]])
      if (L == 1) {
      corrHW1_syll.k[[j]] <- TEMP
      corrHW1_syll[[k]] <- unlist(corrHW1_syll.k)
      next
      }else{ #L > 1
        for(i in 2:L) {
            y <- grepl("[\u102b-\u102c\u1038-\u1039\u103a]",corrHW1.t[[k]][i])
            if (y == TRUE){
                TEMP <- paste0(TEMP,corrHW1.t[[k]][i])
            } else { 
                my.1 <- grepl("[\u1040-\u1049]", corrHW1.t[[k]][i])
                my.0 <- grepl("[\u1040-\u1049]", corrHW1.t[[k]][i-1])
                if (my.1 == TRUE){
                    if (my.0 == TRUE){
                        TEMP <- paste0(TEMP,corrHW1.t[[k]][i])
                    } else {
                        corrHW1_syll.k[[j]] <- TEMP
                        j <- j+1
                        TEMP <- corrHW1.t[[k]][i]
                    }
                } else {
                    if (my.0 == TRUE){
                        corrHW1_syll.k[[j]] <- TEMP
                        j <- j+1
                        TEMP <- corrHW1.t[[k]][i]
                    } else {
                        # for stacked consonant
                        if (grepl("[\u1039]",corrHW1.t[[k]][i-1])==TRUE){
                            TEMP <- paste0(TEMP,corrHW1.t[[k]][i])
                        } else {
                            corrHW1_syll.k[[j]] <- TEMP
                            j <- j+1
                            TEMP <- corrHW1.t[[k]][i]
                        }
                    }
                }
            }
        }
        if (i == L){
            corrHW1_syll.k[[j]] <- TEMP
        }
      }
   corrHW1_syll[[k]] <- paste0(unlist(corrHW1_syll.k),collapse = "|")
}
corrHW1_syll[1:10]
[[1]]
[1] "က|စော့"

[[2]]
[1] "က|ညို့"

[[3]]
[1] "က|တူး"

[[4]]
[1] "က|တော|က|မ\u103bော"

[[5]]
[1] "က|တော့"

[[6]]
[1] "က|တိုး|ခ\u103dာ"

[[7]]
[1] "က|တောက\u103a"

[[8]]
[1] "က|တောက\u103a|စပ\u103a"

[[9]]
[1] "က|တ\u103dင\u103aး|ပေ\u102bက\u103a"

[[10]]
[1] "က|န|ဖေ\u102b့"

Find differences between raw headwords and corrected headwords

We need syllabification of raw/corrected with OR for each syllable as pattern, and keep the corrected/raw unchanged in using the regex function gsub().

Difference in syllables both ways: raw-corrected and corrected-raw.

# example showing first 10 and last 10 of the list
utf8::utf8_print(HW1_raw_corrList[c(1:10,733:742)])
 [1] "Raw= က (Extra= ; Missed= စော့), Corrected= ကစော့"                    
 [2] "Raw= ကညို (Extra= ညို; Missed= ့), Corrected= ကညို့"                       
 [3] "Raw= ကာကာ (Extra= ာာ; Missed= ကတူး), Corrected= ကတူး"                
 [4] "Raw= ကတေ (Extra= တေ; Missed= ာမျော), Corrected= ကတောကမျော"         
 [5] "Raw= ကဒ် (Extra= ဒ်; Missed= ကတော့), Corrected= ကတော့"                 
 [6] "Raw= ကတိုး (Extra= ; Missed= ခွာ), Corrected= ကတိုးခွာ"                  
 [7] "Raw= ကောက် (Extra= ော်; Missed= ကတောက်), Corrected= ကတောက်"            
 [8] "Raw= ကောက်စပ် (Extra= ော်; Missed= ကတောက်), Corrected= ကတောက်စပ်"        
 [9] "Raw= က် (Extra= ်; Missed= ကတွင်းပေါ), Corrected= ကတွင်းပေါက်"            
[10] "Raw= ကန (Extra= ; Missed= ဖေါ့), Corrected= ကနဖေါ့"                  
[11] "Raw= စုံဆယ် (Extra= ; Missed= ဖြာ), Corrected= စုံဆယ်ဖြာ"                
[12] "Raw= စုံတွဲ (Extra= တွဲ; Missed= ဆွဲ), Corrected= စုံဆွဲ"                      
[13] "Raw= စုံစုံ (Extra= စုံစုံ; Missed= းး), Corrected= စုံးစုံး"                  
[14] "Raw= စ္ဆေ (Extra= စ္ဆေ; Missed= စွေ), Corrected= စွေ"                  
[15] "Raw= စိ (Extra= ; Missed= စွတ်ကပါနမ်းပါ), Corrected= စွတ်ကပါစိနမ်းပါစိ"     
[16] "Raw= စွတ်စွတ်နခ (Extra= နခ; Missed= ာ), Corrected= စွတ်စွတ်နာ"             
[17] "Raw= စွန်ပြီး (Extra= ပြီး; Missed= မြီး), Corrected= စွန်မြီး"            
[18] "Raw= စွန်ပြီးတော့ (Extra= ပြီးတော့; Missed= မြီးကော့), Corrected= စွန်မြီးကော့"
[19] "Raw= စွန်ရှိ (Extra= ရှိ; Missed= ရဲ), Corrected= စွန်ရဲ"                    
[20] "Raw= စွယ်စုံ (Extra= စုံ; Missed= ရံ), Corrected= စွယ်ရံ"                    

Friday, October 30, 2020

Analyzing the OCR errors in the extraction of Myanmar dictionary headwords


In the first round of editing of the raw headwords produced from the OCR process, I marked an erroneous headword with an asterisk at the end of the word. To begin with each of the raw headwords has a hyphen character at the end, so that the erroneous headword will now have both a hyphen and an asterisk at the end. The headwords marked with an asterisk were of three kinds: (i)identical headwords with multiple meanings appear multiple times in the list; since I am extracting the distinct headwords, only one of them will be retained, (ii)some raw headwords were superfluous and they would be dropped, and (iIi)for the remaining, corrected headwords were typed in the next line and they don’t have any additional character like “-” or "*".

Using the above information, I extracted the vectors: errList (that shows each of a pair of the erroneous raw headword and the corrected headword), NElist (that shows headwords missed completely by OCR), and DRlist (the superfluous headwords dropped).

The following is the test run for the imported data file, that is, data from the last (seventh) OCR output text file:

library(stringr)
errList <- c()
NElist <- c()
DRlist <- c()
errChrDiff <- c()
missErr <- c()
j <- 1
n <- 1
Q <- 0
r <- 1
tmp <- ""
for (i in 1:length(z0.418)){
  # if marked for error *
  if (grepl("[*]", z0.418[i])){
    if (tmp != "") {
      p <- paste0("<",i-1,">")
      DRlist[r] <- paste(p, tmp)
      r <- r+1
    }
    tmp <- z0.418[i]
    Q <- i
  # not marked for error (= correct or corrected + new entry) with - or none 
  }else{
    # if corrected + new entry (none)
    if (grepl("[\u1000-\u104f]$", z0.418[i])){
       if (Q == 0|Q>1 & i != Q+1) {  #new entry, test
        m <- paste0("<",i,">")
        NElist[n] <- paste(m, z0.418[i])
        n <- n+1
      }else{    #Q > 0, current not marked at all, last element marked
        if (tmp != "") {   #corrected
          k <- paste0("<",i-1,">", tmp)
          # utf8::utf8_print(paste(k, z0.418[i], sep = " ==> "))
          errList[j] <- paste(k, z0.418[i], sep = " ==> ")

          tmp0 <- gsub("[^\u1000-\u104f]", "", tmp)
          eD1 <- intToUtf8(setdiff(utf8ToInt(tmp0), utf8ToInt(z0.418[i])))
          eD2 <- intToUtf8(setdiff(utf8ToInt(z0.418[i]), utf8ToInt(tmp0)))
          u <- paste0("<",i-1,">")
          v <- paste0("<",i,">")
          errChrDiff[j] <- paste0(u, "Raw= ", tmp0, " (Extra= ", eD1, ", ", "Missed= ", eD2, "); ", v, "Correct= ", z0.418[i], collapse = "")
          missErr[j] <- eD2
          tmp <- ""
          j <- j+1
        }
        Q <- 0
      }
    }else{   #(1)with - mark, raw correct; (2)if last mark *, it is dropped
      if (tmp != "") {
        p <- paste0("<",i-1,">")
        DRlist[r] <- paste(p, tmp)
        r <- r+1
        tmp <- ""
      }
    }
  }
}
utf8::utf8_print(errList)
 [1] "<18>စက်ဆန်း- * ==> စက်ဆန်"                "<33>စက်နခရီ- * ==> စက်နာရီ"              
 [3] "<38>စက်ပြေခင်း - * ==> စက်ပြောင်း"        "<58>စက်လှေကး - * ==> စက်လှေကား"          
 [5] "<65>စက်- * ==> စက်သီး"                   "<70>စက္က - * ==> စက္ကတုံး"               
 [7] "<83>စောက်ထိုးမိုးမှော် - * ==> စောက်ထိုးမိုးမျှော်" "<112>စင်စွမ်း - * ==> စင်ပြွမ်း"           
 [9] "<115>စင်ယှဉ်ပြ၁သခ်- * ==> စင်ယှဉ်ပြ၁သာဒ်"     "<121>စင်း၁- * ==> စင်းချော"            
[11] "<124>စမ်းလေး – * ==> စင်းလေး"           "<128>စင်းလျာ - * ==> စင်းလျှော"         
[13] "<130>စကြိ- * ==> စင်္ကြံ"                 "<159>စောင်းနှင့်- * ==> စောင်းငဲ့"          
[15] "<162>စောင်းညှိုး- * ==> စောင်းညှို့"           "<166>စောင်းခင်း - * ==> စောင်းပြား"     
[17] "<172>စောင်းများ – * ==> စောင်းလျား"     "<179>စိုင်လျမ် - * ==> စိုင်လျော်"           
[19] "<183>စိုင်းနင်- * ==> စိုင်းနှင်"              "<196>စစ် - * ==> စစ်ကဲ"                 
[21] "<217>စ်ဆုတ် - * ==> စစ်ဆုတ်"                "<220>စစ်ညခင်း - * ==> စစ်ညောင်း"         
[23] "<237>စစ်ပရိယယ်- * ==> စစ်ပရိယာယ်"           "<239>စစ်ရ- * ==> စစ်ပါရီ"               
[25] "<242>စစ် - * ==> စစ်ပြေး"               "<244>စစ်ဖြန် - * ==> စစ်ပြန်"            
[27] "<262>စစ်ဝက်- * ==> စစ်လျှောက်"             "<270>စစ်သော်- * ==> စစ်သင်္ဘော"           
[29] "<285>စဉ် - * ==> စဉ့်"                   "<304>စည်း - * ==> စည်ကား"              
[31] "<308>စည်ထို- * ==> စည်တို"                  "<312>စည်ပတ်သီး- * ==> စည်ပတ်သံပြား"        
[33] "<314>စည်ဝင်း - * ==> စည်ဖောင်း"           "<339>စည်းဝါး - * ==> စည်းပြေးဝါးပြေး"  
[35] "<383>စိတ်စေ- * ==> စိတ်စော"               "<440>စိတ်- * ==> စိတ်ဖြေ"                
[37] "<444>စိတ် - * ==> စိတ်မနှံ့"                 "<446>စိတ်မာ- * ==> စိတ်မော"              
[39] "<481>စိတ်အး - * ==> စိတ်အား"              "<487>စိတ္တ- * ==> စိတ္တဇ"                
[41] "<499>စုတ်ထိုး - * ==> စုတ်ပဲ့"                "<508>စန္ဒ၁- * ==> စန္ဒာ"               
[43] "<510>စာသ- * ==> စန္ဒြမာသ"              "<520>စန္ဒ၁ - * ==> စန္ဒ၁း"             
[45] "<541>စိန်ဝင်း - * ==> စိန်ပြောင်း"          "<567>စပ်ကြခင်း - * ==> စပ်ကြောင်း"       
[47] "<570>စပ်စေခင်- * ==> စပ်စောင်"            "<584>စပ် - * ==> စပ်နွား"               
[49] "<586>စပ်ပာ- * ==> စပ်ပညာ"               "<588>စပ်- * ==> စပ်ဖာ"                 
[51] "<591>စပ်ပြီ - * ==> စပ်ဖြဲဖြဲ"             "<596>စပ် - * ==> စပ်ရှား"               
[53] "<600>စပ်လေ့- * ==> စပ်လှေ"                "<604>စပ်ယပ်- * ==> စပ်ဟပ်"               
[55] "<630>စိမ့် - * ==> စိမ့်တော"                "<637>စိမ်း - * ==> စိမ်းကား"             
[57] "<642>စိမ်းအံ့- * ==> စိမ်းဆွံ"                "<646>စိမ်းရွှမ်း - * ==> စိမ်းရွမ်း"          
[59] "<655>စုံဆယ်- * ==> စုံဆယ်ဖြာ"               "<657>စုံတွဲ- * ==> စုံဆွဲ"                   
[61] "<671>စုံစုံ - * ==> စုံးစုံး"                 "<677>စ္ဆေ- * ==> စွေ"                  
[63] "<702>စိ- * ==> စွတ်ကပါစိနမ်းပါစိ"           "<707>စွတ်စွတ်နခ- * ==> စွတ်စွတ်နာ"           
[65] "<718>စွန်ပြီး- * ==> စွန်မြီး"              "<720>စွန်ပြီးတော့ - * ==> စွန်မြီးကော့"      
[67] "<722>စွန်ရှိ- * ==> စွန်ရဲ"                  "<754>စွယ်စုံ- * ==> စွယ်ရံ"                 

To find the differences in characters between an erroneous headword produced by OCR and the correct headword. I used the setdiff() function. There are two ways to look at the difference. One is the characters in OCR headword not found in the correct headword, and the other is the characters in correct headword missed by the OCR headword. I called them “extra”, and “missed”. To use this approach, first the pair of raw and correct headwords is broken up into characters (here Unicode codepoints) and then the setdiff() function is applied.
I got this solution from the stackoverflow Q/A, here.

utf8::utf8_print(errChrDiff)
 [1] "<18>Raw= စက်ဆန်း (Extra= း, Missed= ); <19>Correct= စက်ဆန်"               
 [2] "<33>Raw= စက်နခရီ (Extra= ခ, Missed= ာ); <34>Correct= စက်နာရီ"             
 [3] "<38>Raw= စက်ပြေခင်း (Extra= ခ, Missed= ာ); <39>Correct= စက်ပြောင်း"       
 [4] "<58>Raw= စက်လှေကး (Extra= , Missed= ာ); <59>Correct= စက်လှေကား"           
 [5] "<65>Raw= စက် (Extra= , Missed= သီး); <66>Correct= စက်သီး"                 
 [6] "<70>Raw= စက္က (Extra= , Missed= တုံး); <71>Correct= စက္ကတုံး"               
 [7] "<83>Raw= စောက်ထိုးမိုးမှော် (Extra= , Missed= ျ); <84>Correct= စောက်ထိုးမိုးမျှော်" 
 [8] "<112>Raw= စင်စွမ်း (Extra= , Missed= ပြ); <113>Correct= စင်ပြွမ်း"          
 [9] "<115>Raw= စင်ယှဉ်ပြ၁သခ် (Extra= ခ, Missed= ာဒ); <116>Correct= စင်ယှဉ်ပြ၁သာဒ်" 
[10] "<121>Raw= စင်း၁ (Extra= ၁, Missed= ချော); <122>Correct= စင်းချော"       
[11] "<124>Raw= စမ်းလေး (Extra= မ, Missed= င); <125>Correct= စင်းလေး"         
[12] "<128>Raw= စင်းလျာ (Extra= , Missed= ှေ); <129>Correct= စင်းလျှော"         
[13] "<130>Raw= စကြိ (Extra= ိ, Missed= င်္ံ); <131>Correct= စင်္ကြံ"               
[14] "<159>Raw= စောင်းနှင့် (Extra= နှ, Missed= ဲ); <160>Correct= စောင်းငဲ့"         
[15] "<162>Raw= စောင်းညှိုး (Extra= , Missed= ့); <163>Correct= စောင်းညှို့"          
[16] "<166>Raw= စောင်းခင်း (Extra= ခ, Missed= ပြ); <167>Correct= စောင်းပြား"   
[17] "<172>Raw= စောင်းများ (Extra= မ, Missed= လ); <173>Correct= စောင်းလျား"   
[18] "<179>Raw= စိုင်လျမ် (Extra= မ, Missed= ော); <180>Correct= စိုင်လျော်"         
[19] "<183>Raw= စိုင်းနင် (Extra= , Missed= ှ); <184>Correct= စိုင်းနှင်"             
[20] "<196>Raw= စစ် (Extra= , Missed= ကဲ); <197>Correct= စစ်ကဲ"                 
[21] "<217>Raw= စ်ဆုတ် (Extra= , Missed= ); <218>Correct= စစ်ဆုတ်"                
[22] "<220>Raw= စစ်ညခင်း (Extra= ခ, Missed= ော); <221>Correct= စစ်ညောင်း"       
[23] "<237>Raw= စစ်ပရိယယ် (Extra= , Missed= ာ); <238>Correct= စစ်ပရိယာယ်"         
[24] "<239>Raw= စစ်ရ (Extra= , Missed= ပါီ); <240>Correct= စစ်ပါရီ"             
[25] "<242>Raw= စစ် (Extra= , Missed= ပြေး); <243>Correct= စစ်ပြေး"           
[26] "<244>Raw= စစ်ဖြန် (Extra= ဖ, Missed= ပ); <245>Correct= စစ်ပြန်"           
[27] "<262>Raw= စစ်ဝက် (Extra= ဝ, Missed= လျှော); <263>Correct= စစ်လျှောက်"       
[28] "<270>Raw= စစ်သော် (Extra= , Missed= င္ဘ); <271>Correct= စစ်သင်္ဘော"         
[29] "<285>Raw= စဉ် (Extra= , Missed= ့); <286>Correct= စဉ့်"                   
[30] "<304>Raw= စည်း (Extra= , Missed= ကာ); <305>Correct= စည်ကား"             
[31] "<308>Raw= စည်ထို (Extra= ထ, Missed= တ); <309>Correct= စည်တို"               
[32] "<312>Raw= စည်ပတ်သီး (Extra= ီ, Missed= ံြာ); <313>Correct= စည်ပတ်သံပြား"      
[33] "<314>Raw= စည်ဝင်း (Extra= ဝ, Missed= ဖော); <315>Correct= စည်ဖောင်း"       
[34] "<339>Raw= စည်းဝါး (Extra= , Missed= ပြေ); <340>Correct= စည်းပြေးဝါးပြေး"
[35] "<383>Raw= စိတ်စေ (Extra= , Missed= ာ); <384>Correct= စိတ်စော"             
[36] "<440>Raw= စိတ် (Extra= , Missed= ဖြေ); <441>Correct= စိတ်ဖြေ"             
[37] "<444>Raw= စိတ် (Extra= , Missed= မနှံ့); <445>Correct= စိတ်မနှံ့"               
[38] "<446>Raw= စိတ်မာ (Extra= , Missed= ေ); <447>Correct= စိတ်မော"             
[39] "<481>Raw= စိတ်အး (Extra= , Missed= ာ); <482>Correct= စိတ်အား"             
[40] "<487>Raw= စိတ္တ (Extra= , Missed= ဇ); <488>Correct= စိတ္တဇ"               
[41] "<499>Raw= စုတ်ထိုး (Extra= ထိး, Missed= ပဲ့); <500>Correct= စုတ်ပဲ့"             
[42] "<508>Raw= စန္ဒ၁ (Extra= ၁, Missed= ာ); <509>Correct= စန္ဒာ"             
[43] "<510>Raw= စာသ (Extra= , Missed= န္ဒြမ); <511>Correct= စန္ဒြမာသ"         
[44] "<520>Raw= စန္ဒ၁ (Extra= , Missed= း); <521>Correct= စန္ဒ၁း"             
[45] "<541>Raw= စိန်ဝင်း (Extra= ဝ, Missed= ပြော); <542>Correct= စိန်ပြောင်း"     
[46] "<567>Raw= စပ်ကြခင်း (Extra= ခ, Missed= ော); <568>Correct= စပ်ကြောင်း"     
[47] "<570>Raw= စပ်စေခင် (Extra= ခ, Missed= ာ); <571>Correct= စပ်စောင်"         
[48] "<584>Raw= စပ် (Extra= , Missed= နွား); <585>Correct= စပ်နွား"             
[49] "<586>Raw= စပ်ပာ (Extra= , Missed= ည); <587>Correct= စပ်ပညာ"             
[50] "<588>Raw= စပ် (Extra= , Missed= ဖာ); <589>Correct= စပ်ဖာ"               
[51] "<591>Raw= စပ်ပြီ (Extra= ီ, Missed= ဖဲ); <592>Correct= စပ်ဖြဲဖြဲ"            
[52] "<596>Raw= စပ် (Extra= , Missed= ရှား); <597>Correct= စပ်ရှား"             
[53] "<600>Raw= စပ်လေ့ (Extra= ့, Missed= ှ); <601>Correct= စပ်လှေ"               
[54] "<604>Raw= စပ်ယပ် (Extra= ယ, Missed= ဟ); <605>Correct= စပ်ဟပ်"             
[55] "<630>Raw= စိမ့် (Extra= , Missed= တော); <631>Correct= စိမ့်တော"             
[56] "<637>Raw= စိမ်း (Extra= , Missed= ကာ); <638>Correct= စိမ်းကား"            
[57] "<642>Raw= စိမ်းအံ့ (Extra= အ့, Missed= ဆွ); <643>Correct= စိမ်းဆွံ"             
[58] "<646>Raw= စိမ်းရွှမ်း (Extra= ှ, Missed= ); <647>Correct= စိမ်းရွမ်း"           
[59] "<655>Raw= စုံဆယ် (Extra= , Missed= ဖြာ); <656>Correct= စုံဆယ်ဖြာ"           
[60] "<657>Raw= စုံတွဲ (Extra= တ, Missed= ဆ); <658>Correct= စုံဆွဲ"                 
[61] "<671>Raw= စုံစုံ (Extra= , Missed= း); <672>Correct= စုံးစုံး"                
[62] "<677>Raw= စ္ဆေ (Extra= ္ဆ, Missed= ွ); <678>Correct= စွေ"                 
[63] "<702>Raw= စိ (Extra= , Missed= ွတ်ကပါနမး); <703>Correct= စွတ်ကပါစိနမ်းပါစိ"   
[64] "<707>Raw= စွတ်စွတ်နခ (Extra= ခ, Missed= ာ); <708>Correct= စွတ်စွတ်နာ"         
[65] "<718>Raw= စွန်ပြီး (Extra= ပ, Missed= မ); <719>Correct= စွန်မြီး"           
[66] "<720>Raw= စွန်ပြီးတော့ (Extra= ပတ, Missed= မက); <721>Correct= စွန်မြီးကော့"   
[67] "<722>Raw= စွန်ရှိ (Extra= ှိ, Missed= ဲ); <723>Correct= စွန်ရဲ"                 
[68] "<754>Raw= စွယ်စုံ (Extra= ု, Missed= ရ); <755>Correct= စွယ်ရံ"                

Missed characters in OCR errors, sorted by their frequencies in descending order:

missErrDF <- data.frame(t(table(unlist(strsplit(missErr, "")))))
missErrDF.s <- missErrDF[order(-missErrDF$Freq), 2:3]
utf8::utf8_print(paste0(missErrDF.s$Var2," = ", missErrDF.s$Freq))
 [1] "ာ = 25" "ေ = 13" "ပ = 9"  "ြ = 9"  "း = 8"  "ှ = 6"   "က = 5"  "ဖ = 5" 
 [9] "မ = 5"  "ဲ = 5"   "တ = 4"  "န = 4"  "ံ = 4"   "့ = 4"   "ွ = 4"   "င = 3" 
[17] "္ = 3"   "ျ = 3"  "ဆ = 2"  "ဒ = 2"  "ရ = 2"  "လ = 2"  "ါ = 2"  "ီ = 2"  
[25] "် = 2"   "ခ = 1"  "ဇ = 1"  "ည = 1"  "ဘ = 1"  "သ = 1"  "ဟ = 1"  "ု = 1"  

``Some headwords missed entirely by OCR were typed-in. Listed below:

utf8::utf8_print(NElist)
 [1] "<1> စက်"             "<4> စက်ကတ်ကြေး"       "<12> စက်ချုပ်"        
 [4] "<14> စက်စက်"          "<24> စက်တင်"          "<37> စက်ပုန်းခုတ်"      
 [7] "<72> စက္ကဗျူဟာ"       "<80> စောက်ချ"        "<81> စောက်တိစောက်ထိုး"  
[10] "<82> စောက်ထိုး"        "<85> စောက်ဓား"       "<86> စောက်မှန်းကမ်းမှန်း"
[13] "<89> စိုက်ခင်း"         "<91> စိုက်စိုက်"          "<96> စိုက်ပျိုး"        
[16] "<107> စင်တော်"        "<132> စင်္ကြံလျှောက်"    "<137> စောင်မ"       
[19] "<168> စောင်းမြောင်း"  "<181> စိုင်း"          "<198> စစ်ကြော"      
[22] "<214> စစ်ဆင်"         "<232> စစ်တုံ"          "<235> စစ်ထွက်"        
[25] "<296> စဉ်းစားခန်းဝင်"  "<310> စည်ပင်"         "<357> စုဏ္ဏိယ"        
[28] "<361> စိတ်ကူးစိတ်သန်း"    "<388> စိတ်ဆိုးမာန်ဆိုး"    "<392> စိတ်ညီ"         
[31] "<393> စိတ်ညို့"          "<394> စိတ်ညစ်"         "<395> စိတ်ညှို့"         
[34] "<401> စိတ်တောင်း"      "<410> စိတ်ထောင့်"       "<419> စိတ်နု"         
[37] "<424> စိတ်နောက်ကိုယ်ပါ"   "<431> စိတ်ပူ"          "<432> စိတ်ပေါက်"      
[40] "<451> စိတ်မြန်လက်မြန်"   "<461> စိတ်ရှည်"         "<472> စိတ်လျှော့"      
[43] "<483> စိတ်အားလက်အား"   "<506> စန္ဒဗျူဟာ"      "<530> စိန်တံချူ"       
[46] "<535> စိန်နီမြင်းသွား"   "<590> စပ်ဖျဉ်းဖျဉ်း"   "<619> စမ်းသပ်"       
[49] "<626> စိမ်ရည်"         "<633> စိမ့်မြေ"        "<650> စုံ"           
[52] "<673> စွ"            "<674> စွာ"           "<675> စွာကျယ်စွာကျယ်"  
[55] "<676> စွာလောင်စွာလောင်" "<679> စွေ့"           "<680> စွေ့ကနဲ"        
[58] "<681> စွေးစွေး"       "<682> စွဲ"            "<692> စွဲမြဲ"         
[61] "<696> စွံ"            "<704> စွတ်ကယ်စွတ်ကယ်"     "<712> စွန်"          
[64] "<732> စွန့်လွှတ်"         "<734> စွန်းထင်း"       "<737> စွပ်စွ"         
[67] "<756> စွယ်ရောင်ပြ"    

These are the superfluous headwords dropped:

utf8::utf8_print(DRlist)
 [1] "<27> စက်တိုင်၌ - *"     "<30> စက်တန့် - *"      "<53> စက် - *"       
 [4] "<100> စေ့တော့ - *"    "<108> စင့်် - *"       "<120> စင်း- *"      
 [7] "<134> စေခင် - *"     "<152> စောင်း- *"     "<153> စေခင်း- *"    
[10] "<182> စစ်ကြောင်း - *" "<187> စစ်- *"        "<188> စစ်- *"       
[13] "<189> စစ်- *"        "<190> စစ်- *"        "<195> စစ်- *"       
[16] "<211> စစ်စစ် - *"     "<233> စစ်တဲ့ - *"      "<282> စဉ် - *"      
[19] "<283> စဉ် - *"       "<290> စဉ်း- *"       "<291> စဉ်း- *"      
[22] "<302> စည် - *"       "<303> စည်- *"        "<318> စည့် - *"      
[25] "<320> စည်း – *"      "<411> စိတ်ကြောင့် - *"  "<473> စိတ် - *"      
[28] "<493> စုတ် - *"       "<494> စုတ် - *"       "<502> စုတ် - *"      
[31] "<523> စိန် - *"       "<539> စိန်ပန်း - *"    "<563> စပ်- *"       
[34] "<573> စပ်စပ် -*"      "<574> စပ်စပ်- *"      "<607> စိပ်- *"       
[37] "<629> စိမ့်- *"        "<683> စုံစုံ *"         "<684> စေ့စေ့- *"     
[40] "<752> စွယ်တော်- *"