511 lines
30 KiB
TeX
511 lines
30 KiB
TeX
|
\documentclass[a4paper,fontsize=14bp]{article}
|
|||
|
|
|||
|
\input{../common-preamble}
|
|||
|
\input{../bmstu-preamble}
|
|||
|
\input{../fancy-listings-preamble}
|
|||
|
\numerationTop
|
|||
|
|
|||
|
\begin{document}
|
|||
|
\thispagestyle{empty}
|
|||
|
\makeBMSTUHeader
|
|||
|
|
|||
|
% ... работе, номер, тема, предмет, ?а, кто
|
|||
|
\makeReportTitle{лабораторной}{2}{Распознавание объекта по цвету}{Цифровая обработка изображений}{}{Большаков В.Э.}
|
|||
|
\newpage
|
|||
|
\thispagestyle{empty}
|
|||
|
\tableofcontents
|
|||
|
\newpage
|
|||
|
\pagestyle{fancy}
|
|||
|
\sloppy
|
|||
|
\section{Цель}
|
|||
|
Основной целью лабораторной работы является изучение методов распознания объекта по цвету с помощью библиотеки цифровой обработки изображений scikit-image, а также разработка программы распознавания блюд на подносе.
|
|||
|
\section{Задание}
|
|||
|
\begin{itemize}
|
|||
|
\item Подготовить выборку 10 цветных цифровых изображений блюд бауманской столовой (по аналогии с примером).
|
|||
|
\item В среде Spyder (сборка Anaconda) на языке Python (3.5 и старше) создать проект и подключить библиотеку scikit-image.
|
|||
|
\item Из л.р. 1 взять модуль загрузки цветного цифрового изображения и модуль обработки пикселей.
|
|||
|
\item Запрограммировать формулу перевода цветного цифрового изображения в цветовое пространство в соответствии с вариантом (В1: RGB, В2: HSV, В3: CMYK, В4: YUV, В5: Hough Circle+HSV, В6: Hough Circle +HSL)
|
|||
|
\item Создать классификацию блюд из цифровых изображений.
|
|||
|
\item Для каждого блюда определить цветовые характеристики. Задать распределение значений каждого цвета.
|
|||
|
\item Провести эксперимент по распознаванию блюд с визуализацией результатов.
|
|||
|
\end{itemize}
|
|||
|
|
|||
|
Вариант (по номеру в списке группы) 9, то есть цветовое пространство CMYK. Для распознавания разрешается использовать только цвет.
|
|||
|
\section{Теоретическая часть}
|
|||
|
\subsection{Цветовое пространство CMYK}
|
|||
|
Система CMYK создана и используется для типографической печати. Аббревиатура \textbf{CMYK} означает названия основных красок, использующихся для четырехцветной печати: голубой (Сyan), пурпурный (Мagenta) и жёлтый (Yellow). Буквой К обозначают черную краску (BlacK), позволяющую добиться насыщенного черного цвета при печати. Используется последняя, а не первая буква слова, чтобы не путать CMYK Black и RGB Blue. На рисунке \hrf{pic:cmyk-rgb} показана цветовая палитра CMYK вместе с RGB.
|
|||
|
\begin{figure}[H]
|
|||
|
\centering
|
|||
|
\includegraphics[width=12cm]{02-dip-lab02-cmyk-rgb.png} \caption{Цветовые пространства CMYK и RGB}
|
|||
|
\label{pic:cmyk-rgb}
|
|||
|
\end{figure}
|
|||
|
|
|||
|
\subsection{Особенность формирования цвета}
|
|||
|
Каждое из чисел, определяющее цвет в CMYK, представляет собой процент краски данного цвета, составляющей цветовую комбинацию. Например, для получения тёмно-оранжевого цвета следует смешать 30\% голубой краски, 45\% пурпурной краски, 80\% жёлтой краски и 5\% чёрной. Это можно обозначить следующим образом: CMYK(30/45/80/5).
|
|||
|
|
|||
|
\subsection{Перевод из RGB в CMYK}
|
|||
|
Для того, чтобы перевести пиксель RGB в пиксель CMYK можно применить формулу, описанную на языке Java и представленную в таблице \hrf{code:rgb-cmyk}.
|
|||
|
\begin{lstlisting}[language=Java, style=JCodeStyle, caption={Формула перевода из RGB в CMYK}, label={code:rgb-cmyk}]
|
|||
|
int black = Math.min(Math.min(255 - red, 255 - green), 255 - blue);
|
|||
|
if (black!=255) {
|
|||
|
int cyan = (255-red-black)/(255-black);
|
|||
|
int magenta = (255-green-black)/(255-black);
|
|||
|
int yellow = (255-blue-black)/(255-black);
|
|||
|
return new int[] {cyan,magenta,yellow,black};
|
|||
|
} else {
|
|||
|
int cyan = 255 - red;
|
|||
|
int magenta = 255 - green;
|
|||
|
int yellow = 255 - blue;
|
|||
|
return new int[] {cyan,magenta,yellow,black};
|
|||
|
}
|
|||
|
\end{lstlisting}
|
|||
|
Пример полученного изображения с использованием преобразования пикселей представлен на рисунке \hrf{pic:trans}. При преобразовании таким методом исходного изображения (\hrf{pic:trans-rgb}) будет получено изображение не естественных цветов (\hrf{pic:trans-cmyk}).
|
|||
|
|
|||
|
\begin{figure}[H]
|
|||
|
\centering
|
|||
|
\begin{subfigure}[b]{0.4\textwidth}
|
|||
|
\centering
|
|||
|
\includegraphics[width=\textwidth]{02-dip-lab02-trans-rgb.png}
|
|||
|
\caption{Исходное изображение в пространстве RGB}
|
|||
|
\label{pic:trans-rgb}
|
|||
|
\end{subfigure}
|
|||
|
\hfill
|
|||
|
\begin{subfigure}[b]{0.4\textwidth}
|
|||
|
\centering
|
|||
|
\includegraphics[width=\textwidth]{02-dip-lab02-trans-cmyk.png}
|
|||
|
\caption{Преобразованное изображение CMYK}
|
|||
|
\label{pic:trans-cmyk}
|
|||
|
\end{subfigure}
|
|||
|
\caption{Пример попиксельного перевода изображения}
|
|||
|
\label{pic:trans}
|
|||
|
\end{figure}
|
|||
|
|
|||
|
Данные результаты не являются правильными. Для более точного преобразования следует использовать ICC профили, описывающие как должен отображаться цвет на мониторе и бумаге.
|
|||
|
|
|||
|
\subsection{Цветовое профилирование}
|
|||
|
Для достоверного управления цветом необходимы ICC-совместимые профили всех цветовоспроизводящих устройств. Например, без точного профиля сканера хорошо отсканированное изображение может отображаться в другой программе неправильно из-за различий между алгоритмами отображения, используемыми сканером и программой. Недостоверность цветопередачи может привести к внесению в хорошее изображение ненужных и, возможно, вредных "улучшений". При наличии точного профиля программа, импортирующая изображение, способна скорректировать разницу с устройством и воспроизвести достоверные цвета отсканированного изображения.
|
|||
|
|
|||
|
Профили документов описывают конкретное цветовое пространство, используемое в документе. Путем назначения профиля, или пометки документа профилем, приложение определяет фактические цвета документа. Например, запись R = 127, G = 12, B = 107 — это просто набор чисел, которые разные устройства будут отображать по-разному. Однако при пометке цветовым пространством Adobe RGB эти числа определяют фактический цвет или длину световой волны. На рисунке \hrf{pic:icc-profile} представлена назначение профилей ICC.
|
|||
|
\begin{figure}[H]
|
|||
|
\centering
|
|||
|
\includegraphics[width=5cm]{02-dip-lab02-icc-profile.png}
|
|||
|
\caption{Пример использования профилей ICC}
|
|||
|
\label{pic:icc-profile}
|
|||
|
\end{figure}
|
|||
|
|
|||
|
При использовании профилей преобразованные изображения получаются более правдоподобными. На рисунке \hrf{pic:icc-trans} представлено такое преобразование, \hrf{pic:icc-trans-rgb} пространство RGB, \hrf{pic:icc-trans-cmyk} пространство CMYK.
|
|||
|
\begin{figure}[H]
|
|||
|
\centering
|
|||
|
\begin{subfigure}[b]{0.4\textwidth}
|
|||
|
\centering
|
|||
|
\includegraphics[width=\textwidth]{02-dip-lab02-trans-rgb.png}
|
|||
|
\caption{Исходное изображение в пространстве RGB}
|
|||
|
\label{pic:icc-trans-rgb}
|
|||
|
\end{subfigure}
|
|||
|
\hfill
|
|||
|
\begin{subfigure}[b]{0.4\textwidth}
|
|||
|
\centering
|
|||
|
\includegraphics[width=\textwidth]{02-dip-lab02-trans-rgb.png}
|
|||
|
\caption{Преобразованное изображение CMYK}
|
|||
|
\label{pic:icc-trans-cmyk}
|
|||
|
\end{subfigure}
|
|||
|
\caption{Пример преобразования с использованием профилей}
|
|||
|
\label{pic:icc-trans}
|
|||
|
\end{figure}
|
|||
|
|
|||
|
\section{Практическая часть}
|
|||
|
\subsection{Состав исполняемых файлов}
|
|||
|
Программа по распознаванию изображений состоит из двух скриптов:
|
|||
|
\begin{itemize}
|
|||
|
\item основного скрипта для выполнения программы \code{main.py};
|
|||
|
\item скрипта для обработки изображений \code{colorFoodView.py}.
|
|||
|
\end{itemize}
|
|||
|
|
|||
|
Основной скрипт для тренировки принимает 2 аргумента для запуска:
|
|||
|
\begin{itemize}
|
|||
|
\item \code{-c}, файл конфигурации программы в формате json;
|
|||
|
\item \code{-f}, файл описывающий блюда и их цвета.
|
|||
|
\end{itemize}
|
|||
|
|
|||
|
Например, скрипт для запуска тренировки может выглядеть следующим образом:
|
|||
|
\begin{lstlisting}[style=CCodeStyle]
|
|||
|
main.py -c "./configs.json" -f "./foods.json"
|
|||
|
\end{lstlisting}
|
|||
|
|
|||
|
\subsection{Конфигурационные файлы}
|
|||
|
Для корректной работы программы, ей необходимо передать два файла в формате json:
|
|||
|
\begin{enumerate}
|
|||
|
\item файл конфигурации (далее \code{config.json});
|
|||
|
\item файл описания блюд (далее \code{food.json}).
|
|||
|
\end{enumerate}
|
|||
|
|
|||
|
Параметры файла \code{config.json} представлены в таблице \hrf{table:config-json}, а пример заполненного файла в листинге \hrf{code:conf-json}.
|
|||
|
\begin{table}[H]
|
|||
|
\centering
|
|||
|
\begin{tabular}{|l|p{100mm}|}
|
|||
|
\hline
|
|||
|
Наименование параметра & Описание \\ \hline
|
|||
|
\code{RGB_profile} & Путь до профиля ICC RGB описывающего цветовое пространство исходного изображения \\ \hline
|
|||
|
\code{CMYK_profile} & Путь до профиля ICC CMYK в чье цветовое пространство должно быть переведено изображение \\ \hline
|
|||
|
\code{Path_to_food} & Путь до папки с изображениями \\ \hline
|
|||
|
\code{Delta_for_color} & Процент погрешности для пикселей \\ \hline
|
|||
|
\code{Color_swap} & Цвет в формате CMYK в который необходимо преобразовать найденные пиксели \\ \hline
|
|||
|
\code{Mode} & Режим работы программы. Если режим работы Test, то в консоль будет выводиться количество найденных пикселей для искомого блюда\\
|
|||
|
\hline
|
|||
|
\end{tabular}
|
|||
|
\caption{Параметры конфигурации}
|
|||
|
\label{table:config-json}
|
|||
|
\end{table}
|
|||
|
|
|||
|
\begin{lstlisting}[language=C,style=CCodeStyle, caption={Пример настроек параметров в программе}, label={code:conf-json}]
|
|||
|
{
|
|||
|
"RGB_profile": "./AdobeICCProfiles/RGB/AdobeRGB1998.icc",
|
|||
|
"CMYK_profile": "./AdobeICCProfiles/CMYK/WebCoatedSWOP2006Grade5.icc",
|
|||
|
"Path_to_food": "./WorkFood",
|
|||
|
"Delta_for_color": 5,
|
|||
|
"Color_swap": [100, 78, 0, 0],
|
|||
|
"Mode": "Product"
|
|||
|
}
|
|||
|
\end{lstlisting}
|
|||
|
|
|||
|
Параметры файла \code{food.json} представлены в таблице \hrf{table:food-json}, а пример заполненного файла в листинге \hrf{code:food-json}. Файл описывает список блюд, а именно параметры каждого из элементов списка.
|
|||
|
\begin{table}[H]
|
|||
|
\centering
|
|||
|
\begin{tabular}{|l|p{100mm}|}
|
|||
|
\hline
|
|||
|
Наименование параметра & Описание \\ \hline
|
|||
|
Name & Наименование блюда \\ \hline
|
|||
|
CMYK & Пиксели которые описывают данное блюдо \\ \hline
|
|||
|
Count & Минимальное количество пикселей, которое должно быть у изображения для того, чтобы можно было говорить о том, что на изображении присутствует искомое блюдо. Данное значение можно получить в тестовом режиме \\
|
|||
|
\hline
|
|||
|
\end{tabular}
|
|||
|
\caption{Параметры конфигурации}
|
|||
|
\label{table:food-json}
|
|||
|
\end{table}
|
|||
|
|
|||
|
\begin{lstlisting}[language=C,style=CCodeStyle, caption={Пример настроек параметров в программе}, label={code:food-json}]
|
|||
|
{
|
|||
|
"Foods": [
|
|||
|
{
|
|||
|
"Name": "Банан в шоколаде",
|
|||
|
"CMYK": [
|
|||
|
{ "C": 23, "M": 29, "Y": 44, "K": 7 },
|
|||
|
{ "C": 29, "M": 40, "Y": 57, "K": 11 },
|
|||
|
{ "C": 34, "M": 61, "Y": 63, "K": 40 }
|
|||
|
],
|
|||
|
"Count": 2000
|
|||
|
},
|
|||
|
{
|
|||
|
"Name": "Сосиска",
|
|||
|
"CMYK": [
|
|||
|
{ "C": 27, "M": 65, "Y": 76, "K": 25 },
|
|||
|
{ "C": 14, "M": 47, "Y": 67, "K": 3 },
|
|||
|
{ "C": 24, "M": 65, "Y": 85, "K": 20 },
|
|||
|
{ "C": 28, "M": 83, "Y": 99, "K": 30 }
|
|||
|
],
|
|||
|
"Count": 9000
|
|||
|
},
|
|||
|
//...
|
|||
|
]
|
|||
|
}
|
|||
|
\end{lstlisting}
|
|||
|
|
|||
|
\subsection{Описание алгоритма работы программы}
|
|||
|
Алгоритм работы основной программы заключается в загрузке и настройки конфигурации и передаче их в скрипт обработки изображения. После выполнения поиска блюд и перед началом нового цикла работа можно скорректировать \code{json} файлы конфигурации и изменения будут применены без необходимости перезапуска программы.
|
|||
|
В скрипте обработки изображения используется следующий алгоритм работы:
|
|||
|
\begin{itemize}
|
|||
|
\item преобразовать все изображения в цветовое пространство, описанное профилем CMYK;
|
|||
|
\item для каждого изображения найти пиксели из выбранного блюда;
|
|||
|
\item если пиксель найден, то его цвет меняется на цвет, заданный в настройках;
|
|||
|
\item проверяется количество найденных пикселей, если их меньше порогового значения, то изображение отбрасывается;
|
|||
|
\item изображения, прошедшие проверку, возвращаются в основную программу для дальнейшей работы.
|
|||
|
\end{itemize}
|
|||
|
|
|||
|
\subsection{Выборка для тестирования}
|
|||
|
Для тестирования была подготовлена выборка из десяти подносов с едой. Каждая картинка была приведена к разрешению 400х300 пикселей. Изначальный размер изображений (более 4000х3000 пикселей) не позволяет добиться приемлемых результатов с использованием только цвета как характеристики для поиска изображений на фото. На рисунке \hrf{pic:food-src} представлена подготовленная для работы выборка.
|
|||
|
|
|||
|
\begin{figure}[H]
|
|||
|
\centering
|
|||
|
\includegraphics[width=3cm]{01-dip-lab02-rgb1.jpg}
|
|||
|
\includegraphics[width=3cm]{01-dip-lab02-rgb2.jpg}
|
|||
|
\includegraphics[width=3cm]{01-dip-lab02-rgb3.jpg}
|
|||
|
\includegraphics[width=3cm]{01-dip-lab02-rgb4.jpg}
|
|||
|
\includegraphics[width=3cm]{01-dip-lab02-rgb5.jpg}
|
|||
|
\includegraphics[width=3cm]{01-dip-lab02-rgb6.jpg}
|
|||
|
\includegraphics[width=3cm]{01-dip-lab02-rgb7.jpg}
|
|||
|
\includegraphics[width=3cm]{01-dip-lab02-rgb8.jpg}
|
|||
|
\includegraphics[width=3cm]{01-dip-lab02-rgb9.jpg}
|
|||
|
\includegraphics[width=3cm]{01-dip-lab02-rgb10.jpg}
|
|||
|
\caption{Исходная выборка изображений}
|
|||
|
\label{pic:food-src}
|
|||
|
\end{figure}
|
|||
|
|
|||
|
\subsection{Подготовка данных}
|
|||
|
Для анализа изображений на предмет присутствия искомых пикселей они должны быть правильно заданы. Для этого используется скрипт \code{converter.py} преобразующий рабочее изображение в пространство CMYK. В дальнейшем с помощью дополнительного инструментария\footnote{онлайн пикер цвета https://www.ginifab.com/feeds/pms/color\_picker\_from\_image.php}, возможно узнать цвет пикселей в пространстве CMYK и внести в \code{food.json}.
|
|||
|
|
|||
|
\section{Результат работы}
|
|||
|
\subsection{Листинг}
|
|||
|
Исходный код скриптов \code{main.py}, \code{colorFoodView.py} и \code{converter.py} представлены в приложениях \hrf{app:main}, \hrf{app:find} и \hrf{app:conv} соответственно.
|
|||
|
|
|||
|
\subsection{Интерфейс и измерения}
|
|||
|
Программа использует терминальный текстовый интерфейс для взаимодействия с пользователем. На основе содержания \code{food.json} строятся возможные варианты выбора в основном меню. Предусмотрен контроль ошибок ввода. В результате измерений программа вернет изображения где, по её мнению, присутствуют искомые продукты. Найденные пиксели будут закрашены в заданный в конфигурации цвет. Результат работы для поиска яичницы приведен на рисунке \hrf{pic:result}.
|
|||
|
|
|||
|
\begin{figure}[H]
|
|||
|
\centering
|
|||
|
\includegraphics[width=12cm]{01-dip-lab02-result.png}
|
|||
|
\caption{Результат работы программы}
|
|||
|
\label{pic:result}
|
|||
|
\end{figure}
|
|||
|
|
|||
|
\subsection{Ошибки}
|
|||
|
В ходе вычислений программа допускает некоторое количество ошибок. Чем качественнее изображение, тем больше шансов что искомый пиксель будет присутствовать на изображении в неожиданных местах. Рабочая выборка подверглась минимальной предварительной обработке и часто в искомую область попадают объекты, не связанные с продуктами, такие как стол или поднос. В таблице \hrf{table:errors} представлены ошибки первого и второго рода для измерений.
|
|||
|
\begin{table}[H]
|
|||
|
\centering
|
|||
|
\begin{tabular}{||r|c|c||}
|
|||
|
\hline
|
|||
|
\multirow{2}{*}{Результаты} & \multicolumn{2}{c|}{Верная гипотеза} \\
|
|||
|
\cline{2-3}
|
|||
|
& H0 & H1 \\
|
|||
|
\hline
|
|||
|
H0 & 27 & 36 \\
|
|||
|
\hline
|
|||
|
H1 & 0 & 524 \\
|
|||
|
\hline
|
|||
|
\end{tabular}
|
|||
|
\caption{Ошибка первого и второго рода}
|
|||
|
\label{table:errors}
|
|||
|
\end{table}
|
|||
|
\begin{itemize}
|
|||
|
\item [] Ошибка первого рода: $0\%$
|
|||
|
\item [] Ошибка второго рода: $36 / 524 * 200 = 7\%$
|
|||
|
\end{itemize}
|
|||
|
Данные результаты легко объяснить. Коэффициенты для срабатывания определения блюда настроены таким образом, чтобы находить блюдо в наборе. И программа хорошо справляется с этой задачей. Ошибки второго рода могут возникнуть, когда цвет блюда очень общий для изображения и срабатывает в нем во многих местах. Таких ситуаций не много, однако они имеют место быть. Основная масса ошибок второго рода отсекаются настраиваемым коэффициентом для каждого блюда.
|
|||
|
|
|||
|
\section{Заключение}
|
|||
|
В ходе выполнения задания были изучены методы по обработке изображения и работы с цветом. Исходя из результатов тестирования можно сделать вывод, что использование только цвета для поиска объектов на картинке не является достаточным, а подготовке рабочей выборки нужно уделять большое внимание. Разный источник света и разные условия сьемки сильно изменяют цвета, которые можно использовать для идентификации объектов.
|
|||
|
|
|||
|
\newpage
|
|||
|
\appendix
|
|||
|
\section*{Приложения}
|
|||
|
\addcontentsline{toc}{section}{Приложения}
|
|||
|
\renewcommand{\thesubsection}{\Asbuk{subsection}}
|
|||
|
|
|||
|
\subsection{Основная программа}
|
|||
|
\label{app:main}
|
|||
|
\begin{lstlisting}[language=Python,style=PyCodeStyle]
|
|||
|
import argparse
|
|||
|
|
|||
|
# Парсим аргументы вызова скрипта
|
|||
|
import codecs
|
|||
|
import json
|
|||
|
import sys
|
|||
|
|
|||
|
from ColorFoodView import find_food
|
|||
|
|
|||
|
|
|||
|
def create_parser():
|
|||
|
parser = argparse.ArgumentParser()
|
|||
|
parser.add_argument('-c', '--configs')
|
|||
|
parser.add_argument('-f', '--foods')
|
|||
|
return parser
|
|||
|
|
|||
|
|
|||
|
def menu(foods):
|
|||
|
print("Допустимый выбор:")
|
|||
|
print("0 : Выход")
|
|||
|
i = 1
|
|||
|
for food in foods["Foods"]:
|
|||
|
print(i, ":", food["Name"])
|
|||
|
i += 1
|
|||
|
print("Введите команду:")
|
|||
|
i -= 1
|
|||
|
while 1:
|
|||
|
try:
|
|||
|
chose = int(input())
|
|||
|
if 0 > chose or chose > i:
|
|||
|
print("Данной команды не существует. Выберите из предложенного набора и попробуйте еще раз.")
|
|||
|
continue
|
|||
|
return chose
|
|||
|
except:
|
|||
|
print("Необходимо ввести число соответсвующее вашему выбору. Попробуйте еще раз")
|
|||
|
|
|||
|
|
|||
|
# поиск продуктов
|
|||
|
def food_name(foods, chose):
|
|||
|
return foods["Foods"][chose - 1]
|
|||
|
|
|||
|
|
|||
|
namespace = create_parser().parse_args(sys.argv[1:])
|
|||
|
|
|||
|
while 1:
|
|||
|
# получение конфигурационных данных
|
|||
|
configs = json.loads(codecs.open(namespace.configs, 'r', encoding='utf-8').read())
|
|||
|
# получение данных для справочника продуктов
|
|||
|
foods = json.loads(codecs.open(namespace.foods, 'r', encoding='utf-8').read())
|
|||
|
# меню выбора
|
|||
|
chose = menu(foods)
|
|||
|
if chose == 0:
|
|||
|
break
|
|||
|
# запрос на поиск продуктов
|
|||
|
images_food = find_food(configs, food_name(foods, chose))
|
|||
|
for image in images_food:
|
|||
|
image.show()
|
|||
|
print("Нажмите любую кнопку чтобы продолжить.")
|
|||
|
input()
|
|||
|
\end{lstlisting}
|
|||
|
|
|||
|
\subsection{Поиск объектов по цвету}
|
|||
|
\label{app:find}
|
|||
|
\begin{lstlisting}[language=Python,style=PyCodeStyle]
|
|||
|
import os
|
|||
|
|
|||
|
import re
|
|||
|
|
|||
|
from PIL import Image
|
|||
|
from PIL.ImageCms import profileToProfile
|
|||
|
|
|||
|
color_swap = [0, 0, 0, 255]
|
|||
|
delta_for_color = 0
|
|||
|
mode = "Product"
|
|||
|
|
|||
|
|
|||
|
# Возвращает список изображений в цветовом пространтстве CMYK
|
|||
|
def get_list_cmyk_image(path, rgb_profile, cmyk_profile):
|
|||
|
list_image = []
|
|||
|
for d, dirs, files in os.walk(path): # Достаем все каталоги по пути
|
|||
|
for f in files: # Достаем все файлы
|
|||
|
if re.search(".jpg", f): # Сортировка по типу
|
|||
|
path = os.path.join(d, f) # Получаем путь до изображения
|
|||
|
im = Image.open(path)
|
|||
|
picture_cmyk = profileToProfile(im, rgb_profile, cmyk_profile, outputMode='CMYK')
|
|||
|
list_image.append(picture_cmyk) # Добавляем полученное изображение в список
|
|||
|
return list_image
|
|||
|
|
|||
|
|
|||
|
# переводит список процентов в соответствующее значение [0..255]
|
|||
|
def percent_to_scale_list(percent):
|
|||
|
list = []
|
|||
|
for p in percent:
|
|||
|
list.append(percent_to_scale(p))
|
|||
|
return list
|
|||
|
|
|||
|
|
|||
|
# преобразует процент в соответветсвующее значение [0..255]
|
|||
|
def percent_to_scale(percent):
|
|||
|
return int(percent / 100 * 255)
|
|||
|
|
|||
|
|
|||
|
# получение минимум
|
|||
|
# значение пикселя - процент погрешности
|
|||
|
def get_min(param):
|
|||
|
result = param - delta_for_color
|
|||
|
if result < 0:
|
|||
|
return 0
|
|||
|
else:
|
|||
|
return result
|
|||
|
|
|||
|
|
|||
|
# получение максимума
|
|||
|
# значение пикселя + процент погрешности
|
|||
|
def get_max(param):
|
|||
|
result = param + delta_for_color
|
|||
|
if result > 255:
|
|||
|
return 255
|
|||
|
else:
|
|||
|
return result
|
|||
|
|
|||
|
|
|||
|
# проверка входит ли значение пикселя в заданные границы
|
|||
|
def pixel_equal(pixel, mask_pixel):
|
|||
|
min_c = get_min(pixel)
|
|||
|
max_c = get_max(pixel)
|
|||
|
return min_c <= mask_pixel <= max_c
|
|||
|
|
|||
|
|
|||
|
# проверка пикселя на соответсвие искомого
|
|||
|
def range_equal(pixel, mask_pixel):
|
|||
|
c = pixel_equal(pixel[0], mask_pixel[0])
|
|||
|
m = pixel_equal(pixel[1], mask_pixel[1])
|
|||
|
y = pixel_equal(pixel[2], mask_pixel[2])
|
|||
|
k = pixel_equal(pixel[3], mask_pixel[3])
|
|||
|
|
|||
|
if c and m and y and k:
|
|||
|
return 1
|
|||
|
else:
|
|||
|
return 0
|
|||
|
|
|||
|
|
|||
|
# ищем пиксели из изображения и сравниваем с искомыми
|
|||
|
# если поиск был успешен, то заменяем его на заданные в конфигурации цвет
|
|||
|
def swap_pixel(image, mask_pixels, color_count):
|
|||
|
count = 0
|
|||
|
for i in range(image.width):
|
|||
|
for j in range(image.height):
|
|||
|
pixel = image.getpixel((i, j))
|
|||
|
for mask_pixel in mask_pixels:
|
|||
|
if range_equal(pixel, mask_pixel):
|
|||
|
image.putpixel((i, j), tuple(color_swap))
|
|||
|
count += 1
|
|||
|
if mode == "Test":
|
|||
|
print(count)
|
|||
|
|
|||
|
if count > color_count:
|
|||
|
return image
|
|||
|
else:
|
|||
|
return None
|
|||
|
|
|||
|
|
|||
|
# Получение списка пикселей для дальшейшего их поиска в изображении
|
|||
|
def get_mask_pixel(food_name):
|
|||
|
list_tuple = []
|
|||
|
for pixel in food_name["CMYK"]:
|
|||
|
c = percent_to_scale(pixel["C"])
|
|||
|
m = percent_to_scale(pixel["M"])
|
|||
|
y = percent_to_scale(pixel["Y"])
|
|||
|
k = percent_to_scale(pixel["K"])
|
|||
|
list_tuple.append(tuple([c, m, y, k]))
|
|||
|
return list_tuple
|
|||
|
|
|||
|
|
|||
|
# поиск блюд на изображении
|
|||
|
def find_food_on_image(cmyk_image_list, food_name):
|
|||
|
list_found_image = []
|
|||
|
for image in cmyk_image_list:
|
|||
|
mask_pixel = get_mask_pixel(food_name)
|
|||
|
image_swap_pixel = swap_pixel(image, mask_pixel, food_name["Count"])
|
|||
|
if image_swap_pixel is not None:
|
|||
|
list_found_image.append(image_swap_pixel)
|
|||
|
return list_found_image
|
|||
|
|
|||
|
|
|||
|
# точка входа в скрипт, настройка параметров и вызов функций
|
|||
|
def find_food(configs, food_name):
|
|||
|
global delta_for_color, color_swap, mode
|
|||
|
delta_for_color = percent_to_scale(configs["Delta_for_color"])
|
|||
|
color_swap = percent_to_scale_list(configs["Color_swap"])
|
|||
|
mode = configs["Mode"]
|
|||
|
cmyk_image_list = get_list_cmyk_image(configs["Path_to_food"], configs["RGB_profile"], configs["CMYK_profile"])
|
|||
|
food_on_image_list = find_food_on_image(cmyk_image_list, food_name)
|
|||
|
return food_on_image_list
|
|||
|
\end{lstlisting}
|
|||
|
|
|||
|
\subsection{Конвертер изображений}
|
|||
|
\label{app:conv}
|
|||
|
\begin{lstlisting}[language=Python,style=PyCodeStyle]
|
|||
|
from PIL import Image
|
|||
|
from PIL.ImageCms import profileToProfile
|
|||
|
|
|||
|
RGB_profile = "./AdobeICCProfiles/RGB/AdobeRGB1998.icc"
|
|||
|
CMYK_profile = "./AdobeICCProfiles/CMYK/WebCoatedSWOP2006Grade5.icc"
|
|||
|
|
|||
|
for i in range(1, 11):
|
|||
|
image_path = "./WorkFood/" + str(i) + ".jpg"
|
|||
|
im = Image.open(image_path)
|
|||
|
converted_image = profileToProfile(im, RGB_profile, CMYK_profile, outputMode='CMYK')
|
|||
|
converted_image.save("./CMYK_Food/" + str(i) + ".jpg")
|
|||
|
\end{lstlisting}
|
|||
|
|
|||
|
|
|||
|
\end{document}
|
|||
|
|