图片颜色处理

时间:2022-12-22 14:19:27

背景

背景叙述的是我为什么要做颜色处理以及整个思考过程,有些流水账,可不看。

任务需求

拍照,获取图片中固定一块区域的颜色,判断是不是红色。

思考过程

知道这个任务的时候,首先对图像处理不熟悉,没做过,第二,任务需求比较模糊。
然后我了解到拍照出来的图像格式为rgb565,那么我需要拿到某一块区域的颜色,我就需要去了解rgb565图片格式,一个像素占几个字节,能取到想要的范围后发现,我这一片区域需要取哪个颜色?这时候我了解到了两个滤波算法,分别是中值滤波和均值滤波。

这时候遇到了一个问题,我对一片区域做中值滤波或者均值滤波,假设这片区域需要滤波9个像素点,那么滤波涉及到的像素点就是5*5 = 25个像素点,那么滤波到后面,肯定包含之前滤波后的像素点,那么这时候使用的值是使用滤波前的还是滤波后的。说的比较绕,如下图所示
下图为滤波前的,红色框代表的是需要滤波的像素点。
图片颜色处理
下图为红框第一位也就是63中值滤波后为52,那么准备第二位滤波也就是21滤波,我该拿的是63还是拿52,63代表的是滤波前的,52代表是滤波后的基础上滤波的。我查了半天没找到哪里有依据,我师傅告诉我说是拿滤波前也就是63再滤波。
图片颜色处理
为什么两种滤波,我选择了中值滤波没选择均值滤波?
这时候我去了解了一下这两种滤波方式的区别以及优缺点。
我选择使用中值滤波是因为如果使用均值滤波的话,我需要滤波的一片像素有一部分噪声,那么就会导致均值滤波出来的颜色跟肉眼看这一片区域的颜色差别很大,中值滤波就不会。

这时候我拿到了我想要的rgb数据,我发现我需要与哪个颜色数据对比,因为不同的饱和度和亮度啥的都会导致每次拿的数据不一样,那我怎么进行颜色比较?然后我首先想到的是不是有个范围,那么这个范围是多少,如何定义的?

这时候一直百度,以及询问别人,了解到了一个色差的概念,经过百度色差相关的知识了解到了hsv,那么rgb565如何转成hsv?百度了一下有相关文章有写。然后对比两个颜色的hsv,就会出来一个数值,那么这个数值设定多少才算是肉眼看上去是同一个颜色?经过了百度以及测试,我赶紧差值在3.5左右是可以接收的。

下面贴的代码主要就是RGB565和RGB888之间的转换,RGB888转换成HSV,以及色差比较的代码。

两个滤波算法代码就没贴上了,百度挺多的。

#include "stdio.h"
#include "stdint.h"
#include "math.h"

#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max3(a, b, c) (((a) > (b) ? (a) : (b)) > (c) ? ((a) > (b) ? (a) : (b)) : (c))
#define min3(a, b, c) (((a) < (b) ? (a) : (b)) < (c) ? ((a) < (b) ? (a) : (b)) : (c))

typedef struct
{
    uint16_t r;
    uint16_t g;
    uint16_t b;
    uint32_t color;
} color_rgb_t;
typedef struct
{
    double h;
    double s;
    double v;
} color_hsv_t;

/*********************************************************************************************
RGB转化到HSV的算法:
    max=max(R,G,B);
    min=min(R,G,B);
    V=max(R,G,B);
    S=(max-min)/max;
    if (R = max) H =(G-B)/(max-min)* 60;
    if (G = max) H = 120+(B-R)/(max-min)* 60;
    if (B = max) H = 240 +(R-G)/(max-min)* 60;
    if (H < 0) H = H + 360;
***********************************************************************************************/

/**
 * @brief   RGB颜色空间 转  HSV颜色空间
 * @param   rgb:RGB颜色空间参数
 * @param   hsv:HSV颜色空间参数
 * @note    The R,G,B values are divided by 255 to change the range from 0..255 to 0..1:
 * @return  none
 */
void rgb2hsv(color_rgb_t *rgb, color_hsv_t *hsv)
{
    float max, min, delta = 0;
    float r = (float)((float)((int)rgb->r) / 255);
    float g = (float)((float)((int)rgb->g) / 255);
    float b = (float)((float)((int)rgb->b) / 255);

    max = max3(r, g, b);
    min = min3(r, g, b);
    // printf("rgb2hsv max = %d,min = %d\n", max, min);
    delta = (max - min);

    // printf("r:%f, g:%f, b:%f\n", r, g, b);

    if (delta == 0)
    {
        hsv->h = 0;
    }
    else
    {
        if /**/ (r == max)
        {
            hsv->h = ((g - b) / delta) * 60;
        }
        else if (g == max)
        {
            hsv->h = 120 + (((b - r) / delta) * 60);
        }
        else if (b == max)
        {
            hsv->h = 240 + (((r - g) / delta) * 60);
        }

        if (hsv->h < 0)
        {
            hsv->h += 360;
        }
    }

    if (max == 0)
    {
        hsv->s = 0;
    }
    else
    {
        hsv->s = (float)(delta / max);
    }

    hsv->v = max;

    // printf("rgb2hsv HSV,H:%lf,S:%lf,V:%lf\n", hsv->h, hsv->s, hsv->v);

    rgb->color = (unsigned int)(((rgb->r & 0xff) << 16) | ((rgb->g & 0xff) << 8) | (rgb->b & 0xff));
    printf("color:%x,r:%x,g:%x,b:%x",rgb->r,rgb->g,rgb->b);
}

double HSVDistance(color_hsv_t *hsv_1, color_hsv_t *hsv_2)
{
    double H_1 = hsv_1->h;
    double S_1 = hsv_1->s;
    double V_1 = hsv_1->v;
    double H_2 = hsv_2->h;
    double S_2 = hsv_2->s;
    double V_2 = hsv_2->v;

    double R = 100;
    double angle = 30;

    double h = R * cos(angle / 180 * M_PI);
    double r = R * sin(angle / 180 * M_PI);

    double x1 = r * V_1 * S_1 * cos(H_1 / 180 * M_PI);
    double y1 = r * V_1 * S_1 * sin(H_1 / 180 * M_PI);
    double z1 = h * (1 - V_1);

    double x2 = r * V_2 * S_2 * cos(H_2 / 180 * M_PI);
    double y2 = r * V_2 * S_2 * sin(H_2 / 180 * M_PI);
    double z2 = h * (1 - V_2);

    double dx = x1 - x2;
    double dy = y1 - y2;
    double dz = z1 - z2;

    return sqrt(dx * dx + dy * dy + dz * dz);
}

#define RGB888_RED 0x00ff0000
#define RGB888_GREEN 0x0000ff00
#define RGB888_BLUE 0x000000ff

#define RGB565_RED 0xf800
#define RGB565_GREEN 0x07e0
#define RGB565_BLUE 0x001f

unsigned short RGB888ToRGB565(unsigned int n888Color)
{
    unsigned short n565Color = 0;

    unsigned char cRed = (n888Color & RGB888_RED) >> 19;
    unsigned char cGreen = (n888Color & RGB888_GREEN) >> 10;
    unsigned char cBlue = (n888Color & RGB888_BLUE) >> 3;

    n565Color = (cRed << 11) + (cGreen << 5) + (cBlue << 0);
    return n565Color;
}

unsigned int RGB565ToRGB888(unsigned short n565Color)
{
    unsigned int n888Color = 0;

    unsigned char cRed = (n565Color & RGB565_RED) >> 8;
    unsigned char cGreen = (n565Color & RGB565_GREEN) >> 3;
    unsigned char cBlue = (n565Color & RGB565_BLUE) << 3;

    n888Color = (cRed << 16) + (cGreen << 8) + (cBlue << 0);
    return n888Color;
}

int main()
{
    // double hsv_1;
    // double hsv_2;

    color_rgb_t rgb_1 = {
        .r = 117,
        .g = 137,
        .b = 44,
    };
    color_rgb_t rgb_2 = {
        .r = 186,
        .g = 203,
        .b = 187,
    };

    color_hsv_t hsv_1;
    color_hsv_t hsv_2;

    rgb2hsv(&rgb_1, &hsv_1);
    rgb2hsv(&rgb_2, &hsv_2);

    printf("RGB,R:%d,G:%d,B:%d,COLOR:0X%x\n", rgb_1.r, rgb_1.g, rgb_1.b, rgb_1.color);
    printf("HSV,H:%lf,S:%lf,V:%lf\n", hsv_1.h, hsv_1.s, hsv_1.v);

    printf("=======================================\n");
    printf("HSV distance:%lf\n", HSVDistance(&hsv_1, &hsv_2));

    return 0;
}

参考文章:HSV颜色空间和RGB颜色空间相互转换C语言实现