Вот подход, основанный на пакете EBImage
. ImageMagik отлично подходит для манипулирования изображениями, но я думаю, что EBImage
может предоставить больше количественных инструментов, которые здесь полезны. Что касается всей обработки изображений, качество входного изображения имеет большое значение. Предложенный здесь подход, вероятно, выиграет от удаления шума и артефактов, масштабирования и, возможно, обрезки.
Кроме того, некоторые лицензии имеют дополнительные символы в интересующей позиции, которые не являются числами. Очевидно, что для таких случаев требуется больше предварительной обработки и фильтрации.
Образец изображения
# Starting from EBImage
if (!require(EBImage)) {
source("http://bioconductor.org/biocLite.R")
biocLite("EBImage")
library(EBImage)
}
# Test images
# setwd(<image directory>)
f1 <- "license1.jpg"
f2 <- "license2.jpg"
# Read image and convert to normalized greyscale
img0 <- readImage(f1)
img <- channel(img0, "grey")
img <- normalize(img)
# plot(img) # insert plot or display commands as desired
# Rudimentary image process for ~300 pixel wide JPEG
xmf <- medianFilter(img, 1)
xgb <- gblur(xmf, 1)
xth <- xgb < otsu(xgb) # Otsu's algorithm to determine best threshold
xto <- opening(xth, makeBrush(3, shape = "diamond"))
Было создано и очищено двоичное (пороговое) изображение для идентификации объектов, как показано здесь.
# Create object mask with unique integer for each object
xm <- bwlabel(xto)
# plot(colorLabels(xm)) # optional code to visualize the objects
В дополнение к элементарной обработке изображения может быть применена некоторая «обработка объекта», как показано здесь. Объекты вдоль края не будут представлять интереса, поэтому они будут удалены. Точно так же артефакты, которые вызывают горизонтальные (широкие) полосы, также могут быть удалены.
# Drop objects touching the edge
nx <- dim(xm)[1]
ny <- dim(xm)[2]
sel <- unique(c(xm[1,], xm[nx,], xm[,1], xm[,ny]))
sel <- sel[sel != 0]
xm <- rmObjects(xm, sel, reenumerate = TRUE)
# Drop exceptionally wide objects (33% of image width)
major <- computeFeatures.moment(xm)[,"m.majoraxis"]
sel <- which(major > nx/3)
xm <- rmObjects(xm, sel, reenumerate = TRUE)
Следующая логика идентифицирует центр масс для каждого объекта с функцией computeFeatures.moment
, равной EBImage
. Кажется, что основные символы будут расположены вдоль горизонтальной линии, а объект-кандидат будет выше этой линии (меньшее значение y в EBImage
объекте Image). Альтернативный подход состоит в том, чтобы находить объекты, наложенные друг на друга, то есть объекты с одинаковыми значениями x.
Для примеров, которые я исследовал, одного стандартного отклонения от медианного значения y для центра масс, по-видимому, достаточно для идентификации объекта-кандидата. Это используется для определения пределов, показанных ниже. Конечно, эта логика должна быть скорректирована в соответствии с фактическими данными.
# Determine center of mass for remaining objects
M <- computeFeatures.moment(xm)
x <- M[,1]
y <- M[,2]
# Show suggested limit on image (y coordinates are inverted)
plot(img)
limit <- median(y) - sd(y)
abline(h = limit, col = "red")
# Show centers of mass on original image
ok <- y < limit
points(x[!ok], y[!ok], pch = 16, col = "blue")
points(x[ok], y[ok], pch = 16, col = "red")
На рисунке показаны сегментированные объекты после отбрасывания объектов по краю. Красным цветом показаны кандидаты, синим - кандидаты.
Поскольку некоторые лицензии имеют два символа над тире, следующий код выбирает крайнего левого из возможных кандидатов, расширяет маску объекта и возвращает прямоугольный фрагмент изображения, который можно передать в ocr()
.
# Accept leftmost (first) of candidate objects
left <- min(x[which(ok)])
sel <- which(x == left)
# Enlarge object mask and extract the candidate image
xm <- dilate(xm, makeBrush(7, "disc"))
ix <- range(apply(xm, 2, function(v) which(v == sel)))
iy <- range(apply(xm, 1, function(v) which(v == sel)))
xx <- ix[1]:ix[2]
yy <- iy[1]:iy[2]
# "Return" selected portion of image
ans <- img[xx, yy] # this is what can be passed to tesseract
plot(ans, interpolate = FALSE)
Вот немасштабированное и извлеченное изображение кандидата из примера 1:
Еще один пример изображения
Тот же код, примененный к этому примеру, дает следующее:
С помощью еще нескольких проверок на наличие ошибок и нелогичных условий код можно собрать в одну функцию и применить к списку из 5000 файлов! Но, конечно, это предполагает, что они правильно отформатированы и т. Д. И т. Д.