Главная > AVR, MSP430, STM32, STM8 > Преобразование HSV в RGB

Преобразование HSV в RGB

Все началось с этого микса:

Слушая его, я понял, что мне нужен новый контроллер для RGB-светодиодной ленты. Подобные штуки, если кто помнит, мной уже делалались, однако честного преобразования HSV->RGB там не было — вместо него использовался простой перебор цветов по Hue.  Теперь же пришло осознание того, что мне совершенно необходимо честное преобразование из всех трех компонент.🙂

Я пошел гуглить, и не сказать, чтобы мои поиски были совсем бесполезны — я нашел и формулы, и некоторое количество реализаций. Проблема состояла только в том, что и формулы, и реализации рассчитаны на плавающую точку, а меня, разумеется, интересовала целочисленная арифметика. Реализации в целых числах, которая меня бы устроила, я так и не нашел.

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

Ниже выложены листинги hsv2rgb.h и hsv2rgb.c — мне лень загружать их архивом.

Модуль содержит как целочисленную (с фиксированной точкой) реализацию, так и реализацию с плавающей точкой. Их компиляция включается независимо соответствующими дефайнами. Бонусом идет собственно реализация операций с фиксированной точкой.

fp_HSV2RGB(…) — реализация с плавающей точкой.

fxp_HSV2RGB(…) — реализация с фиксированной точкой.

 

hsv2rgb.h :

 

#ifndef _COLORSPACE_CONVERTER_
#define _COLORSPACE_CONVERTER_
 
/***************************************************************************************
 * HSV to RGB color space conversion routines (floating and fixed point)               *
 * Implemented by YS from https://embedderslife.wordpress.com/ and radiokot.ru forum.   *
 * You may use this code any way you want, but author gives absolutely no warranties.  *
 ***************************************************************************************/
 
//Uncomment this to compile floating point-based routine
#define INCLUDE_FLOATING_POINT_IMPLEMENTATION
//Uncomment this to compile fixed point-based routine
#define INCLUDE_FIXED_POINT_IMPLEMENTATION
 
#include <stdint.h>
#ifdef INCLUDE_FLOATING_POINT_IMPLEMENTATION
#include <math.h>
#endif
 
/************************************
 *  FIXED POINT MATH IMPLEMENTATION *
 ************************************/
 
#ifdef INCLUDE_FIXED_POINT_IMPLEMENTATION
 
#define FIXPOINT_TYPE           int32_t
//Bits for fracional part
#define FIXED_BITS              8
//Fractional part bitmask
#define FIXED_MASK              0x000000FF
//Fractional part divisor, 2^FIXED_BITS
#define FIXED_DIVISOR           255
 
//Basic arithmetic
#define FXP_ADD(A,B)            ((A) + (B))
#define FXP_SUB(A,B)            ((A) - (B))
#define FXP_MUL(A,B)            (((A)*(B)) >> FIXED_BITS)
#define FXP_DIV(A,B)            (((A) << FIXED_BITS)/(B))
 
//Get fractional part
#define FXP_FRAC(A)             ((A) & FIXED_MASK)
//Round value down
#define FXP_FLOOR(A)            ((A) & (~FIXED_MASK))
 
//Modulo operation
#define FXP_FMOD(A,B)           ((A) - FXP_MUL(FXP_FLOOR(FXP_DIV((A),(B))),(B)))
//Absolute value
#define FXP_ABS(A)              ((A)>0?(A):-(A))
 
//Make fixed point number from its floor and fractional part
#define MAKE_FXP(W,F)           (((W) << FIXED_BITS) + (((F) << FIXED_BITS) / 10))
//Convert fixed point value to stamdard float
#define FXP_TO_FLOAT(N)         ((float)((N) >> FIXED_BITS) + ((float)(N & FIXED_MASK) / FIXED_DIVISOR))
//Convert fixed point to integer
#define FXP_TO_INT(N)           ((N) >> FIXED_BITS)
 
#endif
 
//Macro to check if B1<=V<B2
#define IN_RANGE(V,B1,B2)                (((V)>=(B1)) && ((V)<(B2)))
 
#ifdef INCLUDE_FLOATING_POINT_IMPLEMENTATION
 
/*
  0<=H<360,0<=S<=1,0<=V<=1
  0<=R<=1,0<=G<=1,0<=B<=1
*/
void fp_HSV2RGB(double H,double S,double V,double *R,double *G,double *B);
#endif
#ifdef INCLUDE_FIXED_POINT_IMPLEMENTATION
 
/*
  0<=H<360,0<=S<=100,0<=V<=100
  0<=R<=255,0<=G<=255,0<=B<=255
 
  Though first three arguments have a type of FIXPOINT_TYPE,
  you don't have to use MAKE_FXP() for H, S and V here, it is done internally in correct way;
  plain integers in specified range are assumed as parameters to this function.
*/
void fixp_HSV2RGB(FIXPOINT_TYPE H,FIXPOINT_TYPE S,FIXPOINT_TYPE V,uint8_t *R,uint8_t *G,uint8_t *B);
#endif
 
#endif

 

hsv2rgb.c :

 

#include "hsv2rgb.h"
 
//See hsv2rgb.h for additional info
 
#ifdef INCLUDE_FLOATING_POINT_IMPLEMENTATION
 
void fp_HSV2RGB(double H,double S,double V,double *R,double *G,double *B)
{
  double C,X,m,_R,_G,_B;
 
  if (H>359.999)
  {
      H=0;
  }
 
  if (H<0)
  {
      H=359.999;
  }
 
  if (S>1)
  {
      S=1;
  }
 
  if (V>1)
  {
      V=1;
  }
 
  if (S<0)
  {
      S=0;
  }
 
  if (V<0)
  {
      V=0;
  }
 
  C=V*S;
  X=C*(1-fabs(fmod(H/60,2)-1));
  m=V-C;
 
  _R=0;
  _G=0;
  _B=0;
 
  if IN_RANGE(H,0,60)
  {
    _R=C;
    _G=X;
    _B=0;
  }
 
  if IN_RANGE(H,60,120)
  {
    _R=X;
    _G=C;
    _B=0;
  }
 
  if IN_RANGE(H,120,180)
  {
    _R=0;
    _G=C;
    _B=X;
  }
 
  if IN_RANGE(H,180,240)
  {
    _R=0;
    _G=X;
    _B=C;
  }
 
  if IN_RANGE(H,240,300)
  {
    _R=X;
    _G=0;
    _B=C;
  }
 
  if IN_RANGE(H,300,360)
  {
    _R=C;
    _G=0;
    _B=X;
  }
 
  (*R)=_R+m;
  (*G)=_G+m;
  (*B)=_B+m;
}
 
#endif
 
#ifdef INCLUDE_FIXED_POINT_IMPLEMENTATION
 
void fixp_HSV2RGB(FIXPOINT_TYPE H,FIXPOINT_TYPE S,FIXPOINT_TYPE V,uint8_t *R,uint8_t *G,uint8_t *B)
{
  FIXPOINT_TYPE C,X,m,_R,_G,_B;
 
  H=H%360;
 
  if (S>100)
  {
      S=100;
  }
 
  if (V>100)
  {
      V=100;
  }
 
  if (S<0)
  {
      S=0;
  }
 
  if (V<0)
  {
      V=0;
  }
 
  H=MAKE_FXP(H,0);
  S=FXP_DIV(MAKE_FXP(S,0),MAKE_FXP(100,0));
  V=FXP_DIV(MAKE_FXP(V,0),MAKE_FXP(100,0));
 
  C=FXP_MUL(V,S);
  X=FXP_MUL(C,MAKE_FXP(1,0)-FXP_ABS(FXP_FMOD(FXP_DIV(H,MAKE_FXP(60,0)),MAKE_FXP(2,0))-MAKE_FXP(1,0)));
  m=V-C;
 
  _R=0;
  _G=0;
  _B=0;
 
  H=FXP_TO_INT(H);
 
  if IN_RANGE(H,0,60)
  {
    _R=C;
    _G=X;
    _B=0;
  }
 
  if IN_RANGE(H,60,120)
  {
    _R=X;
    _G=C;
    _B=0;
  }
 
  if IN_RANGE(H,120,180)
  {
    _R=0;
    _G=C;
    _B=X;
  }
 
  if IN_RANGE(H,180,240)
  {
    _R=0;
    _G=X;
    _B=C;
  }
 
  if IN_RANGE(H,240,300)
  {
    _R=X;
    _G=0;
    _B=C;
  }
 
  if IN_RANGE(H,300,360)
  {
    _R=C;
    _G=0;
    _B=X;
  }
 
  (*R)=FXP_TO_INT(FXP_MUL(_R+m,MAKE_FXP(255,0)));
  (*G)=FXP_TO_INT(FXP_MUL(_G+m,MAKE_FXP(255,0)));
  (*B)=FXP_TO_INT(FXP_MUL(_B+m,MAKE_FXP(255,0)));
}
 
#endif

 

Рубрики:AVR, MSP430, STM32, STM8

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s