OpenCV自适应直方图均衡CLAHE C++源代码分享

基于Qt与OpenCV的掌纹特征提取系统:C++实现与工程实践 计算机视觉是人工智能领域的关键技术,其核心在于通过算法让计算机“看懂”图像。特征提取是计算机视觉的基础环节,它从图像中识别并定位关键信息点,如边缘、角点或纹理特征,这些特征是后续图像识别、匹配与分析的基础。在生物识别、安防监控、工业质检等应用场景中,高效、准确的特征提取技术具有重要价值。本文聚焦于一个具体的工程实践案例——手形掌纹特征提取系统,深入探讨了如何利用**OpenCV**强大的图像处理库和**Qt**成熟的跨平台GUI框架,在C++环境下构建一个完整的、可交互的桌面应用程序。文章详细解析了从图像预 阅读详情

一、引言

最近收到几个网友提供OpenCV中CLAHE的源代码的请求,在此直接将OpenCV4.54版本CLAHE.CPP的源码分享出来。

二、OpenCV源代码的下载

下载地址:https://sourceforge.net/projects/opencvlibrary/files/在这里插入图片描述
有3.4.10–4.5.4的版本,但下载很慢,老猿费了很大的劲,大家可以考虑专门的下载工具下载。如果实在下不下来,请关注老猿Python的微信公号给老猿发消息。

三、CLAHE C++源代码

/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2013, NVIDIA Corporation, all rights reserved.
// Copyright (C) 2014, Itseez Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the copyright holders or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

#include "precomp.hpp"
#include "opencl_kernels_imgproc.hpp"

// ----------------------------------------------------------------------
// CLAHE

#ifdef HAVE_OPENCL

namespace clahe
{
    static bool calcLut(cv::InputArray _src, cv::OutputArray _dst,
        const int tilesX, const int tilesY, const cv::Size tileSize,
        const int clipLimit, const float lutScale)
    {
        cv::ocl::Kernel k("calcLut", cv::ocl::imgproc::clahe_oclsrc);
        if(k.empty())
            return false;

        cv::UMat src = _src.getUMat();
        _dst.create(tilesX * tilesY, 256, CV_8UC1);
        cv::UMat dst = _dst.getUMat();

        int tile_size[2];
        tile_size[0] = tileSize.width;
        tile_size[1] = tileSize.height;

        size_t localThreads[3]  = { 32, 8, 1 };
        size_t globalThreads[3] = { tilesX * localThreads[0], tilesY * localThreads[1], 1 };

        int idx = 0;
        idx = k.set(idx, cv::ocl::KernelArg::ReadOnlyNoSize(src));
        idx = k.set(idx, cv::ocl::KernelArg::WriteOnlyNoSize(dst));
        idx = k.set(idx, tile_size);
        idx = k.set(idx, tilesX);
        idx = k.set(idx, clipLimit);
        k.set(idx, lutScale);

        return k.run(2, globalThreads, localThreads, false);
    }

    static bool transform(cv::InputArray _src, cv::OutputArray _dst, cv::InputArray _lut,
        const int tilesX, const int tilesY, const cv::Size & tileSize)
    {

        cv::ocl::Kernel k("transform", cv::ocl::imgproc::clahe_oclsrc);
        if(k.empty())
            return false;

        int tile_size[2];
        tile_size[0] = tileSize.width;
        tile_size[1] = tileSize.height;

        cv::UMat src = _src.getUMat();
        _dst.create(src.size(), src.type());
        cv::UMat dst = _dst.getUMat();
        cv::UMat lut = _lut.getUMat();

        size_t localThreads[3]  = { 32, 8, 1 };
        size_t globalThreads[3] = { (size_t)src.cols, (size_t)src.rows, 1 };

        int idx = 0;
        idx = k.set(idx, cv::ocl::KernelArg::ReadOnlyNoSize(src));
        idx = k.set(idx, cv::ocl::KernelArg::WriteOnlyNoSize(dst));
        idx = k.set(idx, cv::ocl::KernelArg::ReadOnlyNoSize(lut));
        idx = k.set(idx, src.cols);
        idx = k.set(idx, src.rows);
        idx = k.set(idx, tile_size);
        idx = k.set(idx, tilesX);
        k.set(idx, tilesY);

        return k.run(2, globalThreads, localThreads, false);
    }
}

#endif

namespace
{
    template <class T, int histSize, int shift>
    class CLAHE_CalcLut_Body : public cv::ParallelLoopBody
    {
    public:
        CLAHE_CalcLut_Body(const cv::Mat& src, const cv::Mat& lut, const cv::Size& tileSize, const int& tilesX, const int& clipLimit, const float& lutScale) :
            src_(src), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), clipLimit_(clipLimit), lutScale_(lutScale)
        {
        }

        void operator ()(const cv::Range& range) const CV_OVERRIDE;

    private:
        cv::Mat src_;
        mutable cv::Mat lut_;

        cv::Size tileSize_;
        int tilesX_;
        int clipLimit_;
        float lutScale_;
    };

    template <class T, int histSize, int shift>
    void CLAHE_CalcLut_Body<T,histSize,shift>::operator ()(const cv::Range& range) const
    {
        T* tileLut = lut_.ptr<T>(range.start);
        const size_t lut_step = lut_.step / sizeof(T);

        for (int k = range.start; k < range.end; ++k, tileLut += lut_step)
        {
            const int ty = k / tilesX_;
            const int tx = k % tilesX_;

            // retrieve tile submatrix

            cv::Rect tileROI;
            tileROI.x = tx * tileSize_.width;
            tileROI.y = ty * tileSize_.height;
            tileROI.width = tileSize_.width;
            tileROI.height = tileSize_.height;

            const cv::Mat tile = src_(tileROI);

            // calc histogram

            cv::AutoBuffer<int> _tileHist(histSize);
            int* tileHist = _tileHist.data();
            std::fill(tileHist, tileHist + histSize, 0);

            int height = tileROI.height;
            const size_t sstep = src_.step / sizeof(T);
            for (const T* ptr = tile.ptr<T>(0); height--; ptr += sstep)
            {
                int x = 0;
                for (; x <= tileROI.width - 4; x += 4)
                {
                    int t0 = ptr[x], t1 = ptr[x+1];
                    tileHist[t0 >> shift]++; tileHist[t1 >> shift]++;
                    t0 = ptr[x+2]; t1 = ptr[x+3];
                    tileHist[t0 >> shift]++; tileHist[t1 >> shift]++;
                }

                for (; x < tileROI.width; ++x)
                    tileHist[ptr[x] >> shift]++;
            }

            // clip histogram

            if (clipLimit_ > 0)
            {
                // how many pixels were clipped
                int clipped = 0;
                for (int i = 0; i < histSize; ++i)
                {
                    if (tileHist[i] > clipLimit_)
                    {
                        clipped += tileHist[i] - clipLimit_;
                        tileHist[i] = clipLimit_;
                    }
                }

                // redistribute clipped pixels
                int redistBatch = clipped / histSize;
                int residual = clipped - redistBatch * histSize;

                for (int i = 0; i < histSize; ++i)
                    tileHist[i] += redistBatch;

                if (residual != 0)
                {
                    int residualStep = MAX(histSize / residual, 1);
                    for (int i = 0; i < histSize && residual > 0; i += residualStep, residual--)
                        tileHist[i]++;
                }
            }

            // calc Lut

            int sum = 0;
            for (int i = 0; i < histSize; ++i)
            {
                sum += tileHist[i];
                tileLut[i] = cv::saturate_cast<T>(sum * lutScale_);
            }
        }
    }

    template <class T, int shift>
    class CLAHE_Interpolation_Body : public cv::ParallelLoopBody
    {
    public:
        CLAHE_Interpolation_Body(const cv::Mat& src, const cv::Mat& dst, const cv::Mat& lut, const cv::Size& tileSize, const int& tilesX, const int& tilesY) :
            src_(src), dst_(dst), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), tilesY_(tilesY)
        {
            buf.allocate(src.cols << 2);
            ind1_p = buf.data();
            ind2_p = ind1_p + src.cols;
            xa_p = (float *)(ind2_p + src.cols);
            xa1_p = xa_p + src.cols;

            int lut_step = static_cast<int>(lut_.step / sizeof(T));
            float inv_tw = 1.0f / tileSize_.width;

            for (int x = 0; x < src.cols; ++x)
            {
                float txf = x * inv_tw - 0.5f;

                int tx1 = cvFloor(txf);
                int tx2 = tx1 + 1;

                xa_p[x] = txf - tx1;
                xa1_p[x] = 1.0f - xa_p[x];

                tx1 = std::max(tx1, 0);
                tx2 = std::min(tx2, tilesX_ - 1);

                ind1_p[x] = tx1 * lut_step;
                ind2_p[x] = tx2 * lut_step;
            }
        }

        void operator ()(const cv::Range& range) const CV_OVERRIDE;

    private:
        cv::Mat src_;
        mutable cv::Mat dst_;
        cv::Mat lut_;

        cv::Size tileSize_;
        int tilesX_;
        int tilesY_;

        cv::AutoBuffer<int> buf;
        int * ind1_p, * ind2_p;
        float * xa_p, * xa1_p;
    };

    template <class T, int shift>
    void CLAHE_Interpolation_Body<T, shift>::operator ()(const cv::Range& range) const
    {
        float inv_th = 1.0f / tileSize_.height;

        for (int y = range.start; y < range.end; ++y)
        {
            const T* srcRow = src_.ptr<T>(y);
            T* dstRow = dst_.ptr<T>(y);

            float tyf = y * inv_th - 0.5f;

            int ty1 = cvFloor(tyf);
            int ty2 = ty1 + 1;

            float ya = tyf - ty1, ya1 = 1.0f - ya;

            ty1 = std::max(ty1, 0);
            ty2 = std::min(ty2, tilesY_ - 1);

            const T* lutPlane1 = lut_.ptr<T>(ty1 * tilesX_);
            const T* lutPlane2 = lut_.ptr<T>(ty2 * tilesX_);

            for (int x = 0; x < src_.cols; ++x)
            {
                int srcVal = srcRow[x] >> shift;

                int ind1 = ind1_p[x] + srcVal;
                int ind2 = ind2_p[x] + srcVal;

                float res = (lutPlane1[ind1] * xa1_p[x] + lutPlane1[ind2] * xa_p[x]) * ya1 +
                            (lutPlane2[ind1] * xa1_p[x] + lutPlane2[ind2] * xa_p[x]) * ya;

                dstRow[x] = cv::saturate_cast<T>(res) << shift;
            }
        }
    }

    class CLAHE_Impl CV_FINAL : public cv::CLAHE
    {
    public:
        CLAHE_Impl(double clipLimit = 40.0, int tilesX = 8, int tilesY = 8);

        void apply(cv::InputArray src, cv::OutputArray dst) CV_OVERRIDE;

        void setClipLimit(double clipLimit) CV_OVERRIDE;
        double getClipLimit() const CV_OVERRIDE;

        void setTilesGridSize(cv::Size tileGridSize) CV_OVERRIDE;
        cv::Size getTilesGridSize() const CV_OVERRIDE;

        void collectGarbage() CV_OVERRIDE;

    private:
        double clipLimit_;
        int tilesX_;
        int tilesY_;

        cv::Mat srcExt_;
        cv::Mat lut_;

#ifdef HAVE_OPENCL
        cv::UMat usrcExt_;
        cv::UMat ulut_;
#endif
    };

    CLAHE_Impl::CLAHE_Impl(double clipLimit, int tilesX, int tilesY) :
        clipLimit_(clipLimit), tilesX_(tilesX), tilesY_(tilesY)
    {
    }

    void CLAHE_Impl::apply(cv::InputArray _src, cv::OutputArray _dst)
    {
        CV_INSTRUMENT_REGION();

        CV_Assert( _src.type() == CV_8UC1 || _src.type() == CV_16UC1 );

#ifdef HAVE_OPENCL
        bool useOpenCL = cv::ocl::isOpenCLActivated() && _src.isUMat() && _src.dims()<=2 && _src.type() == CV_8UC1;
#endif

        int histSize = _src.type() == CV_8UC1 ? 256 : 65536;

        cv::Size tileSize;
        cv::_InputArray _srcForLut;

        if (_src.size().width % tilesX_ == 0 && _src.size().height % tilesY_ == 0)
        {
            tileSize = cv::Size(_src.size().width / tilesX_, _src.size().height / tilesY_);
            _srcForLut = _src;
        }
        else
        {
#ifdef HAVE_OPENCL
            if(useOpenCL)
            {
                cv::copyMakeBorder(_src, usrcExt_, 0, tilesY_ - (_src.size().height % tilesY_), 0, tilesX_ - (_src.size().width % tilesX_), cv::BORDER_REFLECT_101);
                tileSize = cv::Size(usrcExt_.size().width / tilesX_, usrcExt_.size().height / tilesY_);
                _srcForLut = usrcExt_;
            }
            else
#endif
            {
                cv::copyMakeBorder(_src, srcExt_, 0, tilesY_ - (_src.size().height % tilesY_), 0, tilesX_ - (_src.size().width % tilesX_), cv::BORDER_REFLECT_101);
                tileSize = cv::Size(srcExt_.size().width / tilesX_, srcExt_.size().height / tilesY_);
                _srcForLut = srcExt_;
            }
        }

        const int tileSizeTotal = tileSize.area();
        const float lutScale = static_cast<float>(histSize - 1) / tileSizeTotal;

        int clipLimit = 0;
        if (clipLimit_ > 0.0)
        {
            clipLimit = static_cast<int>(clipLimit_ * tileSizeTotal / histSize);
            clipLimit = std::max(clipLimit, 1);
        }

#ifdef HAVE_OPENCL
        if (useOpenCL && clahe::calcLut(_srcForLut, ulut_, tilesX_, tilesY_, tileSize, clipLimit, lutScale) )
            if( clahe::transform(_src, _dst, ulut_, tilesX_, tilesY_, tileSize) )
            {
                CV_IMPL_ADD(CV_IMPL_OCL);
                return;
            }
#endif

        cv::Mat src = _src.getMat();
        _dst.create( src.size(), src.type() );
        cv::Mat dst = _dst.getMat();
        cv::Mat srcForLut = _srcForLut.getMat();
        lut_.create(tilesX_ * tilesY_, histSize, _src.type());

        cv::Ptr<cv::ParallelLoopBody> calcLutBody;
        if (_src.type() == CV_8UC1)
            calcLutBody = cv::makePtr<CLAHE_CalcLut_Body<uchar, 256, 0> >(srcForLut, lut_, tileSize, tilesX_, clipLimit, lutScale);
        else if (_src.type() == CV_16UC1)
            calcLutBody = cv::makePtr<CLAHE_CalcLut_Body<ushort, 65536, 0> >(srcForLut, lut_, tileSize, tilesX_, clipLimit, lutScale);
        else
            CV_Error( CV_StsBadArg, "Unsupported type" );

        cv::parallel_for_(cv::Range(0, tilesX_ * tilesY_), *calcLutBody);

        cv::Ptr<cv::ParallelLoopBody> interpolationBody;
        if (_src.type() == CV_8UC1)
            interpolationBody = cv::makePtr<CLAHE_Interpolation_Body<uchar, 0> >(src, dst, lut_, tileSize, tilesX_, tilesY_);
        else if (_src.type() == CV_16UC1)
            interpolationBody = cv::makePtr<CLAHE_Interpolation_Body<ushort, 0> >(src, dst, lut_, tileSize, tilesX_, tilesY_);

        cv::parallel_for_(cv::Range(0, src.rows), *interpolationBody);
    }

    void CLAHE_Impl::setClipLimit(double clipLimit)
    {
        clipLimit_ = clipLimit;
    }

    double CLAHE_Impl::getClipLimit() const
    {
        return clipLimit_;
    }

    void CLAHE_Impl::setTilesGridSize(cv::Size tileGridSize)
    {
        tilesX_ = tileGridSize.width;
        tilesY_ = tileGridSize.height;
    }

    cv::Size CLAHE_Impl::getTilesGridSize() const
    {
        return cv::Size(tilesX_, tilesY_);
    }

    void CLAHE_Impl::collectGarbage()
    {
        srcExt_.release();
        lut_.release();
#ifdef HAVE_OPENCL
        usrcExt_.release();
        ulut_.release();
#endif
    }
}

cv::Ptr<cv::CLAHE> cv::createCLAHE(double clipLimit, cv::Size tileGridSize)
{
    return makePtr<CLAHE_Impl>(clipLimit, tileGridSize.width, tileGridSize.height);
}

如对文章内容存在疑问,可在博客评论区留言,或关注:老猿Python 微信公号发消息咨询。

更多图像直方图处理的内容请参考《《数字图像处理》第三章学习总结感悟2:直方图处理》的介绍。

更多图像处理的内容请参考专栏《https://blog.youkuaiyun.com/laoyuanpython/category_9979286.html OpenCV-Python图形图像处理 》、《https://blog.youkuaiyun.com/laoyuanpython/category_10581071.html OpenCV-Python初学者疑难问题集》及《图像处理基础知识》的介绍。

写博不易,敬请支持:

如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!

关于老猿的付费专栏

  1. 付费专栏《https://blog.youkuaiyun.com/laoyuanpython/category_9607725.html 使用PyQt开发图形界面Python应用》专门介绍基于Python的PyQt图形界面开发基础教程,对应文章目录为《 https://blog.youkuaiyun.com/LaoYuanPython/article/details/107580932 使用PyQt开发图形界面Python应用专栏目录》;
  2. 付费专栏《https://blog.youkuaiyun.com/laoyuanpython/category_10232926.html moviepy音视频开发专栏 )详细介绍moviepy音视频剪辑合成处理的类相关方法及使用相关方法进行相关剪辑合成场景的处理,对应文章目录为《https://blog.youkuaiyun.com/LaoYuanPython/article/details/107574583 moviepy音视频开发专栏文章目录》;
  3. 付费专栏《https://blog.youkuaiyun.com/laoyuanpython/category_10581071.html OpenCV-Python初学者疑难问题集》为《https://blog.youkuaiyun.com/laoyuanpython/category_9979286.html OpenCV-Python图形图像处理 》的伴生专栏,是笔者对OpenCV-Python图形图像处理学习中遇到的一些问题个人感悟的整合,相关资料基本上都是老猿反复研究的成果,有助于OpenCV-Python初学者比较深入地理解OpenCV,对应文章目录为《https://blog.youkuaiyun.com/LaoYuanPython/article/details/109713407 OpenCV-Python初学者疑难问题集专栏目录 》
  4. 付费专栏《https://blog.youkuaiyun.com/laoyuanpython/category_10762553.html Python爬虫入门 》站在一个互联网前端开发小白的角度介绍爬虫开发应知应会内容,包括爬虫入门的基础知识,以及爬取优快云文章信息、博主信息、给文章点赞、评论等实战内容。

前两个专栏都适合有一定Python基础但无相关知识的小白读者学习,第三个专栏请大家结合《https://blog.youkuaiyun.com/laoyuanpython/category_9979286.html OpenCV-Python图形图像处理 》的学习使用。

对于缺乏Python基础的同仁,可以通过老猿的免费专栏《https://blog.youkuaiyun.com/laoyuanpython/category_9831699.html 专栏:Python基础教程目录)从零开始学习Python。

如果有兴趣也愿意支持老猿的读者,欢迎购买付费专栏。

老猿Python,跟老猿学Python!

☞ ░ 前往老猿Python博文目录 https://blog.youkuaiyun.com/LaoYuanPython ░
SceneDiver:具身智能中的实时视觉焦点规划方法 视觉焦点规划是具身智能系统实现‘边看边动’闭环控制的基础能力,其本质是在物理交互约束下对有限视觉资源进行动态调度。不同于静态显著性检测或端到端强化学习,它需融合任务状态、场景几何与运动延迟等硬性约束,通过轻量级结构化建模(如场景图谱、视觉-运动耦合模型)达成毫秒级响应。该技术直接支撑服务机器人抓取、工业机械臂巡检、远程手术辅助等强实时场景,核心挑战在于平衡可解释性、确定性与边缘部署可行性。SceneDiver正是面向这一需求提出的可验证、可嵌入、可标定的视觉调度协议。 阅读详情

相关推荐

OpenCV自适应直方图均衡算法CLAHE参数clipLimit的解析与实现(Python版)

CLAHE的clipLimit参数是用于限制直方图均衡中的对比度放大比例。当clipLimit较大时,图像对比度增强的能力也较强,但可能会导致图像中出现更多的噪声。相反,当clipLimit较小时,图像更平滑,但对比度增强的效果也不如较大的clipLimit。在本文中,我将为您解释clipLimit参数的含义,并提供Python代码来演示CLAHE的使用。最后,我们将CLAHE应用于原始图像,生成了一个经过增强的图像。总之,clipLimit是CLAHE算法中用于控制图像对比度增强的参数。

ByteHero的博客 911

解决:OpenCV: FFMPEG: tag 0x5634504d/‘MP4V‘ is not supported with codec id 12 and format ‘mp4 / MP4 (MP

问题描述: 利用python 的opencv包把图片合并为视频(mp4格式)的时候发现错误。 OpenCV: FFMPEG: tag 0x5634504d/'MP4V' is not supported with codec id 12 and format 'mp4 / MP4 (MPEG-4 Part 14)' OpenCV: FFMPEG: fallback to use tag 0x7634706d/'mp4v' 原因,主要是合成时采用的视频编码不对, 还是使用的是 fourcc =.

weixin_40671425的博客 3万+

OpenCV自适应直方图均衡CLAHE的clipLimit含义及理解

较低的clipLimit值会导致较强的限制,即小块中的像素值范围更小,从而减少了对比度增强的程度。而较高的clipLimit值会导致较弱的限制,即小块中的像素值范围更大,从而增加了对比度增强的程度。通过调整clipLimit的取值,可以控制CLAHE方法的增强效果,以在不同的图像和应用场景中获得最佳的结果。请注意,过高的clipLimit值可能会导致图像出现过度增强和噪点放大的问题,因此在使用CLAHE方法时需要谨慎选择clipLimit的取值。方法将CLAHE方法应用于图像,得到增强后的图像。

code88888的博客 796

报错OpenCV: FFMPEG: tag 0x5634504d/‘MP4V‘ is not supported with codec id 12 and format ‘mp4 / MP4V‘

Video Detection Done! Save processed video to the path :0303.mp4 OpenCV: FFMPEG: tag 0x5634504d/'MP4V' is not supported with codec id 12 and format 'mp4 / MP4 (MPEG-4 Part 14)' OpenCV: FFMPEG: fallback to use tag 0x7634706d/'mp4v'

xiaoqtbai的博客 804

OpenCV VideoWriter报错: FFMPEG:不支持标签“MP4V”与编解码器ID和格式mp4的解决方法 - Python

当遇到"FFMPEG: tag ‘MP4V’ is not supported with codec id and format mp4"错误时,我们可以通过更改编解码器或更改输出视频的格式来解决问题。如果上述方法没有解决问题,请尝试使用其他编解码器或输出格式,并确保系统中安装了适当的解码器。如果更改编解码器没有解决问题,我们可以尝试将输出视频的格式更改为其他格式,例如AVI。要解决这个问题,我们可以尝试两种方法:更改编解码器或更改输出视频的格式。在上面的示例中,我们将输出视频的名称更改为。

Meta_C的博客 1790

OpenCV自适应直方图均衡CLAHE源代码分享

然而,传统的全局直方图均衡化在处理具有不均匀光照的图像时,会导致过度增强噪声和细节,从而使图像变得不真实。CLAHE(Contrast Limited Adaptive Histogram Equalization)是AHE的变体,其目的是通过限制对比度增强,来避免AHE的缺陷。总之,CLAHE是一种非常有用的图像增强技术,能够消除图像中不均匀的光照引起的问题。在OpenCV中,我们可以很容易地使用CLAHE,并获得高质量的增强图像。最后,应用CLAHE到图像中,然后显示原图和处理后的图像。

BUG?不存在的! 1052

OpenCV自适应直方图均衡CLAHE图像和分块大小不能整除的处理

一、引言 最近一个月来都在研究OpenCV 中CLAHE算法的一些问题,如: 图像横向和纵向分块大小与图像的宽和高不能整除怎么处理? CLIP的剪裁是怎么实施的? 解决棋盘效应的具体插值处理过程怎样? 彩色图像怎么处理? 到处找资料,也看了部分博客所谓的源代码,结果还是没有找到答案,看来没有捷径,干脆直接下载了一份OpenCV的源代码来阅读。可惜自从没有亲手做C语言相关开发后,手上的机器连C++运行环境都没有,先直接读代码。本来想等所有问题都有答案时再写博文,不过这一阵子单位和家里事情都很多,没有多少.

老猿Python 4354

自适应直方图均衡化(CLAHE):Python和C++源代码分享

然而,传统的直方图均衡化方法在全局范围内对图像进行操作,可能导致细节丢失和过度增强的问题。首先,我们将图像转换为Lab颜色空间,并提取亮度通道。接下来,我们将CLAHE算法应用于亮度通道,并将结果合并回Lab颜色空间。通过使用以上的Python和C++源代码,您可以在自己的项目中实现自适应直方图均衡化(CLAHE)算法,以增强图像的对比度并获得更好的视觉效果。首先,我们将图像转换为Lab颜色空间,这是因为CLAHE算法对亮度通道进行操作。在C++实现中,我们使用了OpenCV的C++接口,并使用了。

2301_79366177的博客 864

C++与OpenCV图像处理实战:从滤波到物体检测的完整路径

图像处理是计算机视觉的基石,其核心在于通过算法对像素数据进行变换以提取有用信息。从原理层面看,滤波通过卷积核操作去除噪声或增强特征,边缘检测则基于梯度计算识别物体边界。这些基础技术为高级视觉任务提供了干净的输入和关键特征,其技术价值在于构建了从原始像素到语义理解的桥梁。在应用场景上,无论是工业质检、自动驾驶还是医学影像分析,都离不开图像预处理、特征提取和对象定位这一经典流程。本文聚焦于C++与OpenCV实现,深入探讨了高斯滤波、中值滤波等经典算法,并详解了如何通过轮廓分析实现物体定位,为集成深度学习模型奠

347

计算机视觉算法探究:OpenCV CLAHE 插值算法详解

本文介绍了 OpenCV 对比度受限的自适应直方图均衡 CLAHE 算法的图像插值算法。相关内容在网上公开资料中没有公布,是老猿断断续续花费了 4 个月时间研究剖析,并在春节期间花了大量精力整理的,对计算机视觉算法感兴趣但不熟悉自适应直方图均衡 CLAHE 算法的各位同好应该是非常有帮助的资料。

老猿Python 4937

基于OpenCV C++的低照度图像增强实战:伽马校正与CLAHE流程

低照度图像增强是计算机视觉与图像处理中的经典难题,其核心在于解决动态范围压缩与信噪比失衡带来的细节丢失问题。夜间监控、工业检测、车载视觉等场景常因光照不足导致画面整体偏暗、对比度低下且噪声显著。OpenCV作为成熟的开源视觉库,提供高效的像素操作与算法模块,结合C++的内存可控与高性能优势,非常适合构建嵌入式和实时处理管线。本文从直方图分析出发,介绍自适应伽马校正恢复全局亮度,利用CLAHE(对比度受限自适应直方图均衡化)提升局部细节,配合去噪与颜色恢复步骤,形成一套可落地的完整增强流程。通过参数调优与工程

weixin_29041767的博客 279

C++与OpenCV实现车道线检测:从图像处理到霍夫变换的完整指南

计算机视觉中的图像处理是理解场景信息的基础技术,其核心原理在于通过算法对图像进行变换和特征提取。在自动驾驶和辅助驾驶领域,车道线检测是一项关键技术,它依赖于边缘检测、霍夫变换等经典算法来识别结构化道路。从工程实践角度看,掌握C++结合OpenCV库进行图像预处理、ROI提取和直线拟合,能够构建出稳定、可解释的视觉感知模块。本文聚焦于使用C++和OpenCV,通过**霍夫变换**和**Canny边缘检测**等核心步骤,详细拆解如何从零实现一个车道线检测器,涵盖了参数调优、视频流处理及常见问题排查,为学习传统计

393

OpenCV与Python智能图像处理:从环境搭建到项目实战全解析

图像处理是计算机视觉的基础,其核心原理是将图像视为像素矩阵,通过算法对其进行变换与分析。OpenCV作为开源计算机视觉库,提供了从基础操作到高级算法的完整工具链,技术价值在于能高效实现图像增强、特征提取与目标识别等任务,广泛应用于人脸识别、自动驾驶、医学影像等领域。本文聚焦于OpenCV与Python的工程实践,详细拆解了环境配置、核心API调用与性能优化等关键环节,并深入剖析了实时人脸检测、文档扫描矫正等典型应用场景的实现细节。通过结合直方图均衡化、Canny边缘检测等热词技术,为初学者构建从理论到实践的

weixin_34033624的博客 368

解决:OpenCV: FFMPEG: tag 0x5634504d/‘MP4V‘ is not supported with codec id 12 and format ‘mp4 / MP4

问题: 利用python 的opencv包把图片合并为视频(mp4格式)时出现如下报错: OpenCV: FFMPEG: tag 0x5634504d/'MP4V' is not supported with codec id 12 and format 'mp4 / MP4 (MPEG-4 Part 14)' OpenCV: FFMPEG: fallback to use tag 0x7634706d/'mp4v' ———————————————— 版权声明:本文为优快云博主「酉意铭」的原创文章,

你若盛开,清风自来。 9008

Python+OpenCV实现智能停车场车牌识别计费系统全流程实战

车牌识别技术是智能交通与停车管理领域的核心应用之一,通常涉及图像采集、车牌定位、字符识别等计算机视觉环节。基于Python与OpenCV构建的车牌识别系统,能够通过HSV色彩空间提取车牌区域,结合轮廓检测与透视变换完成字符分割,再借助模板匹配或轻量级CNN实现高精度识别。在工程落地中,系统还需集成计费引擎与SQLite数据库,实现车辆出入场记录、停车时长计算和费用结算。借助PyInstaller打包成exe可执行程序,配合配置文件与使用说明,即可交付给非技术用户直接运行。该方案适用于毕业设计、中小型停车场管

weixin_34218579的博客 397

在Jetson orin NX上安装部署Yolov8

1、环境准备与依赖安装2、安装PyTorch和Torchvision:从NVIDIA官方论坛或开发者下载中心获取与你的JetPack版本兼容的PyTorch wheel文件。例如,对于JetPack 5.1.2,可以安装PyTorch v2.1.0 8。:需要编译安装与PyTorch版本匹配的Torchvision。这里自己编译没有成功,下载了一个现成的可以用torchvision-0.16.2+c6f3977-cp38-cp38-linux_aarch64.whl。

xxxcccvvvbbb的博客 776

对比度受限自适应直方图均衡化方法

图像增强技术可具体为时域和频域。 时域增强往往被应用于提高图像的对比度且改进其灰度级, 其原理是在灰度映射转化的基础上, 对像素进行直接性地处理; 频域增强的作用是以强化图像的低、 高频来达到改善图像的平滑性和边缘为主,其原理是通过傅里叶转换的方式提升兴趣区的频率分量。 图像灰度分布不均的问题对于图像处理技术而言是一个影响较大的问题, 为了能解决这个问题, 基于时域增强理论, 采用映射函数...

zengshaoqing的博客 5155

用FPGA控制步进电机是种什么体验?从状态机到分频器,详解Verilog驱动A4988全流程

本文详细介绍了如何使用FPGA通过Verilog语言控制步进电机,从状态机设计到A4988驱动模块的精准控制全流程。FPGA的并行处理能力和纳秒级响应速度使其在步进电机控制中展现出显著优势,特别适合工业自动化和精密仪器应用。通过实战代码示例,展示了如何实现高效的状态机设计和可编程分频器,为开发者提供了一套完整的FPGA驱动步进电机解决方案。

weixin_30983965的博客 329

OpenCV: FFMPEG: tag 0x5634504d/‘MP4V‘ is not supported with codec id 12 and format ‘mp4 / MP4

我的呆头鬼女友的博客 2596

解忧杂货铺(四):Hightec生成HEX方法+小功能开启

Hightec使用的一些常用技能

梦想技术家 2160

树莓派4B学习笔记一

树莓派4b烧录Ubuntu20.04&&配置Ubuntu桌面

m0_55202222的博客 3635
上一篇: OpenCV自适应直方图均衡CLAHE图像和分块大小不能整除的处理
下一篇: OpenCV自适应直方图均衡CLAHE的clipLimit的含义及理解
LaoYuanPython
LaoYuanPython 2020年博客之星Top3 2020年博客之星Top3 领域专家: Python技术领域 领域专家: Python技术领域
博客等级 码龄8年 15万+粉丝 1324原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LaoYuanPython

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值