美文网首页
使用OpenCV处理图片的亮度、对比度、曝光、高光、阴影、饱和度

使用OpenCV处理图片的亮度、对比度、曝光、高光、阴影、饱和度

作者: 吃大米的小蚂蚁 | 来源:发表于2023-05-21 21:41 被阅读0次

    集成opencv的方式请参照上一个帖子
    OpenCV在iOS端的集成及Mat和UIImage互相转化(附源码)

    废话不多说直接上代码,伸手党福利:
    代码中记得引入头文件及命名空间

    #import <opencv2/opencv.hpp>
    
    #import "CVTools.h" //对应的.h文件  命名空间在自己的.h文件后定义
    
    using namespace cv;
    using namespace std;
    

    下面的处理方法统一对使用最多的8bit图片处理,如果是16bit的图片需要修改对应的格式,在使用的方法内需要改动的如下:
    255 调整为 65535
    Vec4b 调整为 Vec4w
    uchar 调整为 ushort
    UCHAR_MAX 调整为 USHRT_MAX

    1.亮度

    //调整亮度
    +(UIImage *)brightInPutImage:(UIImage *)inputImage value:(float)beta{
        Mat g_srcImage = [CVTools cvMatFromUIImage:inputImage];
        if(g_srcImage.empty()){
            return nil;
        }
        Mat g_dstImage=Mat::zeros(g_srcImage.size(),g_srcImage.type());
        g_srcImage.convertTo(g_dstImage, -1, 1, beta);
        
        return [CVTools UIImageFromCVMat:g_dstImage];
    }
    

    2.对比度

    //调整对比度
    +(UIImage *)contrasInPutImage:(UIImage *)inputImage value:(CGFloat)alpha{
        Mat g_srcImage = [CVTools cvMatFromUIImage:inputImage];
        if(g_srcImage.empty()){
            return nil;
        }
        Mat g_dstImage=Mat::zeros(g_srcImage.size(),g_srcImage.type());
        g_srcImage.convertTo(g_dstImage, -1, alpha, 1);
        return [CVTools UIImageFromCVMat:g_dstImage];
    }
    

    3.曝光

    曝光调节的方法是是对伽马参数进行调节来啊达到调整曝光的目的

    //调节曝光
    +(UIImage *)gammaInPutImage:(UIImage *)inputImage value:(CGFloat)value{
        
        Mat g_srcImage =  [CVTools cvMatFromUIImage:inputImage];
        
        double gamma_c = 1;
        double gamma_g = value;
        int height=g_srcImage.rows;
        int width=g_srcImage.cols;
        
        double val;
        Mat g_dstImage=Mat::zeros(g_srcImage.size(),g_srcImage.type());
        for (int row=0; row<height; row++) {
            for (int col=0; col<width; col++) {
                
                for (int c = 0; c < 3; c++){
                    val = (double)g_srcImage.at<cv::Vec4b>(row, col)[c] / UCHAR_MAX;
                    g_dstImage.at<cv::Vec4b>(row, col)[c] =  saturate_cast<uchar>((uchar)(pow(val / gamma_c, 1 / gamma_g) * UCHAR_MAX));
                }
                g_dstImage.at<cv::Vec4b>(row, col)[3] = g_srcImage.at<cv::Vec4b>(row, col)[3];
            }
        }
        
        return [CVTools UIImageFromCVMat:g_dstImage];
    }
    

    4.高光

    //调节高光
    +(UIImage *)highLightInPutImage:(UIImage *)inputImage value:(CGFloat)light{
        
        Mat input =  [CVTools cvMatFromUIImage:inputImage];
        // 生成灰度图
        cv::Mat gray = cv::Mat::zeros(input.size(), CV_32FC1);
        cv::Mat f = input.clone();
        f.convertTo(f, CV_32FC3);
        vector<cv::Mat> pics;
        split(f, pics);
        gray = 0.299f*pics[2] + 0.587*pics[2] + 0.114*pics[0];
        gray = gray / 255.0;
        
        // 确定高光区
        cv::Mat thresh = cv::Mat::zeros(gray.size(), gray.type());
        thresh = gray.mul(gray);
        // 取平均值作为阈值
        Scalar t = mean(thresh);
        cv::Mat mask = cv::Mat::zeros(gray.size(), CV_16UC1);
        mask.setTo(255.0, thresh >= t[0]);
        
        // 参数设置
        int max = 4;
        float bright = light / 100.0f / max;
        float mid = 1.0f + max * bright;
        
        // 边缘平滑过渡
        cv::Mat midrate = cv::Mat::zeros(input.size(), CV_32FC1);
        cv::Mat brightrate = cv::Mat::zeros(input.size(), CV_32FC1);
        for (int i = 0; i < input.rows; ++i)
        {
            uchar *m = mask.ptr<uchar>(i);
            float *th = thresh.ptr<float>(i);
            float *mi = midrate.ptr<float>(i);
            float *br = brightrate.ptr<float>(i);
            for (int j = 0; j < input.cols; ++j)
            {
                if (m[j] == 255.0)
                {
                    mi[j] = mid;
                    br[j] = bright;
                }
                else {
                    mi[j] = (mid - 1.0f) / t[0] * th[j] + 1.0f;
                    br[j] = (1.0f / t[0] * th[j])*bright;
                }
            }
        }
        
        // 高光提亮,获取结果图
        cv::Mat result = cv::Mat::zeros(input.size(), input.type());
        for (int i = 0; i < input.rows; i++)
        {
            float *mi = midrate.ptr<float>(i);
            float *br = brightrate.ptr<float>(i);
            for (int j = 0; j < input.cols; j++)
            {
                for (int k = 0; k < 3; k++)
                {
                    float temp = pow(float(input.at<cv::Vec4b>(i,j)[k]) / UCHAR_MAX, 1.0f / mi[j])*(1.0 / (1 - br[j]));
                    if (temp > 1.0f)temp = 1.0f;
                    if (temp < 0.0f) temp = 0.0f;
                    uchar utemp = uchar(UCHAR_MAX*temp);
                    result.at<cv::Vec4b>(i,j)[k] = utemp;
                }
                result.at<cv::Vec4b>(i,j)[3] = input.at<cv::Vec4b>(i,j)[3];
            }
        }
        
        return  [CVTools UIImageFromCVMat:result];
        
    }
    

    5.阴影

    // 调节阴影
    /// - Parameters:
    ///   - inputImage: inputImage
    ///   - value: value
    +(UIImage *)shadowInPutImage:(UIImage *)inputImage value:(CGFloat)light{
        
        Mat input =  [CVTools cvMatFromUIImage:inputImage];
        // 生成灰度图
        cv::Mat gray = cv::Mat::zeros(input.size(), CV_32FC1);
        cv::Mat f = input.clone();
        f.convertTo(f, CV_32FC3);
        vector<cv::Mat> pics;
        split(f, pics);
        gray = 0.299f*pics[2] + 0.587*pics[2] + 0.114*pics[0];
        gray = gray / 255.0;
        
        // 确定阴影区
        cv::Mat thresh = cv::Mat::zeros(gray.size(), gray.type());
        thresh = (1.0f - gray).mul(1.0f - gray);
        // 取平均值作为阈值
        Scalar t = mean(thresh);
        cv::Mat mask = cv::Mat::zeros(gray.size(), CV_16UC1);
        mask.setTo(35535, thresh >= t[0]);
        
        // 参数设置
        int max = 4;
        float bright = light / 100.0f / max;
        float mid = 1.0f + max * bright;
        
        // 边缘平滑过渡
        cv::Mat midrate = cv::Mat::zeros(input.size(), CV_32FC1);
        cv::Mat brightrate = cv::Mat::zeros(input.size(), CV_32FC1);
        for (int i = 0; i < input.rows; ++i)
        {
            uchar *m = mask.ptr<uchar>(i);
            float *th = thresh.ptr<float>(i);
            float *mi = midrate.ptr<float>(i);
            float *br = brightrate.ptr<float>(i);
            for (int j = 0; j < input.cols; ++j)
            {
                if (m[j] == 255.0)
                {
                    mi[j] = mid;
                    br[j] = bright;
                }
                else {
                    mi[j] = (mid - 1.0f) / t[0] * th[j]+ 1.0f;
                    br[j] = (1.0f / t[0] * th[j])*bright;
                }
            }
        }
        
        // 阴影提亮,获取结果图
        cv::Mat result = cv::Mat::zeros(input.size(), input.type());
        for (int i = 0; i < input.rows; ++i)
        {
            float *mi = midrate.ptr<float>(i);
            float *br = brightrate.ptr<float>(i);
            for (int j = 0; j < input.cols; ++j)
            {
                for (int k = 0; k < 3; ++k)
                {
                    float temp = pow(float(input.at<cv::Vec4b>(i,j)[k]) / UCHAR_MAX, 1.0f / mi[j])*(1.0 / (1 - br[j]));
                    if (temp > 1.0f)
                        temp = 1.0f;
                    if (temp < 0.0f)
                        temp = 0.0f;
                    uchar utemp = uchar(UCHAR_MAX*temp);
                    result.at<cv::Vec4b>(i,j)[k] = utemp;
                }
                result.at<cv::Vec4b>(i,j)[3] = input.at<cv::Vec4b>(i,j)[3];
            }
        }
        
        return [CVTools UIImageFromCVMat:result];
        
    }
    

    6.饱和度

    将RGB颜色空间转换为HLS颜色空间,HLS空间三个分量分别是色相(H)、亮度(L)、饱和度(S),通过调整S通道的数值来达到修改色相的目的,代码中有8位转32位浮点数的操作,是因为在iOS端Opencv只支持处理8位或者32位的浮点数,因项目中使用的是16位的图片,所以需要转成32位的归一化的浮点数格式来进行处理,如果是8位图片,可以考虑去除掉该转换。

    //调节饱和度
    +(UIImage *)saturationInPutImage:(UIImage *)inputImage value:(CGFloat)value{
        cv::Mat originImgMat = [CVTools cvMatFromUIImage:inputImage];
        
        //u8 to 32f
        cv::Mat folatImgMat;
        originImgMat.convertTo(folatImgMat,CV_32FC4,1/255.0);
    
        //32f to hls
        cv::Mat hlsMat = Mat(folatImgMat.size(),CV_32FC3);
        cvtColor(folatImgMat, hlsMat, COLOR_RGB2HLS);
    
        //处理S通道
        for (int row=0; row < originImgMat.rows; row++) {
            for (int col=0; col < originImgMat.cols; col++) { //hlsMat.at<Vec3f>(row,col)[1]
                CGFloat sValue = hlsMat.at<Vec3f>(row,col)[2] + value;
                if(sValue < 0){
                    sValue = 0;
                }
                if(sValue > 1){
                    sValue = 1;
                }
                hlsMat.at<Vec3f>(row,col)[2] = sValue;
            }
        }
    
        //hls to 32f
        cv::Mat hlsFloatMat = Mat(folatImgMat.size(),CV_32FC4);
        cvtColor(hlsMat, hlsFloatMat, COLOR_HLS2RGB);
    
        //32f to u8
        cv::Mat reslutMat = Mat(folatImgMat.size(),CV_8UC4);
        hlsFloatMat.convertTo(reslutMat,CV_8UC4,255.0);
        
        return [CVTools UIImageFromCVMat:reslutMat];
    }
    

    7.色温

    增加红绿通道,则颜色偏暖;减少红绿通道,则颜色偏冷,以达到修改色温的目的

    // 调节色温
    +(UIImage *)colorTemperatureInPutImage:(UIImage *)inputImage value:(CGFloat)value{
        Mat input =  [CVTools cvMatFromUIImage:inputImage];
        //    cv::Mat result = input.clone();
        Mat result=Mat::zeros(input.size(),input.type());
        
        int row = input.rows;
        int col = input.cols;
        for (int i = 0; i < row; i++)
        {
            for (int j = 0; j < col; j++)
            {
                result.at<cv::Vec4b>(i, j)[0] = saturate_cast<uchar>(input.at<cv::Vec4b>(i, j)[0] + value*1.3);
                result.at<cv::Vec4b>(i, j)[1] = saturate_cast<uchar>(input.at<cv::Vec4b>(i, j)[1] + value*1.3 );
                result.at<cv::Vec4b>(i, j)[2] = saturate_cast<uchar>(input.at<cv::Vec4b>(i, j)[2]);
                //处理alpha通道
                result.at<Vec4b>(i,j)[3] = input.at<cv::Vec4b>(i,j)[3];
            }
        }
        return [CVTools UIImageFromCVMat:result];
    }
    

    8.色相

    与调整饱和度逻辑类似,将RGB颜色空间转换为HLS颜色空间,HLS空间三个分量分别是色相(H)、亮度(L)、饱和度(S),通过调整H通道的数值来达到修改色相的目的。

    /// 调节色相
    +(UIImage *)hueInPutImage:(UIImage *)inputImage value:(CGFloat)value{
        cv::Mat originImgMat = [CVTools cvMatFromUIImage:inputImage];
        
        //u8 to 32f
        cv::Mat folatImgMat;
        originImgMat.convertTo(folatImgMat,CV_32FC4,1/255.0);
    
        //32f to hls
        cv::Mat hlsMat = Mat(folatImgMat.size(),CV_32FC3);
        cvtColor(folatImgMat, hlsMat, COLOR_RGB2HLS);
    
        //处理H通道
        for (int row=0; row < originImgMat.rows; row++) {
            for (int col=0; col < originImgMat.cols; col++) { //hlsMat.at<Vec3f>(row,col)[1]
                CGFloat hue = hlsMat.at<Vec3f>(row,col)[0] + value;
                if(hue < 0){
                    hue = 0;
                }
                if(hue > 360){
                    hue = 360;
                }
                hlsMat.at<Vec3f>(row,col)[0] = hue;
            }
        }
    
        //hls to 32f
        cv::Mat hlsFloatMat = Mat(folatImgMat.size(),CV_32FC4);
        cvtColor(hlsMat, hlsFloatMat, COLOR_HLS2RGB);
    
        //32f to u8
        cv::Mat reslutMat = Mat(folatImgMat.size(),CV_8UC4);
        hlsFloatMat.convertTo(reslutMat,CV_8UC4,255.0);
        
        return [CVTools UIImageFromCVMat:reslutMat];
    }
    

    代码中用到的cvMatFromUIImage方法及UIImageFromCVMat方法可移步OpenCV在iOS端的集成及Mat和UIImage互相转化(附源码)
    获取

    相关文章

      网友评论

          本文标题:使用OpenCV处理图片的亮度、对比度、曝光、高光、阴影、饱和度

          本文链接:https://www.haomeiwen.com/subject/mdrzsdtx.html