Hsv Backpage

Hsv Backpage



💣 👉🏻👉🏻👉🏻 ALL INFORMATION CLICK HERE 👈🏻👈🏻👈🏻

































Удаленная работа для IT-специалистов

В качестве хобби я занимаюсь светодиодным реквизитом и столкнулся с интересной задачей — показать что-то «красивое» на управляемой светодиодной ленте вместо традиционной радуги, не тратя на это половину памяти микроконтроллера и значительную часть процессорного времени.

Пиксели светодиодной ленты отличаются от пикселей экрана отсутствием фоновой подсветки. Чёрный пиксель не будет выглядеть «чёрным» — он сольётся с фоном, а в движении фактически будет «прозрачным», но если добавить хотя бы единицу к любому цветовому каналу — этот пиксель будет светиться. В свою очередь, «серый» пиксель от белого будет отличаться только яркостью и будет казаться более тусклым, но всё же именно «белым».

Хранится и передаётся цвет пикселя в 24-bit RGB, но значительная часть этого цветового диапазона (ненасыщенные и яркие цвета) не слишком репрезентабельна в отдельных светодиодах. Кроме того, строить симпатичные градиенты в модели RGB не получится — смешивание RGB-цветов даёт не интуитивно-очевидный результат (жёлтый + синий = серый, а хочется — зелёный). Модели HSL и HSV подойдут лучше, но стандартные реализации используют нецелочисленную арифметику. Удобно будет использовать модель, которая сможет компактно хранить параметры цвета и быстро считать их RGB-значения, не используя числа с плавающей запятой и деление на произвольное число — речь идёт о микроконтроллере и сложные алгоритмы нам ни к чему, а деление (кроме небольших степеней двойки) и вовсе противопоказано.


Решение

Для своих нужд я использую модель HSV (HSB) с определёнными диапазонами для каждой из координат (немного magic numbers).


Hue — тон, цикличная угловая координата.
Value, Brightness — яркость, воспринимается как альфа-канал, при пиксель не светится, при — светится максимально ярко, в зависимости от и .
Saturation. С отсутствием фона, значения дадут не серый цвет, а белый разной яркости, поэтому параметр можно называть Whiteness — он отражает степень «белизны» цвета. При цвет полностью определяется Hue, при цвет пикселя будет белым.

Математика модели строится на целочисленном делении на одну шестую максимального значения тона (размер одного сектора), поэтому в качестве удобно взять максимальное значение равное , например 48 или 96. Это позволит удобно вычислять RGB-цвет, а значение меньше 128 позволит строить градиент, который несколько раз содержит полный цветовой круг. В моделях HSV/HSL , в MS Paint — 240, в некоторых библиотеках — 255.

При выборе максимальных значений и перемножение даёт результат, лежащий в диапазоне .

Минимальная конфигурация HSV, простая в расчёте и ограниченная 8-битными значениями, при диапазонах , и даёт нам цвета, часть из которых практически повторяется, а часть отстоит довольно далеко друг от друга (справедливости ради замечу, что этим грешат все цветовые модели, оперирующие H — натянуть три стороны куба на конус не исказив длины не смог бы и Меркатор). Цифра кажется скудной в сравнении с 24-битным цветом в типичном мониторе (16,7 млн уникальных цветов), но её достаточно, чтобы разнообразить светодиодный реквизит, в котором раньше и семь цветов вместо одного зачастую были приятным бонусом. Координаты цвета в такой модели можно хранить в двух байтах.

Разумеется, разрешение HSV можно и нужно повышать до удобного. Я использую и 96 тонов, что даёт уже 27,6 тысяч оттенков. Пример кода с такими параметрами (конфигурация модели — max_value, max_whiteness, sixth_hue):


Код


typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
} RGB_t;

typedef struct {
uint8_t h;
uint8_t s;
uint8_t v;
} HSV_t;

const uint8_t max_whiteness = 15;
const uint8_t max_value = 17;

const uint8_t sixth_hue = 16;
const uint8_t third_hue = sixth_hue * 2;
const uint8_t half_hue = sixth_hue * 3;
const uint8_t two_thirds_hue = sixth_hue * 4;
const uint8_t five_sixths_hue = sixth_hue * 5;
const uint8_t full_hue = sixth_hue * 6;

inline RGB_t rgb(uint8_t r, uint8_t g, uint8_t b) {
return (RGB_t) {r, g, b};
}

inline HSV_t hsv(uint8_t h, uint8_t s, uint8_t v) {
return (HSV_t) {h, s, v};
}

const RGB_t black = {0, 0, 0};

RGB_t hsv2rgb(HSV_t hsv) {
if (hsv.v == 0) return black;

uint8_t high = hsv.v * max_whiteness;//channel with max value
if (hsv.s == 0) return rgb(high, high, high);

uint8_t W = max_whiteness - hsv.s;
uint8_t low = hsv.v * W;//channel with min value
uint8_t rising = low;
uint8_t falling = high;

uint8_t h_after_sixth = hsv.h % sixth_hue;
if (h_after_sixth > 0) {//not at primary color? ok, h_after_sixth = 1..sixth_hue - 1
uint8_t z = hsv.s * uint8_t(hsv.v * h_after_sixth) / sixth_hue;
rising += z;
falling -= z + 1;//it's never 255, so ok
}

uint8_t H = hsv.h;
while (H >= full_hue) H -= full_hue;

if (H < sixth_hue) return rgb(high, rising, low);
if (H < third_hue) return rgb(falling, high, low);
if (H < half_hue) return rgb(low, high, rising);
if (H < two_thirds_hue) return rgb(low, falling, high);
if (H < five_sixths_hue) return rgb(rising, low, high);
return rgb(high, low, falling);
}



P.S.
TeX-коды в топик включил в порядке эксперимента. Если есть способ делать это удобнее или правильнее — намекните в ПМ.
Если будет интересно — могу отдельно пояснить особенности обсчёта HSV, в частности механику функций rising/falling и функцию обратного расчёта в «такой» HSV.
Укажите причину минуса, чтобы автор поработал над ошибками
Присылаем лучшие статьи раз в месяц
Скоро на этот адрес придет письмо. Подтвердите подписку, если всё в силе.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Ееее. Ура HSV на светодиодах!
Когда делал лампу настроения, по одной из статей, ужаснулся коду на выбор цвета и количеству условий в RGB. Перешел на HSV и код стал значительно проще.
Я правда не стал все так круто продумывать и сделал Hue-0...360, Sat-0...255, Val-0...255 и арифметика была не целочисленная. Но я по контроллерам не спец и оптимизация там не понадобилась. Заработало и хорошо.
Я когда делал HSV сделал Hue 0..1530 (255 * 6) и получилась целочисленная арифметика. Основная идея была такая: 6 участков показывает рост и спад вторичных цветов, если они будут расти 0..255 и спадать 0..255, то мало того что будут получаться целые числа, так ещё и плавность повысится.

P.S. Я делал с S = 255, V = 255, т. е. менять можно было только оттенок, но переделать не очень сложно.
void set_led_color(unsigned int hue)
{
// sector specifies which colors are primary and secondary
unsigned char sector = hue / 0xff;

// primary color is always full
const unsigned char primary = 0xff;

// calculate secondary color value from hue
unsigned char secondary = abs(sector % 2 * 0xff - hue % 0xff);

// тут идёт 6 кейсов sector и установка PWM на полученные значения
}

Описанный вами случай подходит и под HSL при L = Lmax/2, S = Smax.

Действительно, при S = 255, V = 255 удобно.

По поводу кода:
Странно, что у вас вместо byte используется unsigned char.
Вместо sector % 2 можно использовать (sector & 1), вместо hue % 255 — byte (hue).

По поводу «переделать несложно»:
На самом деле при этом возникают неприятные эффекты. V — это то значение на которое идёт домножение primary и secondary. Как только оно не 255 — появляется дополнительное деление/умножение в secondary. Более того, если вы начнёте уменьшать S — третья компонента цвета будет расти пропорционально (255-S)*V/255, и это же слагаемое появится и в secondary, а то что там было придётся домножать на S/255. В общем, при любом минимальном понижении S и V такое высокое разрешение по тону станет ненужным. А под конкретную задачу — реализация удачная, не спорю :)

Не берусь сходу дописать код для HSV, но там могут случиться небольшие потери значений при делении.
Я сейчас статью на хабру пишу, где HSL-пространство, но за HSV однозначно привет однополчанам! Hue вы интересно считаете.
У HSL есть та же проблема, что и у HSV но в два раза сильнее: в HSL три стороны куба проецируются на конус, а три — на круг, образуя его основание, а у HSL — два конуса. Фактически, у вас две зоны (в окрестностях L = 0 и L = Lmax), где будет много слабо отличающихся друг от друга значений.
TeX-коды в топик включил в порядке эксперимента. Если есть способ делать это удобнее или правильнее — намекните в ПМ.

Что такое ПМ я не знаю, но если уж вы используйте TeX, то желательно соблюдать математические традиции вместо программерского жаргона типа двоеточия для диапазона и звёздочки для умножения.

__attribute__ ((__packed__)) — лишние. Eсли вам нужна оптимизация по размеру, то разумно так и указать это в опциях для компилятора. «Пакование» структур в явном виде применяется в более специфичных случаях (например, когда нужно обеспечить бинарную переносимость структуры в независимости от опций компилятора).

В-третьих, причём тут привязка к AVR и, тем более, Arduino?

Ну это я попридирался просто. Сама идея использования пространства HSV вместо RGB в некоторых случаях разобрана и, надеюсь, защитит многих от изобретения своих велосипедов.
ПМ это личное сообщение — private message.

С математическими традициями у меня проблема — и тройка в дипломе, и с FFT уже месяц разбираюсь, чтобы понять. Но попробую учесть Ваш совет.

__attribute__ ((__packed__)) для RGB_t прописан для совместимости вот с этим кодом — ассемблерной вставкой для time-critical вывода значений на светодиодную ленту (см. также).

Arduino упомянут как самый популярный девайс на хабре, использующий AVR.
Собственно, я не уверен что тот же код будет актуальным для контроллеров на ARM — вряд ли там есть необходимость экономить память, циклы процессора, отказываться от float-point и делений (из того же блога — про скорость деления на AVR).


Arduino упомянут как самый популярный девайс на хабре, использующий AVR [...]

Ваш код — на чистейшем С (не считая __attribute__), поэтому совершенно ни к чему не привязан, да и мир не заканчивается на AVR и ARM. А FPU лишены большинство микроконтроллеров.


__attribute__ ((__packed__)) для RGB_t прописан для совместимости вот с этим кодом [...]

Опять же — какая разница для чего вы его там используете, если в статье к этому привязки нет. Код у вас просто демонстрирует переход от HSV к RGB модели c целочисленной арифметикой.

Короче, зачем искусственно ограничивать общность статьи?
Исправил заголовок топика и убрал packed, спасибо за замечание.
А нельзя ли осветить железную часть проекта? Схему? Фото? И возможно ли собрать это дома? Я так понимаю, что у Arduino три аналоговых вывода, которые усиливаются транзисторами, но я, например, никогда не смогу спроектировать такую схему (с обвязками и разделением по питанию). По этому было бы интересно посмотреть и другую сторону.
Так как электротехник из меня слабый — схем нет, я ничего не монтирую. Проводами соединена Arduino, радиомодуль nRF24L01+, метр ленты на WS2811 и питание.

Фото будет, даже видео, но вряд ли на хабре — светодиодный жонглёрский реквизит тема очень узкая, мне кажется фаер-шоу и похожие искусства вряд ли будут интересны хабровчанам.
Не стоит недооценивать хабр :) Если там будут интересные технические решения, то такие узкоспецифические проекты вызывают еще больший интерес.
Из интересных технических решений осталось использование в качестве альтернативной цветовой модели давно забытых Web-safe colors и градиентов на их основе, они пришлись ко двору :)
Для Arduino использую HSV2RGB
Ссылка на форум где можно скачать HSV2RGB
Там сразу несколько реализаций. Первые две (_360, _384) используют double и деление, _Binary — только деление, _Adv и _Adv1 — чистые, _Adv2 — снова деление.
Adv и Adv1 можно дооптимизировать по точности, коду и скорости разом, но если вас они устраивают — особой нужды в этом нет :)
Я Ваш код еще не успел попробовать в работе
Завтра буду на работе тестировать.

А как насчёт цветовой модели YCbCr? Подойдёт ли она для решения вашей задачи? Она более стандартная и используются как в аналоговой части, так и в кодировке цифрового видео. Там преобразования в RGB и обратно намного проще.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

1. OpenCV шаг за шагом. Введение
2. Установка
3. Hello World
4. Загрузка картинки.
5. Вывод видео
6. Ползунок
7. Захват видео с камеры
8. Запись видео
9. События от мышки
10. Обработка изображения — сглаживание
11. Обработка изображения — изменение размеров
12. ROI — интересующая область изображения
13. Типы данных OpenCV
14. Матрица
15. Сохранение данных в XML
16. Генерация случайных чисел
17. Обработка изображения — морфологические преобразования
18. Обработка изображения — морфологические преобразования 2
19. Обработка изображения — заливка части изображения
20. Обработка изображения — альфа-смешивание
21. Обработка изображения — пороговое преобразование
22. Поиск объекта по цвету — RGB
23. Поиск объекта по цвету. Цветовое пространство HSV.

Успели соскучиться? Всё суета-суета :) А ещё и Новый Год приближается :)
Как понятно из названия, продолжим рассмотрение выделения объектов по цвету. И в конце, в качестве бонуса, рассмотрим пример детектирования кожи :)

На прошлом шаге было рассмотрено выделение объекта по его RGB-цвету, но использовать модель HSV намного удобнее, т.к. координаты этой цветовой модели выбраны с учетом человеческого восприятия.

HSV (англ. Hue, Saturation, Value — тон, насыщенность, значение) — цветовая модель, в которой координатами цвета являются:
* Hue — цветовой тон, (например, красный, зелёный или сине-голубой). Варьируется в пределах 0—360°, однако иногда приводится к диапазону 0—100 или 0—1.
* Saturation — насыщенность. Варьируется в пределах 0—100 или 0—1. Чем больше этот параметр, тем «чище» цвет, поэтому этот параметр иногда называют чистотой цвета. А чем ближе этот параметр к нулю, тем ближе цвет к нейтральному серому.
* Value (значение цвета) или Brightness — яркость. Также задаётся в пределах 0—100 и 0—1.




Модель HSV была создана Элви Реем Смитом, одним из основателей Pixar, в 1978 году. Она является нелинейным преобразованием модели RGB.


Эта модель очень удобна для поиска на изображении объектов по цвету (и яркости). Например, пятно от лазерной указки, цветовые маркеры или просто объекты с выделяющимся цветом.
Преобразовать изображение из RGB в HSV очень просто:

// создаём изображения
IplImage* hsv = cvCreateImage( cvGetSize(src), 8, 3 );
IplImage* h_plane = cvCreateImage( cvGetSize(src), 8, 1 );
IplImage* s_plane = cvCreateImage( cvGetSize(src), 8, 1 );
IplImage* v_plane = cvCreateImage( cvGetSize(src), 8, 1 );
// конвертируем в HSV
cvCvtColor( src, hsv, CV_BGR2HSV );
// разбиваем на каналы
cvCvtPixToPlane( hsv, h_plane, s_plane, v_plane, 0 );


Т.е. сначала просто создаются 4 изображения: одно для хранения изображения в формате HSV и три для последующего разделения изображения на отдельные каналы H, S и V.

Ниже приводится код полезной утилиты, которая может быть полезна для определения минимальных и максимальных значений каналов HSV для выделения объекта на изображении.
Программа преобразует изображение в формат HSV и затем используя ползунки и функцию cvInRangeS() формирует в дополнительных оконах «H range» бинарное изображение, которое получается из тех пикселей канала, значение которых больше Hmin и меньше Hmax.
Далее для всех трёх получившихся пороговых изображений выполняется операция логического И (т.е. в результирующем окне «hsv and» белыми будут только те пиксели, которые есть у всех трёх пороговых изображений).


//
// конвертирует изображение в HSV
// и позволяет подобрать параметры
// Hmin, Hmax, Smin, Smax, Vmin, Vmax
// для выделения нужного объекта
//
// robocraft.ru
//
#include
#include
#include
#include

IplImage* image = 0;
IplImage* dst = 0;

// для хранения каналов HSV
IplImage* hsv = 0;
IplImage* h_plane = 0;
IplImage* s_plane = 0;
IplImage* v_plane = 0;
// для хранения каналов HSV после преобразования
IplImage* h_range = 0;
IplImage* s_range = 0;
IplImage* v_range = 0;
// для хранения суммарной картинки
IplImage* hsv_and = 0;

int Hmin = 0;
int Hmax = 256;

int Smin = 0;
int Smax = 256;

int Vmin = 0;
int Vmax = 256;

int HSVmax = 256;

//
// функции-обработчики ползунков
//
void myTrackbarHmin(int pos) {
Hmin = pos;
cvInRangeS(h_plane, cvScalar(Hmin), cvScalar(Hmax), h_range);
}

void myTrackbarHmax(int pos) {
Hmax = pos;
cvInRangeS(h_plane, cvScalar(Hmin), cvScalar(Hmax), h_range);
}

void myTrackbarSmin(int pos) {
Smin = pos;
cvInRangeS(s_plane, cvScalar(Smin), cvScalar(Smax), s_range);
}

void myTrackbarSmax(int pos) {
Smax = pos;
cvInRangeS(s_plane, cvScalar(Smin), cvScalar(Smax), s_range);
}

void myTrackbarVmin(int pos) {
Vmin = pos;
cvInRangeS(v_plane, cvScalar(Vmin), cvScalar(Vmax), v_range);
}

void myTrackbarVmax(int pos) {
Vmax = pos;
cvInRangeS(v_plane, cvScalar(Vmin), cvScalar(Vmax), v_range);
}

int main(int argc, char* argv[])
{
// имя картинки задаётся первым параметром
char* filename = argc == 2 ? argv[1] : "Image0.jpg";
// получаем картинку
image = cvLoadImage(filename,1);

printf("[i] image: %s\n", filename);
assert( image != 0 );

// создаём картинки
hsv = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 3 );
h_plane = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
s_plane = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
v_plane = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
h_range = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
s_range = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
v_range = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
hsv_and = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
// конвертируем в HSV
cvCvtColor( image, hsv, CV_BGR2HSV );
// разбиваем на отельные каналы
cvCvtPixToPlane( hsv, h_plane, s_plane, v_plane, 0 );

//
// определяем минимальное и максимальное значение
// у каналов HSV
double framemin=0;
double framemax=0;

cvMinMaxLoc(h_plane, &framemin, &framemax);
printf("[H] %f x %f\n", framemin, framemax );
Hmin = framemin;
Hmax = framemax;
cvMinMaxLoc(s_plane, &framemin, &framemax);
printf("[S] %f x %f\n", framemin, framemax );
Smin = framemin;
Smax = framemax;
cvMinMaxLoc(v_plane, &framemin, &framemax);
printf("[V] %f x %f\n", framemin, framemax );
Vmin = framemin;
Vmax = framemax;

// окна для отображения картинки
cvNamedWindow("original",CV_WINDOW_AUTOSIZE);
cvNamedWindow("H",CV_WINDOW_AUTOSIZE);
cvNamedWindow("S",CV_WINDOW_AUTOSIZE);
cvNamedWindow("V",CV_WINDOW_AUTOSIZE);
cvNamedWindow("H range",CV_WINDOW_AUTOSIZE);
cvNamedWindow("S range",CV_WINDOW_AUTOSIZE);
cvNamedWindow("V range",CV_WINDOW_AUTOSIZE);
cvNamedWindow("hsv and",CV_WINDOW_AUTOSIZE);

cvCreateTrackbar("Hmin", "H range", &Hmin, HSVmax, myTrackbarHmin);
cvCreateTrackbar("Hmax", "H range", &Hmax, HSVmax, myTrackbarHmax);
cvCreateTrackbar("Smin", "S range", &Smin, HSVmax, myTrackbarSmin);
cvCreateTrackbar("Smax", "S range", &Smax, HSVmax, myTrackbarSmax);
cvCreateTrackbar("Vmin", "V range", &Vmin, HSVmax, myTrackbarVmin);
cvCreateTrackbar("Vmax", "V range", &Vmax, HSVmax, myTrackbarVmax);

//
// разместим окна по рабочему столу
//
if(image->width <1920/4 && image->height<1080/2){
cvMoveWindow("original", 0, 0);
cvMoveWindow("H", image->width+10, 0);
cvMoveWindow("S", (image->width+10)*2, 0);
cvMoveWindow("V", (image->width+10)*3, 0);
cvMoveWindow("hsv and", 0, image->height+30);
cvMoveWindow("H range", image->width+10, image->height+30);
cvMoveWindow("S range", (image->width+10)*2, image->height+30);
cvMoveWindow("V range", (image->width+10)*3, image->height+30);
}

while(true){

// показываем картинку
cvShowImage("original",image);

cvShowImage( "H", h_plane );
cvShowImage( "S", s_plane );
cvShowImage( "V", v_plane );

cvShowImage( "H range", h_range );
cvShowImage( "S range", s_range );
cvShowImage( "V range", v_range );

// складываем
cvAnd(h_range, s_range, hsv_and);
cvAnd(hsv_and, v_range, hsv_a
OpenCV: HSV и поиск объектов по цвету - IT Notes
Оптимизация преобразования HSV в RGB для микроконтроллеров / Хабр
23. OpenCV шаг за шагом. Поиск объекта... / RoboCraft. Роботы? Это просто!
Вирус простого герпеса. Симптомы и диагностика.
Простой герпес HSV -1, HSV-2: симптомы, диагностика, лечение | mfarma.ru
Houston Masajes Para Adultos
Escort Guide
Escorts In Sanford Nc
Hsv Backpage
h_720" width="550" alt="Hsv Backpage" title="Hsv Backpage">c_limit/backpage3.jpg" width="550" alt="Hsv Backpage" title="Hsv Backpage">

Report Page