主頁 > 後端開發 > opencv-12-高斯濾波-雙邊濾波(附C++代碼實作)

opencv-12-高斯濾波-雙邊濾波(附C++代碼實作)

2020-09-13 20:24:52 後端開發

開始之前

這幾天由于自己的原因沒有寫, 一個是因為自己懶了, 一個是感覺這里遇到點問題不想往下寫了, 我們先努力結束這個章節吧, 之前介紹了比較常用而且比較好理解的均值和中值濾波, 但是呢,在例程Smoothing Images, 還有給出的其他的濾波方式, 主要是高斯濾波和雙邊濾波,

我們這一次完結掉濾波與平滑的這個部分, 寫的有點多了,反而不想再寫了, 加油

目錄

  • 開始之前
  • 本文目標
  • 正文
    • 高斯濾波(Gaussian Filter)
      • 高斯濾波原理
      • C++ 實作
      • opencv 高斯濾波
      • 高斯濾波效果對比
    • 雙邊濾波(Bilateral Filter)
      • 雙邊濾波原理
      • C++ 實作 雙邊濾波
      • opencv 實作 雙邊濾波
      • 雙邊濾波演算法對比
  • 總結
  • 參考鏈接

本文目標

本文主要是介紹

  • 高斯濾波
  • 雙邊濾波

和之前介紹的一樣, 我們仍然還是 介紹一下原理, 給出一下具體的形式, 然后使用 opencv 進行一下實作的程序, 最后使用我們之前的影像進行測驗 進行演算法的分析與總結.

正文

高斯濾波(Gaussian Filter)

我們在之前介紹了中值濾波是統計排序的結果, 屬于非線性的結果, 均值濾波是使用模板核進行的操作, 我們在的文章中也提到了均值濾波在計算的程序中必須要考慮權重的問題, 進而提出了加權的均值濾波的操作, 比如最常見的加權均值濾波的操作核.

\[M = \frac{1}{16} \left [ \begin{array}{c} 1 & 2 & 1 \\ 2& 4 & 2 \\ 1 & 2 & 1 \end{array} \right ] \]

其實呢,這個核也就是高斯濾波器在 3*3視窗的離散取整的結果值, 最明顯的特點就是模板的系數隨著距離模板中心的距離而變換, 能夠有效的抑制噪聲,平滑影像, 相比均值濾波能夠更好的平滑影像, 保留影像邊緣.

高斯濾波原理

由于我們的影像是二維的, 但是高斯分布是一維的, 那我們先考慮一維的高斯分布, 就是我們常用的正太分布曲線,

\[G(x) = \frac{1}{\sqrt{2\pi \sigma}} e^{-\frac{x^2}{2\sigma^2}} \]

一維高斯分布

對于二維的高斯分布其實可以考慮成兩個方向的運算相疊加的得到的結果

\[G(x,y) = \frac{1}{2\pi \sigma^2} e^{-\frac{x^2+y^2}{2\sigma^2}} = G(x)*G(y) \]

二維高斯分布

考慮到影像的計算實際上是離散的座標, 對于視窗大小為 \((2k + 1) \times (2k + 1)\) 模板, 我們可以表示成

\[G{i,j} = \frac{1}{2\pi \sigma ^ 2}e ^{-\frac{(i - k - 1)^2 + (j - k - 1)^2}{2 \sigma ^ 2}} \]

可以參考影像處理基礎(4):高斯濾波器詳解
里面給出的方法, 使用

void generateGaussianTemplate(double window[][11], int ksize, double sigma)
{
    static const double pi = 3.1415926;
    int center = ksize / 2; // 模板的中心位置,也就是坐標的原點
    double x2, y2;
    for (int i = 0; i < ksize; i++)
    {
        x2 = pow(i - center, 2);
        for (int j = 0; j < ksize; j++)
        {
            y2 = pow(j - center, 2);
            double g = exp(-(x2 + y2) / (2 * sigma * sigma));
            g /= 2 * pi * sigma*sigma;	// 
            window[i][j] = g;
        }
    }
    double k = 1 / window[0][0]; // 將左上角的系數歸一化為1
    for (int i = 0; i < ksize; i++)
    {
        for (int j = 0; j < ksize; j++)
        {
            window[i][j] *= k;
        }
    }
}

生成了\(3 \times 3, \sigma = 0.8\) 的高斯模板, 對應的將其取整就得到了

\[M = \frac{1}{16} \left [ \begin{array}{c} 1 & 2 & 1 \\ 2& 4 & 2 \\ 1 & 2 & 1 \end{array} \right ] \]

上面給出的文章同樣的詳細介紹了 \(\sigma\) 在統計學中的意義, 可以去參考學習
不過根據高中的知識, 我們可以看到 正態分布的曲線

正態分布曲線

C++ 實作

在我們之前提到的影像處理基礎(4):高斯濾波器詳解 這里給出了基于 opencv 的代碼實作, 這里是\(O(m*n*k^2)\) 的演算法實作

// 來源鏈接: https://www.cnblogs.com/wangguchangqing/p/6407717.html
void GaussianFilter(const Mat &src, Mat &dst, int ksize, double sigma)
{
    CV_Assert(src.channels() || src.channels() == 3); // 只處理單通道或者三通道影像
    const static double pi = 3.1415926;
    // 根據視窗大小和sigma生成高斯濾波器模板
    // 申請一個二維陣列,存放生成的高斯模板矩陣
    double **templateMatrix = new double*[ksize];
    for (int i = 0; i < ksize; i++)
        templateMatrix[i] = new double[ksize];
    int origin = ksize / 2; // 以模板的中心為原點
    double x2, y2;
    double sum = 0;
    for (int i = 0; i < ksize; i++)
    {
        x2 = pow(i - origin, 2);
        for (int j = 0; j < ksize; j++)
        {
            y2 = pow(j - origin, 2);
            // 高斯函式前的常數可以不用計算,會在歸一化的程序中給消去
            double g = exp(-(x2 + y2) / (2 * sigma * sigma));
            sum += g;
            templateMatrix[i][j] = g;
        }
    }
    for (int i = 0; i < ksize; i++)
    {
        for (int j = 0; j < ksize; j++)
        {
            templateMatrix[i][j] /= sum;
            cout << templateMatrix[i][j] << " ";
        }
        cout << endl;
    }
    // 將模板應用到影像中
    int border = ksize / 2;
    copyMakeBorder(src, dst, border, border, border, border, BorderTypes::BORDER_REFLECT);
    int channels = dst.channels();
    int rows = dst.rows - border;
    int cols = dst.cols - border;
    for (int i = border; i < rows; i++)
    {
        for (int j = border; j < cols; j++)
        {
            double sum[3] = { 0 };
            for (int a = -border; a <= border; a++)
            {
                for (int b = -border; b <= border; b++)
                {
                    if (channels == 1)
                    {
                        sum[0] += templateMatrix[border + a][border + b] * dst.at<uchar>(i + a, j + b);
                    }
                    else if (channels == 3)
                    {
                        Vec3b rgb = dst.at<Vec3b>(i + a, j + b);
                        auto k = templateMatrix[border + a][border + b];
                        sum[0] += k * rgb[0];
                        sum[1] += k * rgb[1];
                        sum[2] += k * rgb[2];
                    }
                }
            }
            for (int k = 0; k < channels; k++)
            {
                if (sum[k] < 0)
                    sum[k] = 0;
                else if (sum[k] > 255)
                    sum[k] = 255;
            }
            if (channels == 1)
                dst.at<uchar>(i, j) = static_cast<uchar>(sum[0]);
            else if (channels == 3)
            {
                Vec3b rgb = { static_cast<uchar>(sum[0]), static_cast<uchar>(sum[1]), static_cast<uchar>(sum[2]) };
                dst.at<Vec3b>(i, j) = rgb;
            }
        }
    }
    // 釋放模板陣列
    for (int i = 0; i < ksize; i++)
        delete[] templateMatrix[i];
    delete[] templateMatrix;
}

然后同樣的給出了分離的實作, 將影像進行水平運算之后再進行豎直運算, 計算的時間上會有一定的速度提升

// 來源鏈接: https://www.cnblogs.com/wangguchangqing/p/6407717.html
// 分離的計算
void separateGaussianFilter(const Mat &src, Mat &dst, int ksize, double sigma)
{
    CV_Assert(src.channels()==1 || src.channels() == 3); // 只處理單通道或者三通道影像
    // 生成一維的高斯濾波模板
    double *matrix = new double[ksize];
    double sum = 0;
    int origin = ksize / 2;
    for (int i = 0; i < ksize; i++)
    {
        // 高斯函式前的常數可以不用計算,會在歸一化的程序中給消去
        double g = exp(-(i - origin) * (i - origin) / (2 * sigma * sigma));
        sum += g;
        matrix[i] = g;
    }
    // 歸一化
    for (int i = 0; i < ksize; i++)
        matrix[i] /= sum;
    // 將模板應用到影像中
    int border = ksize / 2;
    copyMakeBorder(src, dst, border, border, border, border, BorderTypes::BORDER_REFLECT);
    int channels = dst.channels();
    int rows = dst.rows - border;
    int cols = dst.cols - border;
    // 水平方向
    for (int i = border; i < rows; i++)
    {
        for (int j = border; j < cols; j++)
        {
            double sum[3] = { 0 };
            for (int k = -border; k <= border; k++)
            {
                if (channels == 1)
                {
                    sum[0] += matrix[border + k] * dst.at<uchar>(i, j + k); // 行不變,列變化;先做水平方向的卷積
                }
                else if (channels == 3)
                {
                    Vec3b rgb = dst.at<Vec3b>(i, j + k);
                    sum[0] += matrix[border + k] * rgb[0];
                    sum[1] += matrix[border + k] * rgb[1];
                    sum[2] += matrix[border + k] * rgb[2];
                }
            }
            for (int k = 0; k < channels; k++)
            {
                if (sum[k] < 0)
                    sum[k] = 0;
                else if (sum[k] > 255)
                    sum[k] = 255;
            }
            if (channels == 1)
                dst.at<uchar>(i, j) = static_cast<uchar>(sum[0]);
            else if (channels == 3)
            {
                Vec3b rgb = { static_cast<uchar>(sum[0]), static_cast<uchar>(sum[1]), static_cast<uchar>(sum[2]) };
                dst.at<Vec3b>(i, j) = rgb;
            }
        }
    }
    // 豎直方向
    for (int i = border; i < rows; i++)
    {
        for (int j = border; j < cols; j++)
        {
            double sum[3] = { 0 };
            for (int k = -border; k <= border; k++)
            {
                if (channels == 1)
                {
                    sum[0] += matrix[border + k] * dst.at<uchar>(i + k, j); // 列不變,行變化;豎直方向的卷積
                }
                else if (channels == 3)
                {
                    Vec3b rgb = dst.at<Vec3b>(i + k, j);
                    sum[0] += matrix[border + k] * rgb[0];
                    sum[1] += matrix[border + k] * rgb[1];
                    sum[2] += matrix[border + k] * rgb[2];
                }
            }
            for (int k = 0; k < channels; k++)
            {
                if (sum[k] < 0)
                    sum[k] = 0;
                else if (sum[k] > 255)
                    sum[k] = 255;
            }
            if (channels == 1)
                dst.at<uchar>(i, j) = static_cast<uchar>(sum[0]);
            else if (channels == 3)
            {
                Vec3b rgb = { static_cast<uchar>(sum[0]), static_cast<uchar>(sum[1]), static_cast<uchar>(sum[2]) };
                dst.at<Vec3b>(i, j) = rgb;
            }
        }
    }
    delete[] matrix;
}

這里的演算法都是 上面提到的https://www.cnblogs.com/wangguchangqing/p/6407717.html 這篇文章, 具體可以去看內容

opencv 高斯濾波

其實這篇文章影像處理--高斯濾波寫的很好
其實主要的結構也就是他給出的程序

高斯函式呼叫圖

高斯濾波函式呼叫簡圖

其實整個高斯濾波的程序就是創建高斯核, 然后使用 filter2D 的方法進行的濾波操作, 具體要深入的話可以看函式的呼叫圖, 實作起來也是一樣的思路, 很簡單的操作, 我們之后測驗一下效果..

// /modules\imgproc\src\smooth.dispatch.cpp:600
void GaussianBlur(InputArray _src, OutputArray _dst, Size ksize,
                  double sigma1, double sigma2,
                  int borderType)
  • src ?輸入影像
  • dst 輸出影像
  • ksize 核的尺寸 奇數
  • sigmaX x 方向 的 sigma 值
  • sigmaY ?y 方向 的 sigma 值
  • borderType 邊界處理的方式

高斯濾波效果對比

我們還是使用之前的高椒鹽噪聲影像, 然后直接進行演算法濾波, 計算結果就好, 跟之前的測驗影像很相似, 這里

測驗結果圖

這里的四張圖分別對應 高噪聲影像, 直接高斯濾波的結果, 分離xy方向進行濾波結果,以及opencv 自帶的高斯濾波效果圖, 這里是預覽影像, 實際的檢測結果就是上面給出的引數值, 實際上效果只能說一般, 我們之后再進行演算法層面的對比.

image-noise: psnr:19.4727, mssim: B:0.353134 G:0.383638 R:0.629353
image-noise: psnr:26.3155, mssim: B:0.584585 G:0.617172 R:0.812303
image-noise: psnr:26.1721, mssim: B:0.574719 G:0.607494 R:0.809844
image-noise: psnr:26.4206, mssim: B:0.598176 G:0.630657 R:0.819658

雙邊濾波(Bilateral Filter)

雙邊濾波原理

我們在上面提出了高斯濾波的原理是對于距離模板中心 距離不同給予不同的權重, 而雙邊濾波則不僅考慮影像的空間距離, 還要考慮其灰度距離, 對于越接近中間灰度值的點權重越高, 灰度值相差大的則權重更小.

雙邊濾波的原理可以參考雙邊濾波(Bilateral Filter)詳解,
可以參考Bilateral Filtering for Gray and Color Images

雙邊濾波原理

在文章影像處理基礎(5):雙邊濾波器詳細介紹了雙邊濾波
其實跟上面給出的濾波演示一致, 都是在保證影像邊緣資訊的情況下進行噪聲的濾波..

雙邊濾波原理

可以參考bilateral filter雙邊濾波器的通俗理解 給出的雙邊濾波的數學表達

\[g(x,y) = \frac{\sum_{kl}f(k,l)w(i,j,k,l)}{\sum_{kl}w(i,j,k,l)} \]

對于不同的模板系數又有兩個部分, 主要是 空間域模板權值 \(w_d\) 和 灰度域 模板權值 \(w_r\),

\[\begin{array}{rl} w_d(i,j,k,l) &= e^{-\frac{(i-k)^2 +(j-l)^2}{2\sigma_d^2}} \\ w_r(i,j,k,l) &= e^{-\frac{\left \| f(i,j) - f(k,l) \right \|} {2\sigma_r^2}} \\ w &= w_d * w_r \end{array} \]

其中,\(q(i,j)\) 為模板視窗的其他系數的坐標,\(f(i,j)\) 表示影像在點\(q(i,j)\) 處的像素值;\(p(k,l)\) 為模板視窗的中心坐標點,對應的像素值為\(f(k,l)\)\(\sigma_r\) 為高斯函式的標準差,

C++ 實作 雙邊濾波

感覺這里寫的挺好的 影像處理基礎(5):雙邊濾波器, 手動實作了雙邊濾波, 我們可以詳細的參考, 這里

// 參考來源: https://www.cnblogs.com/wangguchangqing/p/6416401.html
void myBilateralFilter(const Mat &src, Mat &dst, int ksize, double space_sigma, double color_sigma)
{
    int channels = src.channels();
    CV_Assert(channels == 1 || channels == 3);
    double space_coeff = -0.5 / (space_sigma * space_sigma);
    double color_coeff = -0.5 / (color_sigma * color_sigma);
    int radius = ksize / 2;
    Mat temp;
    copyMakeBorder(src, temp, radius, radius, radius, radius, BorderTypes::BORDER_REFLECT);
    vector<double> _color_weight(channels * 256); // 存放差值的平方
    vector<double> _space_weight(ksize * ksize); // 空間模板系數
    vector<int> _space_ofs(ksize * ksize); // 模板視窗的坐標
    double *color_weight = &_color_weight[0];
    double *space_weight = &_space_weight[0];
    int    *space_ofs = &_space_ofs[0];
    for (int i = 0; i < channels * 256; i++)
        color_weight[i] = exp(i * i * color_coeff);
    // 生成空間模板
    int maxk = 0;
    for (int i = -radius; i <= radius; i++)
    {
        for (int j = -radius; j <= radius; j++)
        {
            double r = sqrt(i*i + j * j);
            if (r > radius)
                continue;
            space_weight[maxk] = exp(r * r * space_coeff); // 存放模板系數
            space_ofs[maxk++] = i * temp.step + j * channels; // 存放模板的位置,和模板系數相對應
        }
    }
    // 濾波程序
    for (int i = 0; i < src.rows; i++)
    {
        const uchar *sptr = temp.data + (i + radius) * temp.step + radius * channels;
        uchar *dptr = dst.data + i * dst.step;
        if (channels == 1)
        {
            for (int j = 0; j < src.cols; j++)
            {
                double sum = 0, wsum = 0;
                int val0 = sptr[j]; // 模板中心位置的像素
                for (int k = 0; k < maxk; k++)
                {
                    int val = sptr[j + space_ofs[k]];
                    double w = space_weight[k] * color_weight[abs(val - val0)]; // 模板系數 = 空間系數 * 灰度值系數
                    sum += val * w;
                    wsum += w;
                }
                dptr[j] = (uchar)cvRound(sum / wsum);
            }
        }
        else if (channels == 3)
        {
            for (int j = 0; j < src.cols * 3; j+=3)
            {
                double sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0;
                int b0 = sptr[j];
                int g0 = sptr[j + 1];
                int r0 = sptr[j + 2];
                for (int k = 0; k < maxk; k++)
                {
                    const uchar *sptr_k = sptr + j + space_ofs[k];
                    int b = sptr_k[0];
                    int g = sptr_k[1];
                    int r = sptr_k[2];
                    double w = space_weight[k] * color_weight[abs(b - b0) + abs(g - g0) + abs(r - r0)];
                    sum_b += b * w;
                    sum_g += g * w;
                    sum_r += r * w;
                    wsum += w;
                }
                wsum = 1.0f / wsum;
                b0 = cvRound(sum_b * wsum);
                g0 = cvRound(sum_g * wsum);
                r0 = cvRound(sum_r * wsum);
                dptr[j] = (uchar)b0;
                dptr[j + 1] = (uchar)g0;
                dptr[j + 2] = (uchar)r0;
            }
        }
    }
}

opencv 實作 雙邊濾波

void bilateralFilter( InputArray _src, OutputArray _dst, int d,
                      double sigmaColor, double sigmaSpace,
                      int borderType )
  • InputArray src: 輸入影像,可以是Mat型別,影像必須是8位或浮點型單通道、三通道的影像,
  • OutputArray dst: 輸出影像,和原影像有相同的尺寸和型別,
  • int d: 表示在過濾程序中每個像素鄰域的直徑范圍,如果這個值是非正數,則函式會從第五個引數sigmaSpace計算該值,
  • double sigmaColor: 顏色空間過濾器的sigma值,這個引數的值月大,表明該像素鄰域內有越寬廣的顏色會被混合到一起,產生較大的半相等顏色區域, (這個引數可以理解為值域核的)
  • double sigmaSpace: 坐標空間中濾波器的sigma值,如果該值較大,則意味著越遠的像素將相互影響,從而使更大的區域中足夠相似的顏色獲取相同的顏色,當d>0時,d指定了鄰域大小且與sigmaSpace無關,否則d正比于sigmaSpace. (這個引數可以理解為空間域核的)
  • int borderType=BORDER_DEFAULT: 用于推斷影像外部像素的某種邊界模式,有默認值BORDER_DEFAULT.

雙邊濾波函式呼叫圖

雙邊濾波演算法對比

一開始的時候看雙邊濾波真的搞不懂, 也不知道這么做有什么目的, 最終的結果又代表什么, 我們按照之前的方法去測驗我們的影像, 結果真的是幾種演算法中最差的了, 但是這只是說不適用于我們的影像結果, 在實際使用程序中還是要進行測驗之后才能得出結論

測驗結果如下: 對應原始圖和 手動實作的結果以及 opencv 的結果 都使用的 是3 的視窗, sigma 的值 為 255
, 這篇文章https://blog.csdn.net/Jfuck/article/details/8932978 講的很好, 介紹了引數對濾波的影響, 可以學習一下..

image-noise: psnr:19.4727, mssim: B:0.353134 G:0.383638 R:0.629353
image-noise: psnr:24.4502, mssim: B:0.538774 G:0.570666 R:0.776195
image-noise: psnr:24.4691, mssim: B:0.539177 G:0.571087 R:0.776461

雙邊濾波演算法

總結

其實個人使用雙邊濾波真的不算很多, 在之前研究導向濾波的時候才了解過很多, 這里寫的比較差吧, 只能說勉強能看, 強烈推薦 https://www.cnblogs.com/wangguchangqing/category/740760.html 這個系列, 將的很詳細, 很多都是博文里面的內容, 可以參考學習, 高斯濾波就比較簡單了, 其實復雜的濾波程序主要是理解演算法, 然后根據演算法的思路進行代碼的實作程序, 最后做一定的程式上的優化就好, 理解第一, 實作其次.. 希望帶給讀者一點點啟發..

我這里一開始不準備寫這么多的, 結果越寫越多, 導致自己收不住了, 很多自己說不上很了解的地方, 這一次也是深入的了解了一下, 但是還是很僵硬, 只能說能用而已, 這里還是推薦看我給出的鏈接或者自己去查閱相關的內容, 我這里只是給出一個大略的介紹, 如果有錯誤還請指名, 十分感謝

參考鏈接

  1. 《快速高斯濾波、高斯模糊、高斯平滑(二維卷積分步為一維卷積)_人工智能_青城山小和尚-CSDN博客》. 見于 2020年5月10日. https://blog.csdn.net/qq_36359022/article/details/80188873.
  2. 《雙邊濾波 - 旗亭涉的博客 | Qitingshe Blog》. 見于 2020年5月10日. https://qitingshe.github.io/2018/06/14/雙邊濾波/.
  3. 《雙邊濾波(Bilateral Filter)詳解_人工智能_Jfuck的專欄-CSDN博客》. 見于 2020年5月10日. https://blog.csdn.net/Jfuck/article/details/8932978.
  4. 《雙邊濾波器》. 收入 維基百科,自由的百科全書, 2019年11月16日. https://zh.wikipedia.org/w/index.php?title=雙邊濾波器&oldid=56898678.
  5. 《影像處理--高斯濾波_網路_L-inYi的專欄-CSDN博客》. 見于 2020年5月10日. https://blog.csdn.net/L_inYi/article/details/8915116.
  6. 《影像處理基礎(4):高斯濾波器詳解 - Brook_icv - 博客園》. 見于 2020年5月10日. https://www.cnblogs.com/wangguchangqing/p/6407717.html.
  7. 《影像處理基礎(5):雙邊濾波器 - Brook_icv - 博客園》. 見于 2020年5月10日. https://www.cnblogs.com/wangguchangqing/p/6416401.html.
  8. 《影像處理-線性濾波-3 高斯濾波器 - Tony Ma - 博客園》. 見于 2020年5月10日. https://www.cnblogs.com/pegasus/archive/2011/05/20/2052031.html.
  9. 《【轉】高斯影像濾波原理及其編程離散化實作方法_Smile_Gogo_新浪博客》. 見于 2020年5月10日. http://blog.sina.com.cn/s/blog_640577ed0100yz8v.html.
  10. 《bilateral filter雙邊濾波器的通俗理解_網路_pan_jinquan的博客-CSDN博客》. 見于 2020年5月10日. https://blog.csdn.net/guyuealian/article/details/82660826.
  11. 《Bilateral Filtering》. 見于 2020年5月10日. http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html.
  12. 《Cv影像處理 - OpenCV China :影像處理,計算機視覺庫,Image Processing, Computer Vision》. 見于 2020年5月10日. http://wiki.opencv.org.cn/index.php/Cv影像處理.
  13. 《o(1)復雜度之雙邊濾波演算法的原理、流程、實作及效果, - 云+社區 - 騰訊云》. 見于 2020年5月10日. https://cloud.tencent.com/developer/article/1011738.

本文由博客一文多發平臺 OpenWrite 發布!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/27252.html

標籤:C++

上一篇:C++ 自動變數

下一篇:C++ 靜態持續變數

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more