opencv 图像锐化_(二十三)用梯度(一阶微分)实现图像锐化

本文介绍了如何使用OpenCV通过一阶偏导数(梯度)进行图像锐化。首先,解释了微分在图像锐化中的作用,接着探讨了一阶偏导数的概念,并将其应用于数字图像。内容涵盖了罗伯特交叉算子和Sobel算子,展示了OpenCV中用于梯度锐化的函数及参数设置,并提供了示例代码。
摘要由CSDN通过智能技术生成

2416b5e97488abdad3f1388fb7966ef6.png

时间为友,记录点滴。

我在《数字图像处理》专栏的锐化空间滤波中聊过了什么是图像的锐化,以及为什么可以用微分可以实现图像的锐化。在这里,我们抛开细节,把思路理一下:

定性的思考方向是:

既然可以通过积分的思想来做图像的模糊,是不是可以通过积分的反操作微分来实现模糊的反操作-锐化?

定量的思考方向是:

我们把微分的过程当分别求x, y方向上的偏导,那么可以想象:

  1. 在恒定灰度区域内的微分值为0
  2. 在灰度台阶或斜坡处微分值非0
  3. 沿着斜坡的微分值为0

bd617b8764160a280f0bf9e41e25d4b3.png

从上图可以从逻辑上讲得通了。

怎么实现呢?

我们先以一阶偏导为例,具体的推导过程可以参考《数字图像处理》专栏中的梯度介绍。

1. 首先要引入,什么是一阶偏导,它的形式是什么?

equation?tex=%5Cfrac%7B%5Cpartial+f%7D%7B%5Cpartial+x%7D%3Df%28x%2B1%29-f%28x%29

2. 然后再看数字图像中的一阶偏导的形式是什么?

我们可以用梯度代表,为什么是梯度?因为梯度是一张二维平面上最大一阶偏导的形式。

equation?tex=%5Cnabla+f+%5Cequiv+%5Coperatorname%7Bgrad%7D%28f%29+%5Cequiv%5Cleft%5B%5Cbegin%7Barray%7D%7Bl%7D%7Bg_%7Bx%7D%7D+%5C%5C+%7Bg_%7By%7D%7D%5Cend%7Barray%7D%5Cright%5D%3D%5Cleft%5B%5Cbegin%7Barray%7D%7Bl%7D%7B%5Cfrac%7B%5Cpartial+f%7D%7B%5Cpartial+x%7D%7D+%5C%5C+%7B%5Cfrac%7B%5Cpartial+f%7D%7B%5Cpartial+y%7D%7D%5Cend%7Barray%7D%5Cright%5D

3. 两个公式怎么结合?

当然是求梯度的模。

equation?tex=M%28x%2C+y%29%3D%5Coperatorname%7Bmag%7D%28%5Cnabla+f%29%3D%5Csqrt%7Bg_%7Bx%7D%5E%7B2%7D%2Bg_%7By%7D%5E%7B2%7D%7D

4. 有了公式怎么引入算子

偷一下懒,抄过来把。

假设现在有一幅图像其中的一个3x3的区域:

257047798788b0683fbf110509690acc.png
一幅图像的局部:3x3大小,从z1到z9

其中

equation?tex=z_5 是其中心(目标)像素, 由前面的一阶微分知识(
equation?tex=%5Cfrac%7B%5Cpartial+f%7D%7B%5Cpartial+x%7D%3Df%28x%2B1%29-f%28x%29 )我们可以得到它对应的
equation?tex=g_x
equation?tex=g_y 分别为
equation?tex=g_%7Bx%7D%3D%5Cleft%28z_%7B8%7D-z_%7B5%7D%5Cright%29
equation?tex=g_%7By%7D%3D%5Cleft%28z_%7B6%7D-z_%7B5%7D%5Cright%29
  • 罗伯特交叉算子

不过,这里我们介绍另外一种罗伯特交叉算子,他们被定义为:

equation?tex=g_%7Bx%7D%3D%5Cleft%28z_%7B9%7D-z_%7B5%7D%5Cright%29

equation?tex=g_%7By%7D%3D%5Cleft%28z_%7B8%7D-z_%7B6%7D%5Cright%29

由此,我们可以计算梯度图像

equation?tex=M%28x%2C+y%29%3D%5Cleft%5B%5Cleft%28z_%7B9%7D-z_%7B5%7D%5Cright%29%5E%7B2%7D%2B%5Cleft%28z_%7B8%7D-z_%7B6%7D%5Cright%29%5E%7B2%7D%5Cright%5D%5E%7B1+%2F+2%7D

罗伯特交叉算子对应的模板是:

c3f6caa800013e7af708727273fb06ee.png
目标像素的右下角四
  • Sobel算子

另外一种称为Sobel算子,中心权重使用2用来突出中心点的作用

equation?tex=g_%7Bx%7D%3D%5Cfrac%7B%5Cpartial+f%7D%7B%5Cpartial+x%7D%3D%5Cleft%28z_%7B7%7D%2B2+z_%7B8%7D%2Bz_%7B9%7D%5Cright%29-%5Cleft%28z_%7B1%7D%2B2+z_%7B2%7D%2Bz_%7B3%7D%5Cright%29

equation?tex=g_%7By%7D%3D%5Cfrac%7B%5Cpartial+f%7D%7B%5Cpartial+y%7D%3D%5Cleft%28z_%7B3%7D%2B2+z_%7B6%7D%2Bz_%7B9%7D%5Cright%29-%5Cleft%28z_%7B1%7D%2B2+z_%7B4%7D%2Bz_%7B7%7D%5Cright%29

对应的梯度图像:

equation?tex=M%28x%2C+y%29+%5Capprox%5Cleft%7C%5Cleft%28z_%7B7%7D%2B2+z_%7B8%7D%2Bz_%7B9%7D%5Cright%29-%5Cleft%28z_%7B1%7D%2B2+z_%7B2%7D%2Bz_%7B3%7D%5Cright%29%5Cright%7C%2B%5Cleft%7C%5Cleft%28z_%7B3%7D%2B2+z_%7B6%7D%2Bz_%7B9%7D%5Cright%29-%5Cleft%28z_%7B1%7D%2B2+z_%7B4%7D%2Bz_%7B7%7D%5Cright%29%5Cright%7C

ccc8a329df4f5f4a1860d1ce08abdc49.png


完美。

更完美的是,OpenCV中已经帮我们时间了梯度锐化的函数:

比如参考OpenCv官方给的AIP介绍:

void cv::Sobel	(	InputArray 	src,
OutputArray 	dst,
int 	ddepth,
int 	dx,
int 	dy,
int 	ksize = 3,
double 	scale = 1,
double 	delta = 0,
int 	borderType = BORDER_DEFAULT 
)	
  • src input image.
  • dst output image of the same size and the same number of channels as src
  • ddepth output image depth, see combinations; in the case of 8-bit input images it will result in truncated derivatives.

77a24a53a6d6d55d4ecaa0e1badd4e6f.png
  • dx order of the derivative x
  • dy order of the derivative y
  • ksize size of the extended Sobel kernel; it must be 1, 3, 5, or 7.
  • scale optional scale factor for the computed derivative values; by default, no scaling is applied (see getDerivKernels for details).放缩比率,1 表示不变
  • delta optional delta value that is added to the results prior to storing them in dst.对输出结果图像加上常量值
  • borderType pixel extrapolation method, see BorderTypes

C++

注意点:

1 Scharr算子是Sobel的增强版,但是不知道为什么对x, y方向不能一块微分。

55723af14e646f44ad8a90267b70867a.png
2. convertScaleAbs,是做加权求绝对值并转转成8bit用的。参考如下公式

equation?tex=dst%28I%29%3Dsaturate%5C_cast%3Cuchar%3E%28%7Csrc%28I%29%E2%88%97alpha%2Bbeta%7C%29
#include <iostream>
#include <string>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

static bool scharrTest(Mat imgOri);
static bool sobelTest(Mat imgOri);
static void showImgPara(Mat &img);

int main()
{
	Mat imgOri = imread("Fig0342(a)(contact_lens_original).tif");
	//sobelTest(imgOri);

	Mat imgGril = imread("Gril.jpg");
	scharrTest(imgGril);

	waitKey(0);
	return true;
}

static bool sobelTest(Mat imgOri)
{
	imshow("imgOri", imgOri);
	Mat imgSobel;
	Sobel(imgOri, imgSobel, imgOri.depth(), 1, 1, 3, 1, 0, BORDER_DEFAULT);
	imshow("imgSobel", imgSobel);

	return true;
}

static bool scharrTest(Mat imgOri)
{
	Mat imgScharr, imgScharrX, imgScharrY;
	Mat imgScharrAbsX, imgScharrAbsY;
	double fx = 0.0, fy = 0.0;
	//GaussianBlur(imgOri, imgOri, Size(11, 11), 0, 0);
	resize(imgOri, imgOri, Size(imgOri.cols * 0.5, imgOri.rows * 0.5), fx = 0, fy = 0, INTER_LINEAR);

	showImgPara(imgOri);
	imshow("scharrTest imgOri", imgOri);
	if (1 != imgOri.channels())
	{
		cvtColor(imgOri, imgOri, COLOR_BGR2GRAY);
	}

	showImgPara(imgOri);

	showImgPara(imgOri);

	Scharr(imgOri, imgScharrX, CV_32F, 1, 0);
	Scharr(imgOri, imgScharrY, CV_32F, 0, 1);

	convertScaleAbs(imgScharrX, imgScharrAbsX);
	convertScaleAbs(imgScharrY, imgScharrAbsY);

	showImgPara(imgScharrAbsX);
	showImgPara(imgScharrAbsY);

	imshow("imgScharrX", imgScharrAbsX);
	imshow("imgScharrY", imgScharrAbsY);

	addWeighted(imgScharrAbsX, 0.5, imgScharrAbsY, 0.5, 1, imgScharr);
	imshow("imgScharr", imgScharr);


	return true;
}

static void showImgPara(Mat &img)
{
	cout << "sizeof(img) is: " << sizeof(img) << ", img size is: " << img.size << endl;
	cout << "rows x cols: (" << img.rows << " x " << img.cols << ")" << endl;
	cout << "dims: " << img.dims << endl;
	cout << "channels: " << img.channels() << endl;
	cout << "type: " << img.type() << endl;
	cout << "depth:" << img.depth() << endl;
	cout << "elemSize:" << img.elemSize() << " (Bytes per element)" << endl;
	cout << "elemSize1:" << img.elemSize1() << "(Bytes per channel)" << endl;
	cout << "step[0]: " << img.step[0] << " (Bytes per cows only when 2 dims)" << endl;
	cout << "step[1]: " << img.step[1] << " (Bytes per element only when 2 dims)" << endl;
	cout << "step1(0): " << img.step1(0) << ", step1(1): " << img.step1(1) << " (step / elemSize1)" << endl;
	cout << "----- showImgPara End ----n" << endl;
}

运行结果:

注意事项:

1. np.shape还是经常忘记,分别代表rows, cols, depth。其中depth还是要认为是数组的维度,如果是1维默认不显。rows代表高度,cols代表宽度。
2. 还有一种思想,C++喜欢用引用,所以代入到参数里面即可,但是Python喜欢用返回值。

944abe8a19e80d46648af395fb4d556a.png
注意看处理后的圆形的右下角的瑕疵

Python:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# author:lowkeyway time:8/21/2019

import sys
import cv2 as cv
import numpy as np

def sobelTest(imgOri):
    cv.imshow("Sobel imgOri", imgOri)
    # imgOri = cv.cvtColor(imgOri, cv.COLOR_BGR2GRAY)
    imgSobel = cv.Sobel(imgOri, cv.CV_16S, 1, 1)
    imgSobel = cv.convertScaleAbs(imgSobel)

    cv.imshow("Sobel imgSobel", imgSobel)


def scharrTest(imgOri):
    imgOri = cv.resize(imgOri, (int(imgOri.shape[1]*0.5), int(imgOri.shape[0]*0.5)))
    cv.imshow("Scharr Imgori", imgOri)

    if 3 == len(imgOri.shape):
        imgOri = cv.cvtColor(imgOri, cv.COLOR_BGR2GRAY)

    imgScharrX = cv.Scharr(imgOri, cv.CV_32F, 1, 0)
    imgScharrY = cv.Scharr(imgOri, cv.CV_32F, 0, 1)

    imgScharrX = cv.convertScaleAbs(imgScharrX)
    imgScharrY = cv.convertScaleAbs(imgScharrY)

    cv.imshow("Scharr imgScharrX", imgScharrX)
    cv.imshow("Scharr imgScharrY", imgScharrY)

    imgScharr = cv.addWeighted(imgScharrX, 0.5, imgScharrY, 0.5, 1, dtype=cv.CV_32F)
    imgScharr = cv.convertScaleAbs(imgScharr)
    cv.imshow("Scharr imgScharr", imgScharr)


def main_func(argv):
    imgOri = cv.imread("Fig0342(a)(contact_lens_original).tif")
    # sobelTest(imgOri)
    
    imgGril = cv.imread("Gril.jpg")
    scharrTest(imgGril)

    cv.waitKey(0)

if __name__ == '__main__':
    main_func(sys.argv)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值