KTIRC_Lib.c 10.9 KB
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "JZsdkLib.h"


#include "../IRC_data_deal/IRC_data_deal.h"

#include "MediaProc/IRC_funtion/IRC_param.h"
#include "MediaProc/ImageProc/PseudoColor/PseudoColor.h"



/*计算直方图均衡化并转换为8位灰度值
	    U16_t in_str 输入的数据
                int in_str_len 输入的数据长度
    灰度0为黑色,灰度255为白色
*/
static T_JZsdkReturnCode Kt_Irc_Histogram_vKT(U16_t *in_str, U8_t **out_str, int *out_str_len, struct IRC_param *dealInfo)
{
    //无图像
    if (in_str == NULL || dealInfo == NULL) 
    {  
        return JZ_ERROR_SYSTEM_MODULE_CODE_INVALID_PARAMETER;  
    }  

    // 分配输出结果的内存空间
    *out_str = (unsigned char*)malloc(sizeof(unsigned char) * dealInfo->PixelNum);
    if (*out_str == NULL) {
        // 如果内存分配失败,打印错误信息并返回空指针
        JZSDK_LOG_ERROR("Error: Memory allocation failed\n");
        return JZ_ERROR_SYSTEM_MODULE_CODE_INVALID_PARAMETER;
    }

/**********************************************
 * 
 *  获取图像的直方图
 * 
 * 
 * *****************************************************/

    // 初始化变量
    unsigned int ThisPixelGray = 0;     //当前处理像素点的灰度值
    long SumAllLegalGray = 0;       //存储的总灰度值

    // 分配当前灰度值出现次数的内存空间,并初始化为0
    //用于记录每个灰度值在输入向量中出现的次数。具体来说,它是一个无符号整型数组,
    //其索引表示灰度值,数组中的值表示该灰度值在输入向量中出现的次数。
    //在遍历输入向量时,将每个像素的灰度值记录在相应的 ThisGrayAppendTime 数组中。
    //这是直方图均衡化算法中需要用到的一个重要步骤,用于统计每个灰度值的出现次数 
    //为保证每一个灰度值都被包括,所以用 dealInfo->ExpectedMax 写长度
    unsigned int* ThisGrayAppendTime = (unsigned int*)calloc(dealInfo->ExpectedMax + 1, sizeof(unsigned int));
    if (ThisGrayAppendTime == NULL) {
        JZSDK_LOG_ERROR("Error: Memory allocation failed\n");
        free(*out_str);
        return JZ_ERROR_SYSTEM_MODULE_CODE_INVALID_PARAMETER;
    }

    // 遍历输入向量,统计各灰度值出现次数和总灰度和
    for (int i = 0; i < dealInfo->PixelNum; ++i) 
    {
        //获取本像素点的灰度值,且避免超过最大灰度值
        ThisPixelGray = in_str[i];
        if (ThisPixelGray > dealInfo->ExpectedMax) 
        {
            ThisPixelGray = dealInfo->ExpectedMax;
        }

        //直方图的 x坐标ThisPixelGray 的y值 即次数+1
        ThisGrayAppendTime[ThisPixelGray]++;

        //计算总灰度值
        SumAllLegalGray += ThisPixelGray;
    }

/*
    注:图像对比度:直方图分布越均匀,图像的对比度越高
        图像亮度:  直方图分布越靠右(最大值),则亮度越高
*/

/**********************************************************************************************************/

    // 初始化边界值和阈值增益
    unsigned int LowLimit = 0; //用于指定直方图均衡化的最低灰度值范围,通常为0。
    unsigned int HighLimit = 0; //用于指定直方图均衡化的最高灰度值范围,通常为最大灰度值。
    unsigned int VMax = dealInfo->ExpectedMax; //表示输入图像中可能的最大灰度值。
    unsigned int VMin = 0; //通常为0,表示输入图像中可能的最小灰度值。
    unsigned int ThresholdLimits = dealInfo->Gain * dealInfo->ImgDataBits; //阈值参数,是一个根据增益和位深度计算得出的阈值,用于在直方图均衡化时进行限制,确保输出图像的对比度合适。
    double AvgGray = SumAllLegalGray / (dealInfo->PixelNum * 1.0); //平均灰度值,表示输入图像中所有合法灰度值的平均值,用于直方图均衡化算法中计算每个像素点的增益值。

    // 计算灰度直方图的边界值,找到合适的灰度范围用于直方图均衡化
    for (int i = 0; i < dealInfo->ExpectedMax; ++i) 
    {
        if (LowLimit < dealInfo->LeftDrop) {
            LowLimit += ThisGrayAppendTime[i];
            VMin = i;
        }
        if (HighLimit < (dealInfo->PixelNum - dealInfo->RightDrop)) 
        {
            HighLimit += ThisGrayAppendTime[i];
            VMax = i;
        }
    }

    //(ThresholdLimits / 12) 是一个阈值的限制
    //根据增益和位深度计算得出的阈值,用于在直方图均衡化时进行限制,确保输出图像的对比度合适

    //将 VMax 增加 (ThresholdLimits / 12) 的目的是为了保证直方图均衡化后的最高灰度值范围与阈值限制的要求相符,
    //从而确保输出图像的对比度得到合适的控制,并且不会超出预期的范围。
    if (VMax < dealInfo->ExpectedMax - (ThresholdLimits / 12)) 
    {
        VMax += (ThresholdLimits / 12); //最大范围右移 1/12个阈值
    }

    //ThresholdDiff 阈值差异,计算阈值和灰度均值之间的差值
    //ThresholdDiff 被计算为 VMax - VMin + (ThresholdLimits / 6.0)
    //代表最高和最低灰度值范围之间的差距,再加上阈值的六分之一
    //ThresholdDiff 用于衡量最大最小灰度范围的宽度,以确定是否需要对像素进行进一步处理。
    double ThresholdDiff = VMax - VMin + (ThresholdLimits / 6.0);

    int LowThreshold = 0;
    if (AvgGray > (ThresholdDiff / 2.0)) 
    {
        //a. 如果 AvgGray 大于 ThresholdDiff / 2.0,则将 LowThreshold 的值设为 AvgGray - (ThresholdDiff / 2.0) 
        //的无符号整数部分。让 LowThreshold 在保证图像对比度的同时,尽可能地靠近平均灰度值 AvgGray。
        LowThreshold = (unsigned int)(AvgGray - (ThresholdDiff / 2.0));
    } 
    else 
    {
        //如果 AvgGray 小于等于 ThresholdDiff / 2.0,则将 LowThreshold 的值设为零。
        //避免 LowThreshold 取负值,确保不会对图像的过滤效果产生不良影响。
        LowThreshold = 0;
    }

    //重新计算阈值差值
    ThresholdDiff = VMax - LowThreshold;

    //避免阈值差值低于阈值限制,
    if (ThresholdDiff < ThresholdLimits) 
    {
        ThresholdDiff = ThresholdLimits;
        
        //根据新的阈值差值,重新计算 低阈值    
        LowThreshold = (unsigned int)(AvgGray - ThresholdDiff / 2.0);
    
        //最大灰度值与低阈值加上阈值限制之间的差异,如果仍然满足条件
        if (VMax > LowThreshold + ThresholdLimits) 
        {
            //重新计算阈值差值
            ThresholdDiff = VMax - LowThreshold;
        }
    }

    // 对输入向量进行直方图均衡化处理,并转换为8位灰度值
    for (int i = 0; i < dealInfo->PixelNum; ++i) 
    {
        //如果该像素点的灰度值小于最小阈值,使其等于最小阈值
        //确保直方图均衡化的值不会低于设定的阈值,从而保证图像的对比度和亮度的均衡。
        if (in_str[i] < LowThreshold) 
        {
            in_str[i] = LowThreshold;
        }

        //原始直方图值in_str[i]减去低阈值LowThreshold
        //然后乘以一个缩放系数(dealInfo->ExpectedMax / (ThresholdDiff * 1.0)),将结果赋值回in_str[i]。
        //目的将直方图的灰度值映射到更广的范围,以增强图像的对比度和细节。
        in_str[i] = (unsigned int)((in_str[i] - LowThreshold) * (dealInfo->ExpectedMax / (ThresholdDiff * 1.0)));

        //避免超出灰度最大值
        if (in_str[i] > dealInfo->ExpectedMax) 
        {
            in_str[i] = dealInfo->ExpectedMax;
        }
    }

    // 将均衡化后的灰度值转换为8位,并存储在输出向量中
    //直方图均衡化后的结果进行操作,将每个值转换为8位灰度值。具体操作是将in_str[i]中的值右移(InBits - 8)位
    //然后将结果转换为unsigned char类型,存储到输出向量对应的位置上。这样操作的目的是将原本可能不是8位的灰度值转换为8位灰度值,以便后续的处理和存储。
    for (int i = 0; i < dealInfo->PixelNum; i += 4) {
        (*out_str)[i]     = (unsigned char)(in_str[i] >> (dealInfo->ImgDataBits - 8));
        (*out_str)[i + 1] = (unsigned char)(in_str[i + 1] >> (dealInfo->ImgDataBits - 8));
        (*out_str)[i + 2] = (unsigned char)(in_str[i + 2] >> (dealInfo->ImgDataBits - 8));
        (*out_str)[i + 3] = (unsigned char)(in_str[i + 3] >> (dealInfo->ImgDataBits - 8));
    }

    *out_str_len = dealInfo->PixelNum;

    // 释放动态分配的内存空间
    free(ThisGrayAppendTime);

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

/******
 * 
 *  两点矫正
 *  
 *  
 * *****/
T_JZsdkReturnCode Kt_Irc_TPC(U16_t *ImageData,struct IRC_param *dealInfo) 
{
    //无图像
    if (ImageData == NULL || dealInfo == NULL) 
    {  
        return JZ_ERROR_SYSTEM_MODULE_CODE_INVALID_PARAMETER;  
    } 
  
    // 如果tpc的斜率和tpc的截距都为0,重新计算斜率与斜距
    int allZeroSlope_flag = 1, allZeroDiff_flag = 1;  
    for (int i = 0; i < dealInfo->PixelNum; i++) 
    {  
        if (dealInfo->TPC_Slope[i] != 0) 
        {  
            allZeroSlope_flag = 0;  
        }  
        if (dealInfo->TPC_Diff[i] != 0) {  
            allZeroDiff_flag = 0;  
        }  
    }  
  
    //如果出现tpc的斜率和tpc的截距都为0,重新计算斜率与斜距
    if (allZeroSlope_flag && allZeroDiff_flag) 
    {  
        double AvgSingleFrame_LowT = 0, AvgSingleFrame_HighT = 0;  

        for (int i = 0; i < dealInfo->PixelNum; i++) 
        {  
            AvgSingleFrame_LowT += dealInfo->LowT_NineFrame_Avg[i];  
            AvgSingleFrame_HighT += dealInfo->HighT_NineFrame_Avg[i];  
        }  

        AvgSingleFrame_LowT = AvgSingleFrame_LowT / dealInfo->PixelNum;  
        AvgSingleFrame_HighT = AvgSingleFrame_HighT / dealInfo->PixelNum;  
  
        for (int i = 0; i < dealInfo->PixelNum; i++) 
        {  
            if (dealInfo->HighT_NineFrame_Avg[i] > dealInfo->LowT_NineFrame_Avg[i]) 
            {  
                dealInfo->TPC_Slope[i] = (AvgSingleFrame_HighT - AvgSingleFrame_LowT) / (dealInfo->HighT_NineFrame_Avg[i] - dealInfo->LowT_NineFrame_Avg[i]);  
            } 
            else 
            {  
                dealInfo->TPC_Slope[i] = 0;  
            }  
            dealInfo->TPC_Diff[i] = AvgSingleFrame_LowT - dealInfo->TPC_Slope[i] * dealInfo->LowT_NineFrame_Avg[i];  
        }  
    }  
  
    // 应用两点校正公式  
    for (int i = 0; i < dealInfo->PixelNum; i++) 
    {  
        ImageData[i] = (int)(dealInfo->TPC_Slope[i] * ImageData[i] + dealInfo->TPC_Diff[i]);  
        if (ImageData[i] < 0) 
        {  
            ImageData[i] = 0;  
        } 
        else if (ImageData[i] > dealInfo->ExpectedMax) 
        {  
            ImageData[i] = dealInfo->ExpectedMax;  
        }  
    }  
  
    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS; // 返回校正后的灰度数组  
}