by Robert Laganiere
이번 장에서는 화소에 접근하는 방법(소금 뿌리기, 색상 감축)과
gray level 이미지를 보다 선명하게 표현하는 것을 한다.
추가로 2개의 이미지를 합치는 것을 한다.
영상처리 작업에서 많은 경우 영상의 모든 화소를 조회할 때가 많다.
이 때문에 많은 화소를 찾아 다녀야 한다.
컬러영상은 3채널 3개의 화소를 조합
한 영역은 3바이트 ->> 256 X 256 X 256 = 약 1600만개
여기서 소개하는 방법 중 하나는 화소의 표현 범위를 줄여서
탐색을 하는데 복잡도를 낮추는 방법이다.
이 알고리즘을 감축 알고리즘이라 한다.
ex) 감축의 값이 8 인경우
256/8 X 256/8 X 256/8 -> 32 X 32 X 32
*들어가기 전에*
컬러영상에서 한 화소는 RGB(빨강, 초로, 파랑)의 값을 갖고
영상 데이터 버퍼의 첫 3바이트는 상위 왼쪽(0행 0열)에 있는
화소의 3채널(BGR) 값이다. 다음 3바이트는 (0행 1열)의 화소 값이다.
(OpenCV는 BGR 순이이며 RGB는 각각 1바이트 씩 갖는다.)
컬러영상에서 height X width X 3 만큼의 화소 수를 갖는다.
(그런데 컴퓨터는 4,8 배수를 좋아해서 여기에 맞춘다. )
감축 알고리즘
11111111 11111111 11111111
예를 들아 아래 소스의 div값을 128이라 하면
>>으로 7번 쉬프트 후 << 7번 쉬프트를 하고 64를 더해준다.
이 말은 R,G,B들은 01000000, 11000000값 두 개 밖에 갖지를 못한다는 말이다.
즉, 256/128 X 256/128 X 256/128 = 2 X 2 X 2 = 8가지 색상만을 표현.
--------------------------------------------------------------------------------------
아래 예제 소스는 순서대로
소금 뿌리기 -> 색상 감축 -> 선명하게(라플라시안?) -> 결합
주석에 설명을 달아 놓았다.
![]() |
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <opencv2\opencv.hpp>
#include <opencv\highgui.h>
using namespace std;
void salt(cv::Mat &image, int n){
for (int k = 0; k < n; k++){
int i = rand()%image.cols;
int j = rand()%image.rows;
if (image.channels() == 1){ // 그레이레벨 영상
image.at<uchar>(j, i) = 255;
}
else if (image.channels() == 3){ //컬러 영상
image.at<cv::Vec3b>(j, i)[0] = 255; //3b = 3원소 byte단위
image.at<cv::Vec3b>(j, i)[1] = 255;
image.at<cv::Vec3b>(j, i)[2] = 255;
}
}
}
void colorReduce(cv::Mat &image, int div){
int nl = image.rows;
int nc = image.cols * image.channels(); //컬러인 경우 *3
for (int j = 0; j < nl; j++){
uchar* data = image.ptr<uchar>(j);
for (int i = 0; i < nc; i++){
//따라서 data[i] = data/div*div + div/2;
data[i] = data[i] / div*div + div / 2;
//생각해봐라 만약 div가 128이면 맨 처음 Blue 영역에
//하위 7비트를 없애 버리고 64를더한다.
//즉 Blue가 값을 가질 수 있는 것은 11000000, 01000000 2가지
//2*2*2 = 8가지의 색상을 표현가능
}
}
/*iterator를 이용
cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();
while (it != itend){
(*it)[0] = (*it)[0] / div*div + div / 2;
(*it)[1] = (*it)[1] / div*div + div / 2;
(*it)[2] = (*it)[2] / div*div + div / 2;
it++;
}
*/
}
int main(void)
{
//icon이미지 읽어온다.
cv::Mat image = cv::imread("white.png");
//이미지 파일이 잘 열리지 않았다면 image.data는 0을 반환
if (!image.data)
cout << "fail" << endl;
else
cout << "sucess" << endl;
//소금 뿌리기
//salt(image, 1000);
//cv::Mat_ <uchar> im2 = image;
//im2(50, 100) = 0;//uchar일때 화소에 직접접근.
colorReduce(image, 128);
//이미지 show
cv::imshow("salt",image);
//0이면 입력이 들어올 때까지 기다린다. 다른 예) 5000이면 5초를 기다린다.
cv::waitKey(0);
return 0;
}
#include <iostream>
#include <stdlib.h>
#include <opencv2\opencv.hpp>
#include <opencv\highgui.h>
using namespace std;
void salt(cv::Mat &image, int n){
for (int k = 0; k < n; k++){
int i = rand()%image.cols;
int j = rand()%image.rows;
if (image.channels() == 1){ // 그레이레벨 영상
image.at<uchar>(j, i) = 255;
}
else if (image.channels() == 3){ //컬러 영상
image.at<cv::Vec3b>(j, i)[0] = 255; //3b = 3원소 byte단위
image.at<cv::Vec3b>(j, i)[1] = 255;
image.at<cv::Vec3b>(j, i)[2] = 255;
}
}
}
void colorReduce(cv::Mat &image, int div){
int nl = image.rows;
int nc = image.cols * image.channels(); //컬러인 경우 *3
for (int j = 0; j < nl; j++){
uchar* data = image.ptr<uchar>(j);
for (int i = 0; i < nc; i++){
//따라서 data[i] = data/div*div + div/2;
data[i] = data[i] / div*div + div / 2;
//생각해봐라 만약 div가 128이면 맨 처음 Blue 영역에
//하위 7비트를 없애 버리고 64를더한다.
//즉 Blue가 값을 가질 수 있는 것은 11000000, 01000000 2가지
//2*2*2 = 8가지의 색상을 표현가능
}
}
/*iterator를 이용
cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();
while (it != itend){
(*it)[0] = (*it)[0] / div*div + div / 2;
(*it)[1] = (*it)[1] / div*div + div / 2;
(*it)[2] = (*it)[2] / div*div + div / 2;
it++;
}
*/
}
int main(void)
{
//icon이미지 읽어온다.
cv::Mat image = cv::imread("white.png");
//이미지 파일이 잘 열리지 않았다면 image.data는 0을 반환
if (!image.data)
cout << "fail" << endl;
else
cout << "sucess" << endl;
//소금 뿌리기
//salt(image, 1000);
//cv::Mat_ <uchar> im2 = image;
//im2(50, 100) = 0;//uchar일때 화소에 직접접근.
colorReduce(image, 128);
//이미지 show
cv::imshow("salt",image);
//0이면 입력이 들어올 때까지 기다린다. 다른 예) 5000이면 5초를 기다린다.
cv::waitKey(0);
return 0;
}
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <opencv2\opencv.hpp>
#include <opencv\highgui.h>
using namespace std;
//선명하게!
void sharpen(const cv::Mat &image, cv::Mat &result){
result.create(image.size(), image.type());
//위, 아래, 양 옆 접근 범위를 벗어나지 않기위해 1부터 시작, 마지막-1까지
for (int j = 1; j < image.rows - 1; j++){
const uchar* previous = image.ptr<const uchar>(j - 1);//이전 행
const uchar* current = image.ptr<const uchar>(j);//현재 행
const uchar* next = image.ptr<const uchar>(j + 1);//다음 행
uchar* ouput = result.ptr<uchar>(j);//결과 행
for (int i = 1; i < image.cols*image.channels() - image.channels(); i++){
*ouput++ = cv::saturate_cast<uchar>( //saturate_cast<uchar> 이녀석이 오버플로에 대한걸 다뤄 줌
5*current[i] - current[i - 1] - current[i + 1]
- previous[i] - next[i]);
}
}
//처리하지 않은 화소를 0으로 설정
result.row(0).setTo(cv::Scalar(0));
result.row(result.rows - 1).setTo(cv::Scalar(0));
result.col(0).setTo(cv::Scalar(0));
result.col(result.cols - 1).setTo(cv::Scalar(0));
}
int main(void)
{
//icon이미지 읽어온다.
cv::Mat image = cv::imread("gray.jpg");
//이미지 파일이 잘 열리지 않았다면 image.data는 0을 반환
if (!image.data)
cout << "fail" << endl;
else
cout << "sucess" << endl;
cv::Mat result;
sharpen(image, result);
//이미지 show
cv::imshow("clearly", result);
cv::imshow("origin", image);
//0이면 입력이 들어올 때까지 기다린다. 다른 예) 5000이면 5초를 기다린다.
cv::waitKey(0);
return 0;
}
#include <iostream>
#include <stdlib.h>
#include <opencv2\opencv.hpp>
#include <opencv\highgui.h>
using namespace std;
//선명하게!
void sharpen(const cv::Mat &image, cv::Mat &result){
result.create(image.size(), image.type());
//위, 아래, 양 옆 접근 범위를 벗어나지 않기위해 1부터 시작, 마지막-1까지
for (int j = 1; j < image.rows - 1; j++){
const uchar* previous = image.ptr<const uchar>(j - 1);//이전 행
const uchar* current = image.ptr<const uchar>(j);//현재 행
const uchar* next = image.ptr<const uchar>(j + 1);//다음 행
uchar* ouput = result.ptr<uchar>(j);//결과 행
for (int i = 1; i < image.cols*image.channels() - image.channels(); i++){
*ouput++ = cv::saturate_cast<uchar>( //saturate_cast<uchar> 이녀석이 오버플로에 대한걸 다뤄 줌
5*current[i] - current[i - 1] - current[i + 1]
- previous[i] - next[i]);
}
}
//처리하지 않은 화소를 0으로 설정
result.row(0).setTo(cv::Scalar(0));
result.row(result.rows - 1).setTo(cv::Scalar(0));
result.col(0).setTo(cv::Scalar(0));
result.col(result.cols - 1).setTo(cv::Scalar(0));
}
int main(void)
{
//icon이미지 읽어온다.
cv::Mat image = cv::imread("gray.jpg");
//이미지 파일이 잘 열리지 않았다면 image.data는 0을 반환
if (!image.data)
cout << "fail" << endl;
else
cout << "sucess" << endl;
cv::Mat result;
sharpen(image, result);
//이미지 show
cv::imshow("clearly", result);
cv::imshow("origin", image);
//0이면 입력이 들어올 때까지 기다린다. 다른 예) 5000이면 5초를 기다린다.
cv::waitKey(0);
return 0;
}
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <opencv2\opencv.hpp>
#include <opencv\highgui.h>
using namespace std;
int main(void)
{
cv::Mat castle = cv::imread("castle.jpg");
cv::Mat rain = cv::imread("rain.jpg");
cv::Mat imageROI;
cout << castle.size() << endl;
//사이즈가 다를때 사용한다. //rain.jpg의 영역을 설정
castle.copyTo(imageROI);
imageROI = imageROI(cv::Rect(0, 0, rain.cols, rain.rows));
cv::addWeighted(imageROI, 0.7, rain, 0.9, 0., imageROI);
//이미지 show
cv::imshow("added", imageROI);
cv::imshow("castle", castle);
cv::imshow("rain", rain);
//0이면 입력이 들어올 때까지 기다린다. 다른 예) 5000이면 5초를 기다린다.
cv::waitKey(0);
return 0;
}
#include <iostream>
#include <stdlib.h>
#include <opencv2\opencv.hpp>
#include <opencv\highgui.h>
using namespace std;
int main(void)
{
cv::Mat castle = cv::imread("castle.jpg");
cv::Mat rain = cv::imread("rain.jpg");
cv::Mat imageROI;
cout << castle.size() << endl;
//사이즈가 다를때 사용한다. //rain.jpg의 영역을 설정
castle.copyTo(imageROI);
imageROI = imageROI(cv::Rect(0, 0, rain.cols, rain.rows));
cv::addWeighted(imageROI, 0.7, rain, 0.9, 0., imageROI);
//이미지 show
cv::imshow("added", imageROI);
cv::imshow("castle", castle);
cv::imshow("rain", rain);
//0이면 입력이 들어올 때까지 기다린다. 다른 예) 5000이면 5초를 기다린다.
cv::waitKey(0);
return 0;
}


댓글 없음:
댓글 쓰기