使用3\times3的Sobel滤波器来进行滤波吧。
Sobel滤波器可以提取特定方向(纵向或横向)的边缘,滤波器按下式定义:
纵向:
K=\left[
\begin{matrix}
1&2&1\\
0&0&0\\
-1&-2&-1
\end{matrix}
\right]
横向:
K=\left[
\begin{matrix}
1&0&-1\\
2&0&-2\\
1&0&-1
\end{matrix}
\right]
python实现:
import cv2
import numpy as np
# Gray scale
def BGR2GRAY(img):
b = img[:, :, 0].copy()
g = img[:, :, 1].copy()
r = img[:, :, 2].copy()
# Gray scale
out = 0.2126 * r + 0.7152 * g + 0.0722 * b
out = out.astype(np.uint8)
return out
# sobel filter
def sobel_filter(img, K_size=3):
if len(img.shape) == 3:
H, W, C = img.shape
else:
img = np.expand_dims(img, axis=-1)
H, W, C = img.shape
# Zero padding
pad = K_size // 2
out = np.zeros((H + pad * 2, W + pad * 2), dtype=np.float)
out[pad: pad + H, pad: pad + W] = gray.copy().astype(np.float)
tmp = out.copy()
out_v = out.copy()
out_h = out.copy()
## Sobel vertical
Kv = [[1., 2., 1.],[0., 0., 0.], [-1., -2., -1.]]
## Sobel horizontal
Kh = [[1., 0., -1.],[2., 0., -2.],[1., 0., -1.]]
# filtering
for y in range(H):
for x in range(W):
out_v[pad + y, pad + x] = np.sum(Kv * (tmp[y: y + K_size, x: x + K_size]))
out_h[pad + y, pad + x] = np.sum(Kh * (tmp[y: y + K_size, x: x + K_size]))
out_v = np.clip(out_v, 0, 255)
out_h = np.clip(out_h, 0, 255)
out_v = out_v[pad: pad + H, pad: pad + W].astype(np.uint8)
out_h = out_h[pad: pad + H, pad: pad + W].astype(np.uint8)
return out_v, out_h
# Read image
img = cv2.imread("imori.jpg").astype(np.float)
# grayscale
gray = BGR2GRAY(img)
# sobel filtering
out_v, out_h = sobel_filter(gray, K_size=3)
# Save result
cv2.imwrite("out_v.jpg", out_v)
cv2.imshow("result_v", out_v)
while cv2.waitKey(100) != 27:# loop if not get ESC
if cv2.getWindowProperty('result_v',cv2.WND_PROP_VISIBLE) <= 0:
break
cv2.destroyWindow('result_v')
cv2.imwrite("out_h.jpg", out_h)
cv2.imshow("result_h", out_h)
# loop if not get ESC or click x
while cv2.waitKey(100) != 27:
if cv2.getWindowProperty('result_h',cv2.WND_PROP_VISIBLE) <= 0:
break
cv2.destroyWindow('result_h')
cv2.destroyAllWindows()
C++:
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <math.h>
// BGR -> Gray
cv::Mat BGR2GRAY(cv::Mat img){
// get height and width
int width = img.cols;
int height = img.rows;
// prepare output
cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1);
// each y, x
for (int y = 0; y < height; y++){
for (int x = 0; x < width; x++){
// BGR -> Gray
out.at<uchar>(y, x) = 0.2126 * (float)img.at<cv::Vec3b>(y, x)[2] \
+ 0.7152 * (float)img.at<cv::Vec3b>(y, x)[1] \
+ 0.0722 * (float)img.at<cv::Vec3b>(y, x)[0];
}
}
return out;
}
// Sobel filter
cv::Mat sobel_filter(cv::Mat img, int kernel_size, bool horizontal){
int height = img.rows;
int width = img.cols;
int channel = img.channels();
// prepare output
cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1);
// prepare kernel
double kernel[kernel_size][kernel_size] = {{1, 2, 1}, {0, 0, 0}, {-1, -2, -1}};
if (horizontal){
kernel[0][1] = 0;
kernel[2][1] = 0;
kernel[1][0] = 2;
kernel[1][2] = -2;
}
int pad = floor(kernel_size / 2);
double v = 0;
// filtering
for (int y = 0; y < height; y++){
for (int x = 0; x < width; x++){
v = 0;
for (int dy = -pad; dy < pad + 1; dy++){
for (int dx = -pad; dx < pad + 1; dx++){
if (((y + dy) >= 0) && (( x + dx) >= 0) && ((y + dy) < height) && ((x + dx) < width)){
v += img.at<uchar>(y + dy, x + dx) * kernel[dy + pad][dx + pad];
}
}
}
v = fmax(v, 0);
v = fmin(v, 255);
out.at<uchar>(y, x) = (uchar)v;
}
}
return out;
}
int main(int argc, const char* argv[]){
// read image
cv::Mat img = cv::imread("imori.jpg", cv::IMREAD_COLOR);
// BGR -> Gray
cv::Mat gray = BGR2GRAY(img);
// sobel filter (vertical)
cv::Mat out_v = sobel_filter(gray, 3, false);
// sobel filter (horizontal)
cv::Mat out_h = sobel_filter(gray, 3, true);
//cv::imwrite("out.jpg", out);
cv::imshow("answer (vertical)", out_v);
cv::imshow("answer (horizontal)", out_h);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
输入:
纵向输出:
横向输出: