LINUX下简单实现ISP图像处理从RAW到RGB,BMP算法、RGB到JPEG库的使用(二)

        接上期图片处理后,还有图像进行色彩空间矩阵变换,伽马矫正,白平衡没讲。本期就来说说这三个操作对图片的处理作用以及效果。

白平衡:

        白平衡是指相机或摄像机为了使图像中的白色看起来是真正的白色,需要对相机的感光元件进行调节的过程。白平衡的主要作用是消除光源色温对图像色彩的影响,确保图像中的白色看起来是真正的白色,从而使整个图像的色彩更加准确和自然。如果白平衡设置不正确,图像中的颜色就会偏色,影响图像的色彩还原效果。

        白平衡有多种算法,可以在RAW域算白平衡系数,也可以在RGB域算白平衡系数,白平衡有两个系数GainA,GainB。白平衡的GainA和GainB是指在进行白平衡调节时,调整红色和蓝色通道的增益值。GainA代表红色通道的增益值,GainB代表蓝色通道的增益值。通过调整这两个通道的增益值,可以使得白色看起来更加真实自然,从而达到更好的色彩还原效果。 在进行白平衡调节时,相机会根据所拍摄的场景的光源色温来自动调整GainA和GainB的值。对于不同的光源,需要进行不同的白平衡调节来达到最佳的色彩还原效果。

        例如,在拍摄日落时,为了强调日落的暖色调,可以将色温调低,使图像呈现出比较暖的色调;在拍摄蓝天白云的风景时,为了突出蓝色的色调,可以将色温调高,使图像呈现出比较冷的色调。

        下面我们就来说说白平衡系数的计算,这里采用的方法是在RGB域计算,然后再返回RAW域,对RAW域图像进行白平衡处理。下面请看白平衡的计算方法。

第一步:求出每个点的亮度Y

第二步:对Y进行分类求权重,不同亮度所占的百分比可以自己调整

第三步:判断它是否接近灰色

第四步:统计灰色的点求 GainR GainB

        由下面代码可以看出是西安调到RGB域去算出白平衡系数,然后再次跳回到RAW域算白平衡。下面是主函数的部分代码:

#if 1
    image = thread_read_raw(RAW_FILE_NAME,5600,5600);//读取一张图片
    seek_bad_Pixel(image);//去坏点
    Black_Level_Correction_respective(image);//四通道黑电平
    Shadow_Correction(image, 0.2);//阴影矫正
    vector<vector<Pixel>> RGB = color_interpolation(image);//跳转到RGB域
    Return_GainR_GrainB RB = calculate_white_balance_nums(RGB);//获取白平衡系数
    white_balance(RB,image);//用拿到的系数做白平衡
    RGB = color_interpolation(image);//重新跳转到RGB域
    //matmul3x3_3x1(RGB); //色彩空间矩阵校正
    //reduce_red(RGB);//去过曝光过度产生的红斑
    //Gamma_correction(RGB,1.1); //伽马矫正
    //cruculation_R_G_B(RGB);
    //cout << RB.GainB << "  " << RB.GainR <<endl;
#endif
#if 1
    Raw_to_Bmp_Pixel16_Enablelow10(RGB,timer(BMP_FILE_NAME,(const char *)"bmp"));
#endif

        白平衡参数计算函数:

Return_GainR_GrainB calculate_white_balance_nums(vector<vector<Pixel>> &image)
{
    // 开辟返回值结构体
    Return_GainR_GrainB return_GB;
    // 算出传入RGB数组的大小
    int width = image.size(), height = image[0].size();
    // 返回一个八位的值
    uint8_t balance_num = 0;
    double GainR = 0, GainB = 0;
    // 64位累加x像素值防止溢出 开辟三个结构体 存三组数据总和
    RGB_total rgb_total[3];
    rgb_total[0].total = 0, rgb_total[1].total = 0, rgb_total[2].total = 0;
    rgb_total[0].R_total = 0, rgb_total[1].R_total = 0, rgb_total[2].R_total = 0;
    rgb_total[0].G_total = 0, rgb_total[1].G_total = 0, rgb_total[2].G_total = 0;
    rgb_total[0].B_total = 0, rgb_total[1].B_total = 0, rgb_total[2].B_total = 0;
    // memset(&rgb_total, 0, sizeof(rgb_total)*3);
    //  创建VUV分量数组
    vector<vector<YUV_float>> yuvImage(width, vector<YUV_float>(height));
    // 循环遍历所有像素
    // 横轴
    int count = 0;
    for (int i = 0; i < width; i++)
    {
        // 竖轴
        for (int j = 0; j < height; j++)
        {
            // 对每个像素利用矩阵算出它的 Y 值并且存入YUV_float

            yuvImage[i][j].YUV_y = (double)Y((double)image[i][j].r, (double)image[i][j].g, (double)image[i][j].b);
            // cout << yuvImage[i][j].YUV_y << "  " <<(double)image[i][j].r << "  " <<(double)image[i][j].g << " "<<(double)image[i][j].b<< endl;
            // cout << (double)image[i][j].r /(double)image[i][j].g  <<" " <<(double)image[i][j].b /(double)image[i][j].g<<endl;
            // cout << (double)((double)image[i][j].r / yuvImage[i][j].YUV_y) <<" " <<(double)((double)image[i][j].b / yuvImage[i][j].YUV_y)<<endl;
            // 判断灰度范围
            // 改成大于0.6左右 R/G约等于0.6
            if (yuvImage[i][j].YUV_y != 0)
            {
                // cout << ((double)image[i][j].r / (double)image[i][j].g) << endl;
                // if (((double)((double)image[i][j].r / (double)image[i][j].g) > R_Y) && ((double)((double)image[i][j].b / (double)image[i][j].g) > B_Y))
                if (((yuvImage[i][j].YUV_u < 30) && (yuvImage[i][j].YUV_u > -30)) && ((yuvImage[i][j].YUV_v > -30) && (yuvImage[i][j].YUV_v < 30)))
                {
                    // 判断亮度范围

                    if ((yuvImage[i][j].YUV_y >= 0x40) && (yuvImage[i][j].YUV_y < 0xc0)) // 128±64
                    {
                        yuvImage[i][j].type = 64;
                        if ((yuvImage[i][j].YUV_y >= 0x58) && (yuvImage[i][j].YUV_y < 0xa8)) // 128±40
                        {
                            yuvImage[i][j].type = 40;
                            if ((yuvImage[i][j].YUV_y >= 0x6c) && (yuvImage[i][j].YUV_y < 0x94)) // 128±20
                            {
                                yuvImage[i][j].type = 20;
                            }
                        }
                    }
                }
            }
            // 判断类型并且累计
            if (yuvImage[i][j].type == 64)
            {
                rgb_total[0].R_total += image[i][j].r;
                rgb_total[0].G_total += image[i][j].g;
                rgb_total[0].B_total += image[i][j].b;
                rgb_total[0].total++;
                count++;
            }
            if (yuvImage[i][j].type == 40)
            {
                rgb_total[1].R_total += image[i][j].r;
                rgb_total[1].G_total += image[i][j].g;
                rgb_total[1].B_total += image[i][j].b;
                rgb_total[1].total++;
            }
            if (yuvImage[i][j].type == 20)
            {
                rgb_total[2].R_total += image[i][j].r;
                rgb_total[2].G_total += image[i][j].g;
                rgb_total[2].B_total += image[i][j].b;
                rgb_total[2].total++;
            }
        }
    }
    // cout << rgb_total[0].total <<endl;
    // cout << rgb_total[1].total <<endl;
    // cout << rgb_total[2].total <<endl;
    // cout << rgb_total[0].R_total << " " << rgb_total[0].G_total << " " << rgb_total[0].B_total << " " << rgb_total[0].total << endl;
    // cout << rgb_total[1].R_total << " " << rgb_total[1].G_total << " " << rgb_total[1].B_total << " " << rgb_total[1].total << endl;
    // cout << rgb_total[2].R_total << " " << rgb_total[2].G_total << " " << rgb_total[2].B_total << " " << rgb_total[2].total << endl;

    // 求平均值 128±64
    rgb_total[0].avg_R = (double)rgb_total[0].R_total / (double)rgb_total[0].total;
    rgb_total[0].avg_G = (double)rgb_total[0].G_total / (double)rgb_total[0].total;
    rgb_total[0].avg_B = (double)rgb_total[0].B_total / (double)rgb_total[0].total;
    // // 128±40
    rgb_total[1].avg_R = (double)rgb_total[1].R_total / (double)rgb_total[1].total;
    rgb_total[1].avg_G = (double)rgb_total[1].G_total / (double)rgb_total[1].total;
    rgb_total[1].avg_B = (double)rgb_total[1].B_total / (double)rgb_total[1].total;
    // // 128±20
    rgb_total[2].avg_R = (double)rgb_total[2].R_total / (double)rgb_total[2].total;
    rgb_total[2].avg_G = (double)rgb_total[2].G_total / (double)rgb_total[2].total;
    rgb_total[2].avg_B = (double)rgb_total[2].B_total / (double)rgb_total[2].total;
    // // // 配置权重 对64配置
    rgb_total[0].avg_R = rgb_total[0].avg_R * 0.2;
    rgb_total[0].avg_G = rgb_total[0].avg_G * 0.2;
    rgb_total[0].avg_B = rgb_total[0].avg_B * 0.2;
    // 对40配置
    rgb_total[1].avg_R = rgb_total[1].avg_R * 0.5;
    rgb_total[1].avg_G = rgb_total[1].avg_G * 0.5;
    rgb_total[1].avg_B = rgb_total[1].avg_B * 0.5;
    // 求平均
    GainR = (rgb_total[0].avg_G + rgb_total[1].avg_G + rgb_total[2].avg_G) / (rgb_total[0].avg_R + rgb_total[1].avg_R + rgb_total[2].avg_R);
    GainB = (rgb_total[0].avg_G + rgb_total[1].avg_G + rgb_total[2].avg_G) / (rgb_total[0].avg_B + rgb_total[1].avg_B + rgb_total[2].avg_B);
    //  返回值
    return_GB.GainR = GainR;
    return_GB.GainB = GainB;
    return return_GB;
}

        白平衡操作:

void white_balance(Return_GainR_GrainB RB, vector<vector<uint16_t>> &image)
{
    int height = image.size(), width = image[0].size(), i = 0, j = 0;
    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            bool Red = (i % 2 == 1) && (j % 2 == 0);
            bool Blue = (i % 2 == 0) && (j % 2 == 1);
            if (Red)
            {
                image.at(i).at(j) = image.at(i).at(j) * RB.GainB;
            }
            if (Blue)
            {
                image.at(i).at(j) = image.at(i).at(j) * RB.GainR;
            }
            if (image.at(i).at(j) > 0x3ff)
            {
                image.at(i).at(j) = 0x3ff;
            }
        }
    }
}

        头文件:

/**
 * @author. zhl 2023-4-11
 * @brief RGB_YUV_H 头文件
 * @note 以下这个头文件存放RAM——RGB变幻的一些需要的参数
 */

#ifndef RGB_YUV_H
#define RGB_YUV_H
#include <iostream>
#include <stdint.h>
#include <stdlib.h>
#include <vector>
#include <cmath>
using namespace std;
// 定义RGB图像的像素结构体
struct Pixel
{
    uint8_t r;
    uint8_t g;
    uint8_t b;
};

struct YUV_float
{
    double YUV_y;
    double YUV_u;
    double YUV_v;
    int type;
};

//JPEG的亮度量化和色度量化函数 传入
struct  Ycrcb
{
    uint8_t y;
    uint8_t cr;
    uint8_t cb;
};


struct RGB_total
{
    uint64_t R_total;
    uint64_t G_total;
    uint64_t B_total;
    double avg_R;
    double avg_G;
    double avg_B;
    int total;
};

struct Return_GainR_GrainB
{
    double GainR;
    double GainB;
};

struct YUYV
{
    uint8_t Y;
    uint8_t U;
    uint8_t Y_;
    uint8_t V;
};


const vector<vector<int>> LuminanceQuantizationTable = {
    { 16, 11, 10, 16, 24, 40, 51, 61 },
    { 12, 12, 14, 19, 26, 58, 60, 55 },
    { 14, 13, 16, 24, 40, 57, 69, 56 },
    { 14, 17, 22, 29, 51, 87, 80, 62 },
    { 18, 22, 37, 56, 68, 109, 103, 77 },
    { 24, 35, 55, 64, 81, 104, 113, 92 },
    { 49, 64, 78, 87, 103, 121, 120, 101 },
    { 72, 92, 95, 98, 112, 100, 103, 99 }
};
const vector<vector<int>> ChrominanceQuantizationTable = {
    { 17, 18, 24, 47, 99, 99, 99, 99 },
    { 18, 21, 26, 66, 99, 99, 99, 99 },
    { 24, 26, 56, 99, 99, 99, 99, 99 },
    { 47, 66, 99, 99, 99, 99, 99, 99 },
    { 99, 99, 99, 99, 99, 99, 99, 99 },
    { 99, 99, 99, 99, 99, 99, 99, 99 },
    { 99, 99, 99, 99, 99, 99, 99, 99 },
    { 99, 99, 99, 99, 99, 99, 99, 99 }
};
//DTC 量化啊表
// vector<vector<int>> q = {
//     {16,  11,  10,  16,  24,  40,  51,  61},
//     {12,  12,  14,  19,  26,  58,  60,  55},
//     {14,  13,  16,  24,  40,  57,  69,  56},
//     {14,  17,  22,  29,  51,  87,  80,  62},
//     {18,  22,  37,  56,  68, 109, 103,  77},
//     {24,  35,  55,  64,  81, 104, 113,  92},
//     {49,  64,  78,  87, 103, 121, 120, 101},
//     {72,  92,  95,  98, 112, 100, 103,  99}
// };


/*
1.546875        -0.397460938        -0.149414063
-0.247070313        1.258789063        -0.01171875
-0.102539063        -0.844726563        1.947265625
*/
//颜色矩阵



// 白平衡色度权重
#define Y128_Deviation_20_Weight 1
#define Y128_Deviation_40_Weight 0.5
#define Y128_Deviation_64_Weight 0.2
// RGBtoYUV
#define Y(r, g, b) 0.299 * (r)+0.587 * (g)+0.114 * (b)
#define U(r, g, b) -0.147 * (r)-0.298 * (g)+0.436 * (b)
#define V(r, g, b) 0.615 * (r)-0.515 * (g)-0.100 * (b)
// YUVtoRGB
#define R(y, v) (y) + 1.140 * (v)
#define G(y, u, v) (y) - 0.395 * (u)-0.581 * (v)
#define B(y, u) (y) + 2.032 * (u)
// 白平衡灰度判断标准
#define R_Y 0.60
#define B_Y 0.60
// UV标准判断
#define U_V 0.15

// 大小端转换,写成内联函数效率高
inline uint16_t swap_endian(uint16_t num)
{
    return (num >> 8) | (num << 8);
}

#endif

        下面是经过白平衡处理的图片,但是因为又亮度过高,有些地方回出现红斑,这个我们后面来处理,已经接近实际场景了。


 色彩空间矩阵矫正:

        色彩矫正是一种用于改善图像色彩的处理方法,其主要作用是消除图像中的色彩偏差,使图像的色彩更加准确、自然、真实。色彩偏差通常是由于光源、相机、显示器等硬件设备的差异或者环境因素等引起的。

  1. 消除色彩偏差:色彩矫正可以消除相机、显示器等设备造成的色彩偏差,使图像的色彩更加准确和自然。例如,可以通过白平衡矫正消除图像中的色温偏差,使图像中的白色看起来真正的白色。
  2. 提高图像质量:色彩矫正可以改善图像的色彩效果,使图像更加清晰、明亮、鲜艳,从而提高图像的质量和视觉效果。
  3. 统一色彩风格:对于一些需要保持一致色彩风格的场景,如商业广告、产品摄影等,色彩矫正可以使图像的色彩保持一致,达到统一的色彩风格。
  4. 降低色彩误差:色彩矫正可以降低图像中的色彩误差,从而提高图像的色彩还原度和准确性,使得图像更加符合人眼的视觉感受。 综上所述,色彩矫正在数字图像处理中具有重要的作用,可以消除图像中的色彩偏差,改善图像的色彩效果,提高图像质量和准确性。

        可能保密,所以我删掉了精度,下面是代码:

void matmul3x3_3x1(vector<vector<Pixel>> &mat3x1)
{
    // 检查输入矩阵的形状是否正确
    vector<vector<double>> mat3x3(3, vector<double>(3));
    mat3x3[0][0] = 1.5, mat3x3[0][1] = -0.3, mat3x3[0][2] = -0.1;
    mat3x3[1][0] = -0.2, mat3x3[1][1] = 1.2, mat3x3[1][2] = -0.01;
    mat3x3[2][0] = -0.1, mat3x3[2][1] = -0.8, mat3x3[2][2] = 1.9;
    // 对每一个像素计算矩阵乘积
    for (int i = 0; i < mat3x1.size(); i++)
    {
        for (int j = 0; j < mat3x1[0].size(); j++)
        {
            int number = (10 * (double)mat3x1[i][j].r * mat3x3[0][0]) / 10 + (10 * (double)mat3x1[i][j].g * mat3x3[0][1]) / 10 + (10 * (double)mat3x1[i][j].b * mat3x3[0][2]) / 10;
        }
    }
}

        正在图片看起来略微的变得更加透亮。


伽马矫正:

        伽马矫正是数字图像处理中常用的一种技术,其主要作用是对图像的亮度进行调整,使其在不同的显示设备上呈现出相同的亮度级别,从而达到更加准确和自然的显示效果。伽马矫正的意义主要有以下几点:

  1. 保证图像的亮度一致性:由于不同的显示设备具有不同的亮度响应曲线,同一个图像在不同设备上的亮度表现会有所不同。伽马矫正可以使图像的亮度表现在不同设备上更加一致,确保图像在不同设备上呈现出相同的亮度级别。
  2. 提高图像对比度:伽马矫正可以调整图像的亮度,使得图像中的细节更加清晰、明亮,从而提高图像的对比度和视觉效果。
  3. 改善图像的色彩鲜艳度:伽马矫正可以使图像中的颜色更加鲜艳、真实,从而提高图像的色彩还原度和准确性。
  4. 适应人眼的视觉特性:人眼对亮度的感受是非线性的,伽马矫正可以使图像的亮度响应与人眼的视觉特性相适应,使图像看起来更加自然和舒适。 综上所述,伽马矫正在数字图像处理中具有重要的意义,可以提高图像的亮度一致性、对比度和色彩鲜艳度,适应人眼的视觉特性,从而达到更加准确、自然和舒适的显示效果。

        下面是代码:

vector<vector<Pixel>> Gamma_correction(vector<vector<Pixel>> &image, float gamma)
{
    for (int i = 0; i < image.size(); i++)
    {
        for (int j = 0; j < image[i].size(); j++)
        {
            /*
            / 255.0f 表示将该像素值除以 255.0,将像素值归一化到 [0, 1] 的范围内。
            这一步是为了将像素值转化为浮点类型,方便进行伽马矫正运算。
            pow(image[i][j].r / 255.0f, gamma) 表示对归一化后的像素值进行伽马矫正运算,其中 pow() 函数表示对指定数值取幂。
            第一个参数是要取幂的数值,第二个参数是幂指数,即伽马值。
            */
            image[i][j].r = pow((double)image[i][j].r / 255.0f, gamma) * 255.0f;
            image[i][j].g = pow((double)image[i][j].g / 255.0f, gamma) * 255.0f;
            image[i][j].b = pow((double)image[i][j].b / 255.0f, gamma) * 255.0f;
        }
    }
    return image;
}

        下面是矫正后的图片,可以看见整张图片的对比度变高了。


 去红:

        可以看出,图片高亮部分存在红色,现在写代码将其去除。

void reduce_red(vector<vector<Pixel>> &RGB)
{
    int height = RGB.size(), width = RGB[0].size(), i = 0, j = 0;
    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            if(Y(RGB[i][j].r,RGB[i][j].g,RGB[i][j].b)>230)
            {
              RGB[i][j].r = RGB[i][j].g; 
              RGB[i][j].b = RGB[i][j].g; 
            }
        }
    }
}

         可以看见红色减少了,但是总体来看,整张图片偏暗,这里吧伽马矫正的系数从1.5降低到1.1试试。让我们来看看调整过后的图片。

        这就是最终的图片啦。还是比较不错的! 

        整个ISP大概的简单流程就已经结束啦,实际上ISP算法非常复杂,作者也是模仿简单化的处理,具体的还得问专业人士!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值