时间为友,记录点滴。
我在《数字图像处理》专栏的锐化空间滤波中聊过了什么是图像的锐化,以及为什么可以用微分可以实现图像的锐化。在这里,我们抛开细节,把思路理一下:
定性的思考方向是:
既然可以通过积分的思想来做图像的模糊,是不是可以通过积分的反操作微分来实现模糊的反操作-锐化?
定量的思考方向是:
我们把微分的过程当分别求x, y方向上的偏导,那么可以想象:
- 在恒定灰度区域内的微分值为0
- 在灰度台阶或斜坡处微分值非0
- 沿着斜坡的微分值为0
从上图可以从逻辑上讲得通了。
怎么实现呢?
我们先以一阶偏导为例,具体的推导过程可以参考《数字图像处理》专栏中的梯度介绍。
1. 首先要引入,什么是一阶偏导,它的形式是什么?
2. 然后再看数字图像中的一阶偏导的形式是什么?
我们可以用梯度代表,为什么是梯度?因为梯度是一张二维平面上最大一阶偏导的形式。
3. 两个公式怎么结合?
当然是求梯度的模。
4. 有了公式怎么引入算子
偷一下懒,抄过来把。
假设现在有一幅图像其中的一个3x3的区域:
其中
是其中心(目标)像素, 由前面的一阶微分知识(
)我们可以得到它对应的
和
分别为
和
。
- 罗伯特交叉算子
不过,这里我们介绍另外一种罗伯特交叉算子,他们被定义为:
由此,我们可以计算梯度图像
罗伯特交叉算子对应的模板是:
- Sobel算子
另外一种称为Sobel算子,中心权重使用2用来突出中心点的作用
对应的梯度图像:
完美。
更完美的是,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.
- 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方向不能一块微分。
2. convertScaleAbs,是做加权求绝对值并转转成8bit用的。参考如下公式
#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喜欢用返回值。
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)