9

I'm trying to read a datamatrix barcodes on the bottom of microtubes. I tried libdmtx which has python bindings and works reasonably well when the dots of the matrix are square but much worse when they are round as here:

datamatrix sample

Another complication is a shine which in some cases reaches the code area.

The barcodes are scanned in a rack on a flatbed scanner so they have constant size and are roughly centered. The orientation is random.

I came to a conclusion I'd have to locate the codes and improve the image myself. I use python and OpenCV 3.1. I have already tried thresholding, contours:

import matplotlib.pyplot as plt
import numpy as np
import cv2

well = plt.imread('https://i.stack.imgur.com/kqHkw.png')
well = cv2.cvtColor(well, cv2.COLOR_BGRA2GRAY)
plt.subplot(151); plt.imshow(well)

x, thr = cv2.threshold(well, .4[enter image description here][2], 1, cv2.THRESH_BINARY)
thr = np.uint8(thr)
plt.subplot(152); plt.imshow(thr)

dst, contours, hierarchy = cv2.findContours(thr.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
c = cv2.drawContours(np.zeros_like(thr), contours, -1, 255, 1)
plt.subplot(153); plt.imshow(c)

areas = map(lambda x: cv2.contourArea(cv2.convexHull(x)), contours)
max_i = areas.index(max(areas))
d = cv2.drawContours(np.zeros_like(thr), contours, max_i, 255, 1)
plt.subplot(154); plt.imshow(d)

rect = cv2.minAreaRect(contours[max_i])
box = cv2.boxPoints(rect)
box = np.int0(box)
e = cv2.drawContours(np.zeros_like(thr),[box],0,255,1)
plt.subplot(155); plt.imshow(e)

plt.show()

result

Mische
  • 431
  • 1
  • 4
  • 12
  • 1
    I did not even try zxing because it [reportedly does not support round elements either](https://stackoverflow.com/a/35017291/603323). – Mische Jul 05 '17 at 12:35

2 Answers2

19

It turns out that the Harris corner detector (B) finds the round elements very well with proper settings.

result image here

After thresholding (C) we detect contours of the resulting areas. We select the largest contour (D) and find a minimal bounding box (E).

import matplotlib.pyplot as plt
import numpy as np
import cv2
import PIL
from urllib.request import urlopen

well = np.array(PIL.Image.open(urlopen('https://i.stack.imgur.com/kqHkw.png')))
well = cv2.cvtColor(well, cv2.COLOR_BGRA2GRAY)
plt.subplot(151); plt.title('A')
plt.imshow(well)

harris = cv2.cornerHarris(well,4, 1,0.00)
plt.subplot(152); plt.title('B')
plt.imshow(harris)

x, thr = cv2.threshold(harris, 0.1 * harris.max(), 255, cv2.THRESH_BINARY)
thr = thr.astype('uint8')
plt.subplot(153); plt.title('C')
plt.imshow(thr)

contours, hierarchy = cv2.findContours(thr, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
areas = [cv2.contourArea(cv2.convexHull(x)) for x in contours]
max_i = areas.index(max(areas))
d = cv2.drawContours(np.zeros_like(thr), contours, max_i, 255, 1)
plt.subplot(154); plt.title('D')
plt.imshow(d)

rect = cv2.minAreaRect(contours[max_i])
box = cv2.boxPoints(rect)
box = np.int0(box)
e = cv2.drawContours(well,[box],0,1,1)
plt.subplot(155); plt.title('E')
plt.imshow(e)

plt.show()
dlitz
  • 5,679
  • 3
  • 19
  • 13
Mische
  • 431
  • 1
  • 4
  • 12
0

For python 3 compatibility you need to modify the above code as this:

contours, hierarchy = cv2.findContours(thr.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) 
areas = map(lambda x: cv2.contourArea(cv2.convexHull(x)), contours)  
areas = list(areas)  
max_i = areas.index(max(areas))
andrvibo
  • 131
  • 1
  • 8