{
    "version": "https:\/\/jsonfeed.org\/version\/1",
    "title": "Блог об аналитике, визуализации данных, data science и BI, заметки с тегом: skimage",
    "home_page_url": "http:\/\/test.leftjoin.ru\/tags\/skimage\/",
    "feed_url": "http:\/\/test.leftjoin.ru\/tags\/skimage\/json\/",
    "icon": "http:\/\/test.leftjoin.ru\/user\/userpic@2x.jpg",
    "author": {
        "name": "Николай Валиотти",
        "url": "http:\/\/test.leftjoin.ru\/",
        "avatar": "http:\/\/test.leftjoin.ru\/user\/userpic@2x.jpg"
    },
    "items": [
        {
            "id": "44",
            "url": "http:\/\/test.leftjoin.ru\/all\/obrabotka-izobrazheniya-s-chekom-dlya-poiska-qr-koda\/",
            "title": "Обработка изображения с чеком для поиска QR-кода через библиотеку skimage",
            "content_html": "<p>Есть много разных сканеров для QR, но не всегда изображение обладает хорошим качеством. В компьютерном зрении для этого используется Image Pre-processing: предобработка изображения. Сегодня рассмотрим, как средствами библиотеки <span class=\"inline-code\">scikit-image<\/span> помочь QR-сканеру найти код на картинке.<\/p>\n<pre class=\"e2-text-code\"><code>from matplotlib import pyplot as plt\r\nimport skimage\r\nfrom skimage import util, exposure, io, measure, feature\r\nfrom scipy import ndimage as ndi\r\nimport numpy as np\r\nimport cv2<\/code><\/pre><h2>Проблема<\/h2>\n<p>Попробуем просканировать чек из материала <a href=\"http:\/\/test.leftjoin.ru\/all\/sobiraem-dannye-s-chekov-gipermarketov-na-python\/\" class=\"nu\">«<u>Собираем данные с чеков гипермаркетов на Python<\/u>»<\/a>. Прочтём картинку методом <span class=\"inline-code\">imread<\/span> библиотеки <span class=\"inline-code\">matplotlib<\/span> и покажем его на экране:<\/p>\n<pre class=\"e2-text-code\"><code>img = plt.imread('чек.jpg')\r\nplt.imshow(img)<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/1.jpg\" width=\"273\" height=\"252\" alt=\"\" \/>\n<\/div>\n<p>Кажется, в такой каше сложно что-либо разобрать. Воспользуемся готовой функцией для чтения чтения QR-кода из библиотеки <span class=\"inline-code\">opencv<\/span>:<\/p>\n<pre class=\"e2-text-code\"><code>def qr_reader(img):\r\n    detector = cv2.QRCodeDetector()\r\n    data, bbox, _ = detector.detectAndDecode(img)\r\n    if data:\r\n        print(data)\r\n    else:\r\n        print('Ничего не нашлось!')<\/code><\/pre><p>И обратимся к ней, чтобы просканировать наше изображение:<\/p>\n<pre class=\"e2-text-code\"><code>qr_reader(img)<\/code><\/pre><pre class=\"e2-text-code\"><code>Ничего не нашлось!<\/code><\/pre><p>И это можно понять: обилие лишних пикселей мешает сканеру распознать здесь QR-код. Тем не менее, мы можем помочь сканеру, указав где находится искомая область.<\/p>\n<h2>Решение<\/h2>\n<p>Сделаем так: уберём с картинки всё лишнее, найдём координаты прямоугольника с QR-кодом, чтобы затем передать в функцию <span class=\"inline-code\">qr_reader<\/span> не исходное изображение, а исключительно QR-код. Первым делом уменьшим шум, используя <a href=\"https:\/\/docs.scipy.org\/doc\/scipy\/reference\/generated\/scipy.ndimage.median_filter.html\">медианный фильтр<\/a> и сконвертируем изображение из rgb в gray: QR-код состоит всего из двух цветов, так что работать с остальными нам не нужно.<\/p>\n<pre class=\"e2-text-code\"><code>image = ndi.median_filter(util.img_as_float(img), size=9)\r\nimage = skimage.color.rgb2gray(image)\r\nplt.imshow(image, cmap='gray')<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/2.jpg\" width=\"273\" height=\"252\" alt=\"\" \/>\n<\/div>\n<p>Медианный фильтр размыл изображение, и разбросанные одинокие пиксели стали менее отчётливыми, а QR теперь выделяется на их фоне. Попробуем применить <a href=\"https:\/\/scikit-image.org\/docs\/dev\/api\/skimage.exposure.html\">adjust_gamma<\/a> к изображению. Эта функция возводит в степень gamma значение каждого пикселя: чем меньше будет этот параметр — тем меньше будет значение пикселя и тем ближе к белому он будет становиться. Попробуем взять <span class=\"inline-code\">gamma<\/span> за 0.5.<\/p>\n<pre class=\"e2-text-code\"><code>pores_gamma = exposure.adjust_gamma(image, gamma=0.5)\r\nplt.imshow(pores_gamma, cmap='gray')<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/3.jpg\" width=\"273\" height=\"252\" alt=\"\" \/>\n<\/div>\n<p>Заметно, что QR стал ещё отчетливее прочего на фото. Воспользуемся этим: все пиксели, значение которых меньше 0.3 сделаем 0, а остальных — 1.<\/p>\n<pre class=\"e2-text-code\"><code>thresholded = (pores_gamma &lt;= 0.3)\r\nplt.imshow(thresholded, cmap='gray')<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/4.jpg\" width=\"273\" height=\"252\" alt=\"\" \/>\n<\/div>\n<p>А теперь воспользуемся <a href=\"https:\/\/scikit-image.org\/docs\/dev\/auto_examples\/edges\/plot_canny.html\">детектором границ canny<\/a> для полученного изображения <span class=\"inline-code\">thresholded<\/span>. Этот оператор сам сглаживает изображение и ищет градиенты: границы находятся там, где градиент принимает максимальное значение. С повышением параметра sigma детектор <span class=\"inline-code\">canny<\/span> перестает замечать менее отчетливые границы.<\/p>\n<pre class=\"e2-text-code\"><code>edge = feature.canny(thresholded, sigma=6)\r\nplt.imshow(edge)<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/5.jpg\" width=\"273\" height=\"252\" alt=\"\" \/>\n<\/div>\n<p>Наконец, получим координаты границ: для этого нарисуем контуры. Получаем их методом <span class=\"inline-code\">find_contours<\/span> и рисуем поверх изображения <span class=\"inline-code\">edge<\/span>. Объекты массива <span class=\"inline-code\">contours<\/span> — координаты по осям X и Y.<\/p>\n<pre class=\"e2-text-code\"><code>contours = measure.find_contours(edge, 0.5)\r\nplt.imshow(edge)\r\nfor contour in contours:\r\n    plt.plot(contour[:,1], contour[:,0], linewidth=2)<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/6.jpg\" width=\"273\" height=\"252\" alt=\"\" \/>\n<\/div>\n<p>Возьмём максимальные и минимальные координаты по X и по Y: это будут границы видимого прямоугольника.<\/p>\n<pre class=\"e2-text-code\"><code>positions = np.concatenate(contours, axis=0)\r\nmin_pos_x = int(min(positions[:,1]))\r\nmax_pos_x = int(max(positions[:,1]))\r\nmin_pos_y = int(min(positions[:,0]))\r\nmax_pos_y = int(max(positions[:,0]))<\/code><\/pre><p>Теперь, имея координаты, можем на исходном изображении обвести область с кодом:<\/p>\n<pre class=\"e2-text-code\"><code>start = (min_pos_x, min_pos_y)\r\nend = (max_pos_x, max_pos_y)\r\ncv2.rectangle(img, start, end, (255, 0, 0), 5)\r\nio.imshow(img)<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/8.jpg\" width=\"300\" height=\"280\" alt=\"\" \/>\n<\/div>\n<p>Попробуем срезать оригинальное изображение по этим координатам:<\/p>\n<pre class=\"e2-text-code\"><code>new_img = img[min_pos_y:max_pos_y, min_pos_x:max_pos_x]\r\nplt.imshow(new_img)<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/7.jpg\" width=\"311\" height=\"252\" alt=\"\" \/>\n<\/div>\n<p>И передадим новое изображение в функцию <span class=\"inline-code\">qr_reader<\/span>:<\/p>\n<pre class=\"e2-text-code\"><code>qr_reader(new_img)<\/code><\/pre><p>Получаем в ответе:<\/p>\n<pre class=\"e2-text-code\"><code>t=20190320T2303&amp;s=5803.00&amp;fn=9251440300007971&amp;i=141637&amp;fp=4087570038&amp;n=1<\/code><\/pre><p>Это то, чего мы и хотели. Конечно, скрипт не будет универсальным, ведь в каждом изображении будут свои недостатки: где-то шума будет больше, где-то фотография размыта, где-то не будет хватать контраста. Поэтому в отдельных случаях потребуется вносить и иные корректировки в изображение. На следующем этапе обработки фотографии мы воспользуемся уже готовой библиотекой.<\/p>\n",
            "date_published": "2020-06-03T12:35:17+03:00",
            "date_modified": "2020-09-21T13:28:56+03:00",
            "image": "http:\/\/test.leftjoin.ru\/pictures\/1.jpg",
            "_date_published_rfc2822": "Wed, 03 Jun 2020 12:35:17 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "44",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "http:\/\/test.leftjoin.ru\/pictures\/1.jpg",
                    "http:\/\/test.leftjoin.ru\/pictures\/2.jpg",
                    "http:\/\/test.leftjoin.ru\/pictures\/3.jpg",
                    "http:\/\/test.leftjoin.ru\/pictures\/4.jpg",
                    "http:\/\/test.leftjoin.ru\/pictures\/5.jpg",
                    "http:\/\/test.leftjoin.ru\/pictures\/6.jpg",
                    "http:\/\/test.leftjoin.ru\/pictures\/8.jpg",
                    "http:\/\/test.leftjoin.ru\/pictures\/7.jpg"
                ]
            }
        }
    ],
    "_e2_version": 3365,
    "_e2_ua_string": "E2 (v3365; Aegea)"
}