[Intel AI SW 아카데미] 영상처리 - 영상 화질 개선과 필터

Jimeaning·2023년 11월 7일
0

Intel AIoT

목록 보기
14/38

23.11.7 (화) 34일차

영상 화질 개선과 복원

영상 복원이 어려울 때?

히스토그램이 한 군데에 포화되어 있을 때, 어떤 기준으로 밸런스를 잡아주거나 낮출 근거가 없어지기 때문에 복원이 어렵다.
추정 가능한 variation이 전혀 없어서 알 수 없음 ,,

히스토그램 평활화

diff 확인

// ISP_HistoEq.cpp

#pragma once

#include "ISP.h"

int main()
{
	std::string fileName = "../thirdparty/opencv_480/samples/data/lena.jpg";
	cv::Mat src = cv::imread(fileName, cv::ImreadModes::IMREAD_ANYCOLOR);

	uchar* pData = src.data; // data array
	int length = src.total(); // data length
	int channels = src.channels();

	Mat gray_cv = Mat(src.rows, src.cols, CV_8UC1);
	Mat gray = Mat(src.rows, src.cols, CV_8UC1);

	cvtColor(src, gray_cv, ColorConversionCodes::COLOR_BGR2GRAY);
	ISP _isp;
	_isp.Convert_BGR2GRAY(src.data, src.cols, src.rows, gray.data);

	Mat diff = gray_cv - gray;
	return 1;
}

히스토그램 평활화 결과

// IST_HistoEq.cpp

#pragma once

#include "ISP.h"

int main()
{
	std::string fileName = "../thirdparty/opencv_480/samples/data/lena.jpg";
	cv::Mat src = cv::imread(fileName, cv::ImreadModes::IMREAD_ANYCOLOR);

	uchar* pData = src.data; // data array
	int length = src.total(); // data length
	int channels = src.channels();

	Mat gray = Mat(src.rows, src.cols, CV_8UC1);

	ISP _isp;
	_isp.Convert_BGR2GRAY(src.data, src.cols, src.rows, gray.data);

	const int histoSz = 256;
	int histo[histoSz] = { 0, };
	_isp.Get_Histogram(gray.data, gray.cols, gray.rows, histo, histoSz);

	// 확률 밀도 함수
	int acc[histoSz] = { 0, };
	// acc[0] = histo[0] + histo[1]
	acc[0] = histo[0];
	for (size_t i = 0; i < histoSz - 1; i++)
	{
		acc[i] = acc[i - 1] + histo[i];
	}

	// new Look Up Table ... 영상의 각 화소값들을 새로운 대응값으로 맵핑
	int new_Gray[histoSz] = { 0, };
	for (size_t i = 0; i < histoSz; i++)
	{
		new_Gray[i] = static_cast<int>(1.0 * acc[i] * 255 / length);
	}
	
	// assign new Gray value using LUT
	Mat gray_Eq = Mat(src.rows, src.cols, CV_8UC1);
	for (size_t i = 0; i < length; i++)
	{
		gray_Eq.data[i] = new_Gray[gray.data[i]];
	}

	return 1;
}
// ISP.cpp

#include "ISP.h"

ISP::ISP()
{
	cout << "ISP::Ctor" << endl;
}

ISP::~ISP()
{
	cout << "ISP::Dtor" << endl;
}

bool ISP::Convert_BGR2GRAY(uchar* pBGR, int cols, int rows, uchar* pGray)
{
	if (pBGR == nullptr || pGray == nullptr)
	{
		return false;
	}

	for (size_t row = 0; row < rows; row++)
	{
		for (size_t col = 0; col < cols; col++)
		{
			int index = row * cols + col;
			float val_Y = 0.299 * pBGR[index * 3 + 2]	// R
				+ 0.587 * pBGR[index * 3 + 1]	// G
				+ 0.114 * pBGR[index * 3 + 0];	// B
			pGray[index] = (uchar)(val_Y + 0.5);
		}
	}
	return true;
}

bool ISP::Get_Histogram(uchar* pGray, int cols, int rows, int* pHisto, int histoSz)
{
	if (pGray == nullptr || pHisto == nullptr)
	{
		return false;
	}

	for (size_t i = 0; i < cols * rows; i++)
	{
		if (pGray[i] >= histoSz) continue;	// size 256
		pHisto[pGray[i]]++;
	}

	return true;
}
// ISP.h

#pragma once

#include "Common.h"

class ISP
{
public:
	ISP();
	~ISP();

	bool Convert_BGR2GRAY(
		uchar* pBGR, // color channel, input
		int cols,	// cols, input
		int rows, // rows, input
		uchar* pGray
	);
	bool Get_Histogram(
		uchar* pGray,
		int cols,
		int rows,
		int* pHisto,
		int histoSz
	);
private:

};

감마 보정

// GammaCorrection.cpp
#pragma once

#include "ISP.h"

int main()
{
	std::string fileName = "../thirdparty/opencv_480/samples/data/lena.jpg";
	cv::Mat src = cv::imread(fileName, cv::ImreadModes::IMREAD_ANYCOLOR);

	ISP _isp;

	// Gray Input
	Mat src_gray;
	cvtColor(src, src_gray, COLOR_BGR2GRAY);

	// Gamma Correction
	// double normalized_value = (double)pixels[i] / 255.0;
	// double corrected_value = pow(normalized_value, gamma) * 255.0;
	double gamma = 1.2;
	Mat src_gamma = Mat(src.size(), CV_8UC1);
	for (size_t i = 0; i < src.total(); i++)
	{
		src_gamma.data[i] = static_cast<uchar>
			(pow(src_gray.data[i] / 255.0, gamma) * 255 + 0.5);
	}

	// Gray Output

	return 1;
}

선형 필터와 비선형 필터

가우시안 필터

  • 이상적 저역 필터
    - 공간 영역에서 sinc 형태의 무한 길이의 필터
    - 요구사항
    	- 공간 영역 : 필터의 크기 최소화
    	- 주파수 영역 : 패스 밴드로 에너지 집중

소벨 연산

  • 1차 미분치 값 계산 후 이를 임계치와 비교
  • 수평과 수직 축으로 미분치 계산

라플라시안 필터

  • 2차 미분 값의 zero-crossing을 이용한 경계선 검출
    - 임계치 불필요
    - 최소 두께의 경계선 검출 가능
  • 문제점
    - 미세한 경계까지 검출하여 잘못 된 경계선 검출 가능
    - 라플라시안 연산자 적용 전, 가우시안 필터를 이용하여 잡음 제거

LoG(Laplacian of Gaussian) 필터

  • 가우시안 필터와 라플라시안 필터를 동시에 적용

  • 문제점
    - 잡음의 형태에 따른 분산값 선택의 어려움
    - 경계점 위치가 이동

캐니 에지

  • 가장 우수하고 가장 명세화한 경계선 검출 알고리즘.
  • 소벨은 경계선이 두툼하게 나오지만 캐니는 하나의 경계선만 검출
    - 경계선이 두툼하면 어디인지 명확하지 않을 때가 있음.

경계선 검출의 목적

  • 낮은 에러율
  • 경계선의 이동 최소화

=> 가우시안 필터와 함께 사용하면 안정적으로 검출할 수 있다

중간값 검출

노이즈가 튈 때 대표값을 어떤 기준으로 출력할 수 있을까?

보통 대표값을 출력하는 방법은 평균값, 중앙값, 최빈값이 있음.

  • 평균값: 값이 튈 때는 평균값을 대표로 쓰기에 무리가 있음
  • 최빈값: 노이즈가 최빈값이면 대표해선 안 됨

=> 이러한 이유로 중앙값을 선택하는 것이 가장 좋다

#pragma once

#include "ISP.h"

int main()
{
	int datas[] = { 6, 4, 8, 9, 4, 4, 8, 64, 4, 6, 4, 8, 6, 4, 11, 1, 3, 1134, 5, 64, 5, 64 };
	// 노이즈가 없는 대표 신호값을 정하시오.

	const int length = sizeof(datas) / sizeof(datas[0]);
	sort(datas, datas + length);

	vector<int> vDatas(datas, datas + length);
	sort(vDatas.begin(), vDatas.end());

	int major = datas[length / 2];
	int major_v = vDatas[length / 2];
    
    return 1;
}

Sort 함수

QuickSort

반 잘라서 판단, 왼쪽 반 잘라서 판단, 오른쪽 반 잘라서 판단,,, 계속 반 잘라가면서 판단


부스러기

파이썬은 슬라이스 기능이 잘 되어 있음,,오호라~
추상화도 잘 되어 있음

arr = [0, 1, 2, 3, 4]
for x in arr:
	print(x ** 2)

** 이런 게 추상화의 꽃,, 정수/실수/문자 모두 가능

class는 무조건 self를 넣어줘야 함 (C++의 this 기능과 흡사)


스탠포드 딥러닝 코스: http://cs231n.stanford.edu/

profile
I mean

0개의 댓글