Search…

Lọc Gaussian - Giao Diện Đơn Giản: Qt + OpenCV

17/09/20205 min read
Hiện thuật giải thuật làm mờ ảnh với hàm gaussian bằng ngôn ngữ C++ cùng thư viện OpenCV và tạo giao diện đơn giản với Qt.

Thuật giải lọc Gaussian

Ký hiệu

  • _srcImg: Ảnh gốc kích thước: RI x CI.
  • _kernel: Kernel, mảng một chiều có số phần tử RK x CK.
  • sigma: Độ lệch chuẩn của hàm gauss.

Giải thuật

Bước 1

  • Tính toán ma trận kernel của toán tử gaussian theo công thức.
ss_1
  • Tính chỉ số truy cập nhanh _kernelIndex, sử dụng trong Tích Chập.
  • Mỗi phần tử là một Point với x là chỉ số dòng và y là chỉ số cột.
  • Anchor point có toạ độ (0, 0).

Bước 2

  • Tính tổng giá trị của tất cả các phần tử trong ma trận kernel lưu vào sumKer. Dành cho việc chuẩn hoá giá trị của mỗi phần tử trong kernel.
sumKer := sum(_kernel);
IF  sumKer == 0:
    sumKer := 1;
  • Chuẩn hoá: chia mỗi phần tử trong _kernel cho sumKer:
_kernel.element := _kernel.element / sumKer;

Bước 3

Thực hiện convolution _kernel với _srcImg cho ảnh kết quả _destImg:

For each pixel pS in _srcImg:
    For each element eK in _kernel:
        _destImg[pS] += _kernel[eK] * _srcImg[pS - _kernelIndex[eK]];
    End For
End For

Hiện thực giải thuật

Trong quá trình hiện thực giải thuật, một số bước không tuân theo do quan tâm đến thời gian thực thi của chương trình. Như trong bước 2, không thực hiện chuẩn hoá kernel ngay. Mà thực hiện tích chập trước, kết quả tích chập sẽ đem chia cho sumKer.

Tính toán ma trận kernel

/*
* @Brief:	function to create kernel for gaussian filter. With height, width, sigma input by users.
* @Param[out]:	kernel: vector with size = 0.
* @Param[in]:	height: Numbers of row of matrix kernel will be created. Should: Odd value.
* @Param[in]:	width: Numbers of column of matrix kernel will be created. Should: Odd value.
* @Param[in]:	sigma: Standard deviation of gaussian funct.
* @Retval:		None.
*/
void
TEST::createGaussianKernel(vector<float> & kernel, int height, int width, float sigma)
{
	float		inverse_Sqrt2Pi_Sigma;
	float		inverse_2_SigmaSquare;
	int			indexRow, indexCol;

	inverse_Sqrt2Pi_Sigma = 1 / (sqrt(2 * PI) * sigma);
	inverse_2_SigmaSquare = 1 / (2 * sigma * sigma);

	for (indexRow = -height / 2; indexRow < ((height - 1) / 2) + 1; indexRow++)
	{
		for (indexCol = -width / 2; indexCol < ((width - 1) / 2) + 1; indexCol++)
		{
			kernel.push_back(inverse_Sqrt2Pi_Sigma
				* exp(-(indexRow * indexRow + indexCol * indexCol)
				* inverse_2_SigmaSquare
				));
		}
	}
}

Tính toán _kernelIndexsumKer

for (int i = -(_kernelHeight / 2); i < ((_kernelHeight - 1) / 2) + 1; i++)
		for (int j = -(_kernelWidth / 2); j < ((_kernelWidth - 1) / 2) + 1; j++)
		{
			_kernelIndex.push_back(Point(i, j));
			_sumKernel += _kernel[ii++];
		}
	
	if (_sumKernel == 0)
		_sumKernel = 1;

Thực hiện convolution _kernel

Thực hiện convolution _kernel với _srcImg cho ảnh kết quả _destImg

void Convolution::doConvolution(Mat& sourceImage, Mat& destinationImage)
{
	int numRowImg = sourceImage.rows;
	int numColImg = sourceImage.cols;
	int indexRow, indexCol, indexKernel, indexRImg, indexCImg;
	float g_val;
	destinationImage.create(Size(numColImg, numRowImg), CV_8UC1);

	for (indexRow = 0; indexRow < numRowImg; indexRow++)
	{
		uchar* data = destinationImage.ptr<uchar>(indexRow);

		for (indexCol = 0; indexCol < numColImg; indexCol++)
		{
			g_val = 0;

			for (indexKernel = 0; indexKernel < _kernel.size(); indexKernel++)
			{
				indexRImg = indexRow - _kernelIndex[indexKernel].x;
				if (indexRImg < 0 || indexRImg > numRowImg - 1)
					continue;

				indexCImg = indexCol - _kernelIndex[indexKernel].y;
				if (indexCImg < 0 || indexCImg > numColImg - 1)
					continue;

				g_val += _kernel[indexKernel] * sourceImage.at<uchar>(indexRImg, indexCImg);
			}

			data[indexCol] = saturate_cast<uchar> (g_val / _sumKernel);
		}
	}
}

Bước tiếp theo, tạo giao diện đơn giản để chạy thuật toán này. Thực ra, nếu chỉ chạy thuật toán này thôi thì không cần tạo giao diện, chỉ cần OpenCV là đủ. Tuy nhiên, bài viết hướng dẫn tạo giao diện cho một project với nhiều tác vụ. 

Tạo Project với Qt + OpenCV

Bài viết này sử dụng các phiên bản phần mềm như sau:

  • Windows 10 - 64bit.
  • Visual Studio 2013 community - 32bit.
  • Qt 5.5.0 for Windows 32 bit.
  • OpenCV 3.0.

Cài đặt và tạo project Qt

Tạo GUI Trong C++ Sử Dụng Qt.

Cài đặt và cấu hình thư viện OpenCV cho project

OpenCV – Cài Đặt Và Ví Dụ Minh Họa Sử Dụng

Thiết kế giao diện

ss_2

Ở cây thư mục project TEST click chọn test.ui và kéo thả như hình dưới:

ss_3

Build chương trình sẽ tự động write lại các file trong Filter folder Generated Files

Thiết kế Class

ss_4

Class TEST (test.h, test.cpp):

  • ui: quản lý giao diện. Hàm ui.setUI được gọi trong hàm khởi tạo của class TEST.
  • _sourceImg: lưu ảnh gốc.
  • _destImg: lưu ảnh sau khi làm mờ.
  • createGaussianKernel: hàm tạo ma trận kernel với kích thước và độ lệch chuẩn do người dùng định.
  • connectObject: hàm thực hiện kết nối tất cả các thông qua signalsslots button QObject (ChooseImageGaussianBlur). Hàm được gọi trong hàm khởi tạo của lớp.
  • slots openImage: được gọi khi người dùng click chọn button Choose Image. Hàm hiển thị dialog để chọn ảnh thực hiện làm mờ.
  • slots doConvo: được gọi khi người dùng click chọn button Gaussian Blur. Hàm thực hiện: createGaussianKernel Convo.setKernel (đối tượng Convo thuộc lớp Convolution) → Convo.doConvolution → Hiển thị kết quả.

Class Convolution (convolution.h, convolution.cpp): được trình bày như trên giải thuật.

Kết quả

Kết quả thực hiện với kernel(5x5), sigma = 2.

Theo thứ tự từ trái qua phải:

  1. Ảnh gốc.
  2. Ảnh thực hiện bởi thuật giải.
  3. Ảnh thực hiện bởi hàm GaussianBlur của thư viện OpenCV.
ss_5

Tải file thực thi

Release_GaussianBlur_STDIO.zip

IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024