1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <string.h>

int main(void)
{
	char c[] = "hello";
	char *d = "bye";
	char e[100];

	printf("%s %s\n", c, d);

	// strlen: string length
	printf("%d\n", strlen(c));
	printf("%d\n", strlen(d));

	// strcmp: string compare
	// strcmp(c, d) : 0 ==> equal
	// strcmp(c, d) : -1(negative) ===> c first
	// strcmp(c, d) : +1(positvie) ==> d first
	printf("comparison result %d\n", strcmp(c, d));

	// strcpy: string copy
	strcpy(e, c); // e = c;
	printf("[%s]-->%d\n", e, strlen(e));

	// strcat: string concatenation
	strcat(e, d);  // e = e + d;
	printf("[%s]-->%d\n", e, strlen(e));
	return 0;
}
반응형
LIST

'데이터구조 > 소스코드' 카테고리의 다른 글

Stack handling string  (0) 2016.04.14
SLL with character string member  (0) 2016.04.14
OJ 1131번 해답  (0) 2016.04.11
OJ 1132번 해답  (0) 2016.04.11
Online Judge 1117 문제의 해답  (1) 2016.04.04

Histogram을 이용한 이미지 유사도 측정: 

Evaluating image similarity using histogram


히스토그램 (histogram)을 이용하면 사진들이 서로 얼마나 비슷한지 측정할 수 있다. 히스토그램간의 유사도를 측정하면 된다. OpenCV는 이러한 측정을 위해 함수 compareHist( )를 제공한다. 비교대상인 두 개의 히스토그램을 인자로 전달하면 유사도를 수치로 반환한다. 비교방식은 7가지가 있는데, 주로 사용되는 것은 correlation, chi-square, intersection과 Bhattachayya가 있다.


 

예를 들어 아래와 같이 다섯 장의 사진 (위에 있는 것이 이름)이 주어졌을 때, 각 사진간의 유사도를 측정해 보자.


img_dotonbori.jpg

img_eiffel.jpg

img2_beach.jpg

img2_garden.jpg

img2_grapefarm.jpg


우선 결과부터 제시하면 아래와 같이 img2_garden(위에서부터 4번째)과 img2_grapefarm(마지막 사진) 이 사용한 4가지 방법 모두에서 가장 유사한 사진으로 나온다.



이러한 비교에 사용된 프로그램은 아래와 같다.

순서도

  1. 비교대상 사진 5장을 읽어들이면서 HLS format으로 전환한다. (24~32)

  2. 각 HLS사진에 대해서 3차원 histogram을 구한다. (40 ~ 52)

  3. 각 histogram끼리 유사도를 계산한다. (54 ~ 62)


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

#define SCALE 0.2
#define NUM 5
#define BINS 8

int main(int argc, char** argv)
{
	// the names of images to be compared each other
	string imgNames[NUM] = { "img_dotonbori.jpg", "img_eiffel.jpg", "img2_beach.jpg", "img2_garden.jpg", "img2_grapefarm.jpg" };

	//for (int i = 0; i < NUM; i++)
	//{
	//	cout << imgNames[i] << endl;
	//}

	// read all images and convert to HLS format
	Mat imgs[NUM];
	Mat imgsHLS[NUM];
	for (int i = 0; i < NUM; i++)
	{
		imgs[i] = imread(imgNames[i], IMREAD_COLOR);
		if (imgs[i].data == 0)
		{
			cout << "Unable to read " << imgNames[i] << endl;
			return 0;
		}
		cvtColor(imgs[i], imgsHLS[i], COLOR_BGR2HLS);
	}

	//cout << "Succeeded to read all images" << endl;

	// compute 3D histogram
	Mat histogram[NUM];

	int channel_numbers[] = { 0, 1, 2 };
	for (int i = 0; i < NUM; i++)
	{
		int* number_bins = new int[imgsHLS[i].channels()];
		for (int ch = 0; ch < imgsHLS[i].channels(); ch++)
		{
			number_bins[ch] = BINS;
		}
		float ch_range[] = { 0.0, 255.0 };
		const float *channel_ranges[] = { ch_range, ch_range, ch_range };
		calcHist(&imgsHLS[i], 1, channel_numbers, Mat(), histogram[i], imgsHLS[i].channels(), number_bins, channel_ranges);
		normalize(histogram[i], histogram[i], 1.0);
	}

	cout << "Image Comparison by HISTCMP_CORREL   " << endl;
	for (int i = 0; i < NUM; i++)
	{
		for (int j = i + 1; j < NUM; j++)
		{
			double matching_score = compareHist(histogram[i], histogram[j], HISTCMP_CORREL);
			cout << imgNames[i] << "-" << imgNames[j] << ", " << 1matching_score << endl;
		}
	}
	return 0;
}


반응형
LIST

3차원 히스토그램 (3 dimensional histogram)


지금까지는 1차원 히스토그램을 이용해왔다. 즉 픽셀이 가질 수 있는 값 별로 몇 개씩의 픽셀들이 실제로 그 값을 가졌는지를 계산했다.


그러나, 컬러이미지의 경우, 하나의 픽셀은 R, G, B 혹은 H, L, S의 3가지 값을 가지기 때문에 1차원 히스토그램이 적절하지 않다. 그래서 3차원 히스토그램을 이용해야 한다. 3차원은 아래 그림과 같이 생각하면 된다. 아래는 HLS 포맷의 이미지의 픽셀들을 H, L, S를 축으로 하는 3차원 히스토그램으로 나타낸 것이다. 좌표 (H, L, S)의 값은 그 조합을 가지는 픽셀들의 갯수이다. 




그런데, H, L, S가 가질 수 있는 값들의 조합이 너무나 많다. 예를 들어, 각각이 20개씩의 값만 가진다 하더라도 20*20*20 = 8000개의 조합이 나온다. 그래서 각 축의 histogram bin의 개수를 4개, 8개, 혹은 적절한 값으로 하면 적당한 복잡도의 히스토그램을 얻을 수 있다.


3D 히스토그램을 그리는 순서도

  1. RGB이미지를 읽어들인다.
  2. HLS포맷으로 전환한다.
  3. H, L, S를 각 축으로 하고, 각 축마다 8개의 bin이 있는 3차원 histogram을 계산한다.
  4. 계산된 histogram의 값을 출력한다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

#define SCALE 0.2
#define NUM 5
#define BINS 8

int main(int argc, char** argv)
{
	// the names of images to be compared each other
	string imgNames[NUM] = { "img_dotonbori.jpg", "img_eiffel.jpg", "img2_beach.jpg", "img2_garden.jpg", "img2_grapefarm.jpg" };

	//for (int i = 0; i < NUM; i++)
	//{
	//	cout << imgNames[i] << endl;
	//}

	// read all images and convert to HLS format
	Mat imgs[NUM];
	Mat imgsHLS[NUM];
	for (int i = 0; i < NUM; i++)
	{
		imgs[i] = imread(imgNames[i], IMREAD_COLOR);
		if (imgs[i].data == 0)
		{
			cout << "Unable to read " << imgNames[i] << endl;
			return 0;
		}
		cvtColor(imgs[i], imgsHLS[i], COLOR_BGR2HLS);
	}

	//cout << "Succeeded to read all images" << endl;

	// compute 3D histogram
	Mat histogram[NUM];

	int channel_numbers[] = { 0, 1, 2 };
	for (int i = 0; i < NUM; i++)
	{
		int* number_bins = new int[imgsHLS[i].channels()];
		for (int ch = 0; ch < imgsHLS[i].channels(); ch++)
		{
			number_bins[ch] = BINS;
		}
		float ch_range[] = { 0.0, 255.0 };
		const float *channel_ranges[] = { ch_range, ch_range, ch_range };
		calcHist(&imgsHLS[i], 1, channel_numbers, Mat(), histogram[i], imgsHLS[i].channels(), number_bins, channel_ranges);
		cout << imgNames[i] << "-----------------------" << endl;
		for (int x = 0; x < BINS; x++)
		{
			for (int y = 0; y < BINS; y++)
			{
				for (int z = 0; z < BINS; z++)
				{
					cout << histogram[i].at<float>(x, y, z) << " ";
				}
			}
		}
		cout << endl;
	}
	return 0;
}


반응형
LIST



Histogram equalization


영상처리기법 중에 histogram equalization이라는 것이 있다. Histogram은 <<여기>>에서 자세히 설명하고 있다. Equalization이라는 것은 특정값을 가진 픽셀들이 너무 많지 않도록 골고루 퍼트리는 기술이다.  비유를 들자면, 서울에 거주하는 사람이 너무 많으면, 서울과 수도권으로 사람들을 이주시키는 식이다. 자세한 원리는 이 <<블로그를>> 참조하면 된다.




Histogram equalization을 하게 되면, 단일 색으로 보이던 부분들이 구분되어 보여지는 효과가 있다. 예를 들어, 아래 사진을 보자. 어느 도시의 야경을 촬영한 것이다. 왼쪽은 원래 영상이고, 오른쪽이 equalization이 후의 영상이다. 차이점은 밤하늘이 전체적인 검은색 일변도에서 여러 가지 단계를 가진 검정색으로 보이게 된 것이다.




Luminance Histogram Equalization


특히, 영상의 밝기 (Luminance)를 equalization하면 안 보이던 부분들이 아주 잘 보이게 된다. 예를 들어 아래 사진을 보자. 왼쪽 사진에서 집 입구가 전체적으로 어두운 색이서 물체들이 제대로 구분되지 않는다. 이 때  luminance를 기준으로 equalization을 하게 되면 오른쪽 결과와 같이 집 입구가 환해지는 효과를 얻을 수 있다.


이러한 환해지는 효과는 아래 사진들에서도 확인할 수 있다. 다양한 상황을 촬영한 사진들에 대해 Luminance histogram equalization을 수행한 결과들이다.




반대로 너무 환해서 잘 보이지 않았던 부분들은 Luminance histogram equalization을 하게 되면, 어두워지면서 세부특징들을 잘 관찰할 수 있다. 예를 들어 아래 사진에서 바닥의 디테일들이 아주 잘 보이는 것을 알 수 있다.



Implementation


OpenCV가 제공하는 기능들을 이용해서 luminance histogram equalization을 구현해 보자. 우선 영상을 읽어들인다. imread( )는 컬러이미지인 경우 BGR 포맷으로 읽기 때문에, 이것을 HLS 포맷으로 변환할 필요가 있다. 이를 위해 cvtColor( )함수를 사용한다. 라인 36 ~ 39은 HLS 포맷 영상에 대해서 Luminance채널에 대해서만 equalization을 수행하고, 이를 다시 HLS 영상에 넣는 과정이다. 마지막으로 HLS 포맷 영상을 BGR 포맷으로 재변환해야 한다.(라인 40) 왜냐하면 OpenCV는 BGR 영상만 표시가능하기 때문이다. 



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

#define SCALE 0.2


int main(int argc, char** argv)
{
	if (argc != 2)
	{
		cout << " Provide image name to read" << endl;
		return -1;
	}

	Mat inputImg, dispImg, disp2Img;
	Mat hlsImg;

	inputImg = imread(argv[1], IMREAD_COLOR); // read in BGR format

	if (inputImg.data == 0)
	{
		cout << "Unable to read " << argv[1] << endl;
		return 0;
	}

	// inputImg is too large to be shown. 
	// use scaled-down dispImg instead just for display
	// use inputImg for the histogram calculation
	resize(inputImg, dispImg, Size(), SCALE, SCALE, CV_INTER_AREA);

	cvtColor(dispImg, hlsImg, COLOR_BGR2HLS_FULL);

	vector<Mat> channels(hlsImg.channels());
	split(hlsImg, channels);
	equalizeHist(channels[1], channels[1]);
	merge(channels, hlsImg);
	cvtColor(hlsImg, disp2Img, COLOR_HLS2BGR_FULL);

	namedWindow("Original", CV_WINDOW_AUTOSIZE);
	namedWindow("After luminacense equalization", CV_WINDOW_AUTOSIZE);

	moveWindow("Original", 100, 100);
	moveWindow("After luminacense equalization", 120, 120);

	imshow("Original", dispImg);
	imshow("After luminacense equalization", disp2Img);

	waitKey(0);
	return 0;
}


반응형
LIST
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>

int main(void)
{
	int n;
	int sum = 0;
	printf("숫자를 입력하세요: ");
	scanf("%d", &n);

	for (int i = 1; i <= n; i++)
	{
		if (n % i == 0)
		{
			int j;
			// i : 약수
			for (j = 2; j <= i - 1; j++)
			{
				if (i%j == 0)
				{
					break;
				}
			}
			if (j == i)
			{
				sum = sum + i;
			}
			else
			{
				// 소수아님
			}

		}
	}
	printf("%d ", sum);
}
반응형
LIST

'C언어프로그래밍 > 소스코드' 카테고리의 다른 글

OJ 1170  (1) 2016.04.25
OJ 1166  (0) 2016.04.25
OJ 1172  (0) 2016.04.25
OJ 1124번 답  (1) 2016.04.11
OJ 1122번 답  (0) 2016.04.11
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>

int main(void)
{
	int n;
	int i = 3;
	int p = 1; // previous
	int pp = 1; // previous previous
	int me;
	printf("숫자를 입력하세요: ");
	scanf("%d", &n);

	if (n <= 2)
	{
		printf("1");
		return 0;
	}
	else
	{
		while (i <= n)
		{
			me = pp + p;
			pp = p;
			p = me;
			i++;
		}
		printf("%d ", me);
	}
}


반응형
LIST

'C언어프로그래밍 > 소스코드' 카테고리의 다른 글

OJ 1170  (1) 2016.04.25
OJ 1166  (0) 2016.04.25
OJ 1172  (0) 2016.04.25
OJ 1136번 해답  (0) 2016.04.11
OJ 1122번 답  (0) 2016.04.11
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>

int main(void)
{
	int n;
	int i;
	int r;
	int upper;
	int target;
	printf("숫자를 입력하세요: ");
	scanf("%d", &n);

	// calculate the remainder by 5
	r = n % 5;
	upper = 5 - r;
	if (r < upper)
	{
		target = n - r;
	}
	else // r > upper
	{
		target = n + upper;
	}

	for (i = 1; i <= target; i++)
	{
		printf("%d ", i);
	}
}
반응형
LIST

'C언어프로그래밍 > 소스코드' 카테고리의 다른 글

OJ 1170  (1) 2016.04.25
OJ 1166  (0) 2016.04.25
OJ 1172  (0) 2016.04.25
OJ 1136번 해답  (0) 2016.04.11
OJ 1124번 답  (1) 2016.04.11
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include <stdio.h>
#include <stdlib.h>

struct node
{
	int data;
	struct node *prev;
	struct node *next;
};
struct node *head = 0;

// returns the pointer to the node which has _data
// returns null if no node has _data
struct node* whereIsNode(int _data)
{
	struct node *temp = head;

	while (temp != 0)
	{
		if (temp->data == _data)
		{
			break;
		}
		temp = temp->next;
	}
	return temp;

}





void addToDLL(int n)
{
	struct node *cur;
	cur = (struct node *)malloc(sizeof(struct node));
	cur->next = cur->prev = 0;
	cur->data = n;

	// check if there is no node
	if (head == 0)
	{
		head = cur;
		return;
	}

	// find the last node
	{
		struct node *temp = head;
		while (temp->next != 0)
		{
			temp = temp->next;
		}
		temp->next = cur;
		cur->prev = temp;
		return;
	}
}

void showDLL()
{
	struct node *cur = head;
	while (cur != 0)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
}

void showNeighbors(int _data)
{
	struct node *temp;
	temp = whereIsNode(_data);

	if (temp == 0)
	{
		printf("-999\n");
		return;
	}
	else
	{
		// prev prev
		if (temp->prev != 0)
		{
			if (temp->prev->prev != 0)
			{
				printf("%d ", temp->prev->prev->data);
			}
			else
			{
				printf("-999 ");
			}
		}
		else
		{
			printf("-999 ");
		}

		// next next
		if (temp->next != 0)
		{
			if (temp->next->next != 0)
			{
				printf("%d \n", temp->next->next->data);
			}
			else
			{
				printf("-999 \n");
			}
		}
		else
		{
			printf("-999 \n");
		}

	}


}

int main(void)
{
	addToDLL(1);
	addToDLL(2);
	addToDLL(3);
	addToDLL(4);
	addToDLL(5);
	addToDLL(6);
	addToDLL(7);
	addToDLL(8);

	showNeighbors(5); // 3, 7
	showNeighbors(8); // 6, -999
	showNeighbors(1); // -999, 3
	showNeighbors(100); // -999

	return 0;
}
반응형
LIST

'데이터구조 > 소스코드' 카테고리의 다른 글

SLL with character string member  (0) 2016.04.14
String function review  (0) 2016.04.14
OJ 1132번 해답  (0) 2016.04.11
Online Judge 1117 문제의 해답  (1) 2016.04.04
Queue 구현코드 (큐)  (0) 2016.04.04
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#include <stdio.h>
#include <stdlib.h>

struct node
{
	int data;
	struct node *prev;
	struct node *next;
};
struct node *head = 0;

struct statNode
{
	int data;
	int freq;
	struct statNode *prev;
	struct statNode *next;
};
struct statNode *sHead = 0;


// returns the pointer to the node which has bigger data than _data
// returns null if _data is the biggest
struct statNode *whoIsBigger(int _data)
{
	struct statNode *temp = sHead;

	while (temp != 0)
	{
		if (temp->data > _data)
		{
			break;
		}
		temp = temp->next;
	}
	return temp;

}

// returns the pointer to the node which has _data
// returns null if no node has _data
struct statNode* whereIsNode(int _data)
{
	struct statNode *temp = sHead;

	while (temp != 0)
	{
		if (temp->data == _data)
		{
			break;
		}
		temp = temp->next;
	}
	return temp;

}

void showStatDLL()
{
	struct statNode *temp = sHead;
	while (temp != 0)
	{
		printf("%d %d ", temp->data, temp->freq);
		temp = temp->next;
	}
}

void addToStatDLL(int _data)
{
	// 1. determine which node has _data
	struct statNode *temp;
	temp = whereIsNode(_data);
	if (temp != 0) // temp is pointing to the node having _data
	{
		temp->freq += 1;
		return;
	}
	else  // no node has _data
	{
		struct statNode *cur;
		cur = (struct statNode *)malloc(sizeof(struct statNode));
		cur->data = _data;
		cur->freq = 1;
		cur->next = cur->prev = 0;

		if (sHead == 0)
		{
			sHead = cur;
			return;
		}
		else
		{
			struct statNode *temp;
			temp = whoIsBigger(_data);
			if (temp == 0)  // _data is the biggest
			{
				// add to the last location
				struct statNode *final = sHead;
				while (final->next != 0)
				{
					final = final->next;
				}
				final->next = cur;
				cur->prev = final;
				return;

			}
			else
			{
				if (temp == sHead)  // _data is the smallest
				{
					// add to the first location
					cur->next = sHead;
					sHead = cur;
					cur->next->prev = cur;
					return;
				}
				else
				{
					// insert _data before temp
					cur->next = temp;
					cur->prev = temp->prev;
					temp->prev = cur;
					cur->prev->next = cur;
					return;
				}
			}
		}
	}
}



void addToDLL(int n)
{
	struct node *cur;
	cur = (struct node *)malloc(sizeof(struct node));
	cur->next = cur->prev = 0;
	cur->data = n;

	// check if there is no node
	if (head == 0)
	{
		head = cur;
		return;
	}

	// find the last node
	{
		struct node *temp = head;
		while (temp->next != 0)
		{
			temp = temp->next;
		}
		temp->next = cur;
		cur->prev = temp;
		return;
	}
}

void showDLL()
{
	struct node *cur = head;
	while (cur != 0)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
}

int main(void)
{
	addToDLL(1);
	addToDLL(4);
	addToDLL(3);
	addToDLL(2);
	addToDLL(1);
	addToDLL(3);
	addToDLL(2);
	addToDLL(1);

	struct node *temp = head;
	while (temp != 0)
	{
		addToStatDLL(temp->data);
		temp = temp->next;
	}

	showStatDLL();
	return 0;
}
반응형
LIST

'데이터구조 > 소스코드' 카테고리의 다른 글

String function review  (0) 2016.04.14
OJ 1131번 해답  (0) 2016.04.11
Online Judge 1117 문제의 해답  (1) 2016.04.04
Queue 구현코드 (큐)  (0) 2016.04.04
Stack 구현 소스  (0) 2016.04.04
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package univ.embedded.timer;

import javax.swing.*;

public class TimerLogic extends Thread 
{
	private JLabel timeLabel = null;
	private TimerUI tui = null;
	
	public TimerLogic()
	{
		
	}
	
	public TimerLogic(JLabel _timeLabel)
	{
		timeLabel = _timeLabel;
	}
	
	public TimerLogic(JLabel _timeLabel, TimerUI _tui)
	{
		timeLabel = _timeLabel;
		tui = _tui;
	}
	
	
	public void run()
	{
		for (int i = 10; i >= 0; i--)
		{
			timeLabel.setText(""+i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		tui.activateButton();
		
	}
	

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import univ.embedded.timer.*;

public class MyTest {

	public static void main(String[] argv)
	{
		TimerUI tu = new TimerUI();
	}
	
}
반응형
LIST
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package univ.embedded.timer;

import java.awt.Color;
import java.awt.event.*;

import javax.swing.*;

public class TimerUI extends JFrame 
implements ActionListener
{
	JLabel timeLabel = null;
	JButton startButton = null;

	@Override
	public void actionPerformed(ActionEvent e) 
	{
		//TimerLogic lc = new TimerLogic(timeLabel);
		//lc.start();
		startButton.setText("NO TOUCH");
		startButton.setEnabled(false);
		
		(new TimerLogic(timeLabel, this)).start();
	}
	
	public void activateButton()
	{
		startButton.setText("Start");
		startButton.setEnabled(true);
	}
	
	public TimerUI()
	{
		setSize(300, 200);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setTitle("10 Sec. Timer");
		
		timeLabel = new JLabel("10");
		startButton = new JButton("Start");
		startButton.addActionListener(this);
		
		setLayout(null);
		timeLabel.setSize(100, 60);
		startButton.setSize(100, 60);
		
		timeLabel.setLocation(50, 20);
		startButton.setLocation(50, 100);
		
		add(timeLabel);
		add(startButton);
		
		
		setVisible(true);
	}

	
}
반응형
LIST
문제 설명 
주어진 숫자들로 두 개의 SLL을 구성하고, 두 SLL 간에 같은 숫자가 몇 개 있는지를 출력하는 프로그램을 작성하시오. 


입력 

첫 번째 줄에는 첫 번째 SLL에 추가할 숫자들의 개수 n이 주어진다. n > 0 

두 번째 줄에는 n개의 숫자들이 공백으로 분리하여 주어진다. 숫자들은 모두 서로 다르다. 

세 번째 줄에는 두 번째 SLL에 추가할 숫자들의 개수 m이 주어진다. m > 0 

네 번째 줄에는 m개의 숫자들이 공백으로 분리하여 주어진다. 숫자들은 모두 서로 다르다. 

하지만 두 번째 줄의 숫자들과는 같은 것들이 있을 수 있다. 


출력 첫 번째 SLL과 두 번째 SLL 간에 서로 같은 숫자가 몇 개나 나오는지를 출력한다. 


입력 예시 

1 2 3 4 

1 2 3 4 5 


출력 예시 

4


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <stdio.h>
#include <stdlib.h>
struct node
{
	int data;
	struct node *next;
};

struct node *head1 = 0;
struct node *head2 = 0;

void addToSLL(int _data, struct node** h)
{
	struct node *new_one = (struct node *)malloc(sizeof(struct node));
	new_one->data = _data;
	new_one->next = 0;

	if (*h == 0)
	{
		*h=new_one;
		return;
	}
	struct node *temp = *h;
	while (temp->next != 0)
	{
		temp = temp->next;
	}
	temp->next = new_one;
	return;
}

void countSameData()
{
	struct node *temp1 = head1;
	struct node *temp2;
	int cnt = 0;

	while (temp1 != 0)
	{

		temp2 = head2;
		while (temp2 != 0)
		{
			if (temp1->data == temp2->data)
			{
				cnt++;
			}
			temp2 = temp2->next;
		}
		temp1 = temp1->next;
	}
	printf("%d", cnt);

}

void showSLL(struct node *h)
{
	struct node *temp = h;
	while (temp != 0)
	{
		printf("%d ", temp->data);
		temp = temp->next;
	}
}

int main(void)
{
	int n;
	int data;

	// for the first SLL
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &data);
		addToSLL(data, &head1);
	}

	// for the second SLL
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &data);
		addToSLL(data, &head2);
	}

	//showSLL(head1);
	//showSLL(head2);
	countSameData();
	return 0;
}


반응형
LIST

'데이터구조 > 소스코드' 카테고리의 다른 글

OJ 1131번 해답  (0) 2016.04.11
OJ 1132번 해답  (0) 2016.04.11
Queue 구현코드 (큐)  (0) 2016.04.04
Stack 구현 소스  (0) 2016.04.04
Doubly Linked List (DLL) 소스코드  (0) 2016.03.28
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#include <stdio.h>

#define SZ 5

int queue[5];
int front = 0;
int rear = 0;

int isEmpty()
{
	return (front == rear);
}

int isFull()
{
	return ((rear + 1) % SZ == front);
}

// put the data d into the queue
// only if there is room.
void enque(int d)
{
	if (isFull() == 1)
	{
		return;
	}

	queue[rear] = d;
	// consider that the queue is circular,,
	rear = (rear + 1) % SZ;

	return;
}

// take out one item from the queue
// returns -999 if the queue is empty.
int deque()
{
	if (isEmpty() == 1)
	{
		return -999;
	}

	{
		int temp = queue[front];
		front = (front + 1) % SZ;
		return temp;
	}

}


int main(void)
{

	// test case for the full queue
	for (int i = 0; i <10; i++)
	{
		enque(i);
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d\n", deque());
	}

#if 0
	printf("%d\n", deque());
#endif

#if 0
	enque(3);
	enque(4);
	enque(5);
	printf("%d\n", deque());
	printf("%d\n", deque());
	printf("%d\n", deque());
#endif
	return 0;
}




#if 0

#include <stdio.h>

#define SZ 5

int stack[5];
int top = -1;

// check the stack is empty
// it returns 1 if so,
//    returns 0 otherwise
int isEmpty()
{
	return (top == -1);
}

// check the stack is full
// it returns 1 if so,
//    returns 0 otherwise
int isFull()
{
	// (SZ-1) is the last index of the stack
	return (top == (SZ - 1));
}

// put the data d into the stack
// if the stack is full, do nothing, just return
void push(int d)
{
	if (isFull() == 1)
	{
		return;
	}
	top++;  // because the initial value of top is -1
	stack[top] = d;

	// stack[++top] = d; the above two lines can be 
	//                   written in one line.

	return;
}

// take out one item from the stack
// returns -999 if the stack is empty.
int pop()
{
	if (isEmpty() == 1)
	{
		return -999;  // -999 means the stack is empty.
	}

	top--;
	return stack[top + 1];

	/* the above two lines mean the following.
	{
		int temp = stack[top];
		top--;
		return temp;
	}
	*/

}
int main(void)
{
#if 0
	// test case forf the full stack
	for (int i = 0; i <10; i++)
	{
		push(i);
	}
	printf("%d\n", pop());
#endif
#if 0
	// test case for the empty stack
	printf("%d \n", pop());
#endif
#if 0
	push(1);
	push(2);
	push(3);
	for (int i = 0; i < 3; i++)
	{
		printf("%d\n", pop());
	}
#endif
	return 0;
}

#endif
반응형
LIST

'데이터구조 > 소스코드' 카테고리의 다른 글

OJ 1132번 해답  (0) 2016.04.11
Online Judge 1117 문제의 해답  (1) 2016.04.04
Stack 구현 소스  (0) 2016.04.04
Doubly Linked List (DLL) 소스코드  (0) 2016.03.28
Singly Linked List (SLL) 소스코드 (Source code)  (0) 2016.03.24
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <stdio.h>

#define SZ 5

int stack[5];
int top = -1;

// check the stack is empty
// it returns 1 if so,
//    returns 0 otherwise
int isEmpty()
{
	return (top == -1);
}

// check the stack is full
// it returns 1 if so,
//    returns 0 otherwise
int isFull()
{
	// (SZ-1) is the last index of the stack
	return (top == (SZ - 1));
}

// put the data d into the stack
// if the stack is full, do nothing, just return
void push(int d)
{
	if (isFull() == 1)
	{
		return;
	}
	top++;  // because the initial value of top is -1
	stack[top] = d;

	// stack[++top] = d; the above two lines can be 
	//                   written in one line.

	return;
}

// take out one item from the stack
// returns -999 if the stack is empty.
int pop()
{
	if (isEmpty() == 1)
	{
		return -999;  // -999 means the stack is empty.
	}

	top--;
	return stack[top + 1];

	/* the above two lines mean the following.
	{
		int temp = stack[top];
		top--;
		return temp;
	}
	*/

}
int main(void)
{
#if 0
	// test case forf the full stack
	for (int i = 0; i <10; i++)
	{
		push(i);
	}
	printf("%d\n", pop());
#endif
#if 0
	// test case for the empty stack
	printf("%d \n", pop());
#endif
#if 0
	push(1);
	push(2);
	push(3);
	for (int i = 0; i < 3; i++)
	{
		printf("%d\n", pop());
	}
#endif
	return 0;
}
반응형
LIST

'데이터구조 > 소스코드' 카테고리의 다른 글

OJ 1132번 해답  (0) 2016.04.11
Online Judge 1117 문제의 해답  (1) 2016.04.04
Queue 구현코드 (큐)  (0) 2016.04.04
Doubly Linked List (DLL) 소스코드  (0) 2016.03.28
Singly Linked List (SLL) 소스코드 (Source code)  (0) 2016.03.24
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#include <stdio.h>
#include <stdlib.h>

struct node 
{
	int data;
	struct node *next;
	struct node *prev;
};

struct node *head;

void addToDLL(int n)
{
	struct node *cur;
	cur = (struct node *)malloc(sizeof(struct node));
	cur->next = cur->prev = 0;
	cur->data = n;

	// check if there is no node
	if (head == 0)
	{
		head = cur;
		return;
	}

	// find the last node
	{
		struct node *temp = head;
		while (temp->next != 0)
		{
			temp = temp->next;
		}
		temp->next = cur;
		cur->prev = temp;
		return;
	}
}

void showDLL()
{
	struct node *cur = head;
	while (cur != 0)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
}

// find the node with data n
// delete it from DLL
void delFromDLL(int n)
{
	// locate the node with data n
	struct node *cur = head;
	while (cur != 0)
	{
		if (cur->data == n)
		{
			// found the node with data n
			break;
		}
		cur = cur->next;
	}
	
	if (cur == 0)  // failed to find the node with n
	{
		return;
	}

	// now, cur is pointing to the node with n


	// case 1. cur is the first node of DLL
	if (cur == head)
	{
		head = head->next;
		free(cur);
		if (head != 0)
		{
			head->prev = 0;
		}
	}
	else if (cur->next == 0) {
		// case 2.cur is the last node of DLL
		cur->prev->next = 0;
		free(cur);
	}
	else // case3. cur is a middle node
	{
		// connect the previous one with the next one
		cur->prev->next = cur->next;
		// connect the next one with the previous one
		cur->next->prev = cur->prev;
		free(cur);
	}

}

// locate the node with n
// add a new with with m
// if direction == 1, add m mode to the next location
//    direction == -1, add m node to the previous location
void insertToDLL(int n, int m, int direction)
{
	// locate the node with n
	struct node *cur = head;
	while (cur != 0)
	{
		if (cur->data == n)
		{
			// found the node with n
			break;
		}
		cur = cur->next;
	}

	if (cur == 0) // failed to find the node with n
	{
		// thank you
		return;
	}
	else
	{
		// create a new node having m
		struct node *newbie;
		newbie = (struct node *)malloc(sizeof(struct node));
		newbie->next = newbie->prev = 0;
		newbie->data = m;

		// case 1: if n node is the head node
		if (cur == head)
		{
			if (direction == -1)
			{ // add the newbie to the previous location
				head = newbie;
				newbie->next = cur;
				cur->prev = newbie;
				return;
			}
			else
			{ // add the newbie to the next location
				// case 1. cur has a next node
				if (cur->next != 0)
				{
					newbie->next = cur->next;
					newbie->prev = cur;
					cur->next->prev = newbie;
					cur->next = newbie;
					return;
				}
				else
				{ // case 2. cur has no next node
					newbie->prev = cur;
					cur->next = newbie;
					return;
				}
			}
		}
		else if (cur->next != 0)
		{ // case 2: cur is neither the first nor the last node.
			if (direction == 1)  // next location
			{
				newbie->next = cur->next;
				newbie->prev = cur;
				cur->next = newbie;
				newbie->next->prev = newbie;
				return;
			}
			else  // previous location
			{
				newbie->next = cur;
				newbie->prev = cur->prev;
				cur->prev = newbie;
				newbie->prev->next = newbie;
				return;
			}
		}
		else
		{
			// case 3. cur is the last node
			if (direction == 1)
			{  // it is the same with the addtion
				cur->next = newbie;
				newbie->prev = cur;
				return;
			}
			else // add to the previous location
			{
				newbie->next = cur;
				newbie->prev = cur->prev;
				cur->prev = newbie;
				newbie->prev->next = newbie;
				return;
			}
		}



	}


}

int main(void)
{
	addToDLL(1);
	addToDLL(2);
	addToDLL(3);
	addToDLL(4);
	
	insertToDLL(4, 10, -1);

	showDLL();

	return 0;
}
반응형
LIST

'데이터구조 > 소스코드' 카테고리의 다른 글

OJ 1132번 해답  (0) 2016.04.11
Online Judge 1117 문제의 해답  (1) 2016.04.04
Queue 구현코드 (큐)  (0) 2016.04.04
Stack 구현 소스  (0) 2016.04.04
Singly Linked List (SLL) 소스코드 (Source code)  (0) 2016.03.24


HLS 모델과 RGB 모델간의 관계


RGB 모델은 red, green, blue를 섞어서 색깔을 만들어내므로 직관적으로 이해가 쉽다. 그러나 같은 식으로 HLS 모델을 생각하는 것은 쉽지 않다. 

그래서 HLS에서 RGB로의 변환을 통해서 두 모델 간에 어떤 관계가 있는지를 살펴보는 것은 HLS모델 이해에 도움이 된다.

두 모델간의 변환을 쉽게 보여주는 유용한 사이트를 통해 아래와 같은 실험을 진행해보았다.


<실험 1>

Hue를 0으로 하고, 채도에 해당하는 S(Saturation)를 100%하여 원색을 만들고, 밝기에 해당하는 L(luminance)는 50%로 설정하였다. 이렇게 되면 Hue의 각 값에 해당하는 색을 볼 수 있다. 이 경우, 빨간색인 것을 알 수 있다.


<실험 2>

L=100으로 하였다. 이것은 색을 최대로 밝게 만든 것이다. 이 조건에서는 H와 S를 아무리 변화시켜도 흰색밖에는 얻을 수가 없다.  


<실험 3>

L=0으로 하였다. 색을 최대한 어둡게 한 것이다. 이 경우에도 H와 S를 아무리 변화시켜도 검정색 밖에는 얻을 수가 없다. 비유적으로 이해하자면, L은 전등스위치라고 생각하면 되겠다. 전등을 켜면 (L=100) 모든 것이 하얗게 보이고, 끄면 (L= 0) 모든 것이 어둡게 보이는 것이다.


<실험 4>

전등은 절반만 켜놓고 (L=50), 원색정도를 나타내는 S=0으로 설정하여, 색을 갖지 못하도록 하였다. 이 조건에서는 H를 아무리 변화시켜도 회색계통 밖에는 얻을 수가 없다. 비유적으로 S=0이면 흑백사진이고, S값이 커질수록 컬러사진이 되는 것으로 이해하면 된다. 


<실험 5>

전등은 절반만 켜고 (L=50), 컬러사진으로(S=100) 만들어 놓은 상태에서는 H를 변화시키면 다양한 원색들을 볼 수 있다. 따라서 H는 색을 결정하는 주요인자인 것이다.   


HLS와 RGB간의 대응관계


아래 그림 1은 주요색들에 대해서 HLS와 RGB간의 대응관계를 보여준다.


<그림 1> 주요색들의 HLS와 RGB 대응관계 



반응형
LIST




사진에서 어떤 계열의 색이 가장 많은지 알아보자.


RGB모델을 사용하는 영상에서는 이 질문에 쉽게 답하기가 어렵다. 왜냐하면 R, G, B 각각이 256가지씩 값을 가질 수가 있어서 

즉 이만큼의 색 각각에 대해서 몇 개의 픽셀들이 그 값을 갖는지를 알아봐야 하기 때문이다.


HLS모델 영상이라면 문제가 조금은 쉬워진다. 우선 HLS모델이 어떤 것인지는  여기를 보면 된다. 사진에서 어떤 계통의 색이 가장 많은지는 픽셀들이 가지고 있는 Hue, Luminance, Saturation 중에서 Hue 정보만 보면 된다. 게다가 이것은 0 ~ 360까지 값을 갖기 때문에 픽셀들을 이것에 따라 분류하기만 하면 된다. 물론 Hue 정보만 가지고는 정확히 어떤 색이라고 말할 수는 없다. 다만 어떤 계열( 빨간색, 파란색, 초록색 등)의 색인지 만을 판별할 수 있다. 이 정도 정보만 하더라도 이미지를 검색하거나 분류할 때 요긴하게 사용할 수 있다.


좀 더 구체적으로 얘기를 하자면 Hue값에 대해서 사진의 히스토그램을 구하고, 가장 높은 빈도수를 갖는 Hue 값 영역의 색을 사진의 대표 색깔로 하면 되는 것이다. 


복숭아와 레몬

예를 들어, 그림 1은 복숭아와 레몬을 찍은 사진이다. 사람은 대표 색깔을 쉽게 알 수 있는데, 사진에서 빨간색과 노란색 계열이 가장 많다. Hue채널에 대한 히스토그램도 그렇게 나왔을까? 그림 2는 해당하는 Hue채널에 대한 히스토그램으로, 예상한대로 빨간 색 계통의 빈도수가 가장 높고, 그 다음은 노란색이라는 것을 알 수 있다. 히스토그램 선을 그릴 때, 해당 Hue 채널의 색으로 표시했기 때문에 그 영역이 무슨 색인지를 쉽게 알 수 있다. 


<그림 1> 복숭아와 레몬을 찍은 사진. 


<그림 2> 그림 1의 Hue채널에 대한 히스토그램. 각 Hue영역에 해당하는 빈도수를 해당하는 색으로 그렸다.


포도밭

그림 3은 포도밭 사진으로 녹색 계열의 색이 대표색이라고 할 수 있다. 물론 지붕의 빨간색, 하늘의 파란 색도 있다. 이 사진에 대한 Hue 히스토그램은 그림 4이다. 가장 많은 색은 붉은 색, 노란색과 하늘색이고, 오히려 녹색은 그다지 많지 않다. 결과가 직관적으로 이해되지는 않지만, 일단 넘어가자.


<그림 3> 포도밭을 찍은 사진


<그림 4> 그림 3 사진의 Hue채널에 대한 히스토그램


해변

그림 5는 해변 이미지이고, 그림 6은 해당하는 히스토그램이다. 결과에서 보듯이 하늘색 계통이 압도적으로 가장 많다는 것을 알 수 있다.

<그림 5> 해변사진


<그림 6> 그림 5 해변사진에 대한 Hue 히스토그램


녹색실험

그림 3(포도밭)에 대한 히스토그램 (그림 4) 결과가 미심쩍어서 한 가지 실험을 해보았다. 그림 7과 같이 녹색 이미지에 대해서 Hue 히스토그램을 구해본 것이다. 포도밭 녹색을 제대로 검출할 수 있는지 검증하기 위한 목적이다. 결과 히스토그램에서는 녹색이 압도적으로 많이 나왔기 때문에 녹색을 검출하는데 문제가 없었다는 것을 확인할 수 있다. 그렇다면 왜 포도밭에서는 붉은색과 노란색이 왜 그리 많이 나왔던 것일까? 좀 더 생각해 볼 문제다. 

<그림 7> 실험용 녹색 이미지

<그림 8> 그림 7에 대한 Hue 히스토그램


소스코드

위의 실험에 사용된 소스 프로그램이다. 


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; #define SCALE 0.2 /* given hue value, which is original_hue/2 calculate a corresponding RGB. It is assumed that luminance is 0.5 (50%), and saturation is 1.0(100%) */ Scalar cvtHLStoRGB(double hue) { double _hue = 2 * hue; double C = 1.0; double X = (double)(1.0 - abs((double)(((int)(_hue/60.0) % 2) - 1))); double m = 0.0; double R = 0.0; double G = 0.0; double B = 0.0; if (_hue >= 0 && _hue < 60) { R = C*255.0; G = X*255.0; } else if (_hue >= 60 && _hue < 120) { R = X*255.0; G = C*255.0; } else if (_hue >= 120 && _hue < 180) { G = C*255.0; B = X*255.0; } else if (_hue >= 180 && _hue < 240) { G = X*255.0; B = C*255.0; } else if (_hue >= 240 && _hue < 300) { R = X*255.0; B = C*255.0; } else if (_hue >= 300 && _hue < 360) { R = C*255.0; B = X*255.0; } return Scalar(B, G, R); } int main(int argc, char** argv) { if (argc != 2) { cout << " Provide image name to read" << endl; return -1; } Mat inputImg, dispImg; Mat hlsImg; inputImg = imread(argv[1], CV_LOAD_IMAGE_COLOR); // inputImg is too large to be shown. // use scaled-down dispImg instead just for display // use inputImg for the histogram calculation resize(inputImg, dispImg, Size(), SCALE, SCALE, CV_INTER_AREA); cvtColor(inputImg, hlsImg, CV_BGR2HLS); // separate into three images with only one channel, H, L, and S vector<Mat> hls_images(3); split(hlsImg, hls_images); //-------------------------------------- // H, L, S 채널별로 histogram을 계산 //-------------------------------------- // Prepare three histograms for H, L, S MatND* histogram = new MatND[hlsImg.channels()]; // Each image has only one channel. const int* channel_numbers = { 0 }; float channel_range[] = { 0.0, 255.0 }; const float* channel_ranges = channel_range; // 255-bins for histogram int number_bins = 255; // calculate histograms one at a time, H, L, and S // histogram[0] contains H's // histogram[1] contains L's // histogram[2] contains S's for (int chan = 0; chan < hlsImg.channels(); chan++) { calcHist(&(hls_images[chan]), 1, channel_numbers, Mat(), histogram[chan], 1, &number_bins, &channel_ranges); } //------------------------ // Plot the histograms in each image //------------------------ int hist_w = 512; int hist_h = 400; int bin_w = cvRound((double)hist_w / number_bins); // histogram image for H channel Mat histImageH(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0)); normalize(histogram[0], histogram[0], 0, histImageH.rows, NORM_MINMAX, -1, Mat()); // histogram image for L channel Mat histImageL(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0)); normalize(histogram[1], histogram[1], 0, histImageL.rows, NORM_MINMAX, -1, Mat()); // histogram image for S channel Mat histImageS(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0)); normalize(histogram[2], histogram[2], 0, histImageS.rows, NORM_MINMAX, -1, Mat()); for (int i = 1; i < number_bins; i++) { line(histImageH, Point(bin_w*(i - 1), hist_h - cvRound(histogram[0].at<float>(i - 1))), Point(bin_w*(i), hist_h - cvRound(histogram[0].at<float>(i))), cvtHLStoRGB((double)i), 2, 8, 0); line(histImageL, Point(bin_w*(i - 1), hist_h - cvRound(histogram[1].at<float>(i - 1))), Point(bin_w*(i), hist_h - cvRound(histogram[1].at<float>(i))), Scalar(0, 255, 0), 2, 8, 0); line(histImageS, Point(bin_w*(i - 1), hist_h - cvRound(histogram[2].at<float>(i - 1))), Point(bin_w*(i), hist_h - cvRound(histogram[2].at<float>(i))), Scalar(0, 0, 255), 2, 8, 0); } namedWindow("Original", CV_WINDOW_AUTOSIZE); namedWindow("histogramH", CV_WINDOW_AUTOSIZE); namedWindow("histogramL", CV_WINDOW_AUTOSIZE); namedWindow("histogramS", CV_WINDOW_AUTOSIZE); moveWindow("Original", 100, 100); moveWindow("histogramH", 120, 120); moveWindow("histogramL", 140, 140); moveWindow("histogramS", 160, 160); imshow("Original", dispImg); imshow("histogramH", histImageH); imshow("histogramL", histImageL); imshow("histogramS", histImageS); waitKey(0); return 0; }


소스코드 설명


line 13-54

함수 cvtHLStoRGB( )는 Hue값을 BGR 색으로 만들어준다. 이 함수는 Hue 히스토그램에서 Hue영역에 상응하는 색으로 선을 그리는데 사용된다. Hue를 BGR로 변환하는 아래 공식을 프로그램 한 것이다.



line 77-78

HLS영상을 채널별로 구분하여 별도의 영상으로 나누는 과정이다.


line 85-103

채널별로 나뉘어진 영상들에 대해 각각 히스토그램을 계산하는 부분이다. 채널별로 3번에 걸쳐 함수 calcHist( )를 이용하여 히스토그램을 계산하고, 배열 histogram에 저장한다.


line 125-127

Hue 히스토그램에서 해당 Hue 영역의 색으로 선을 그리는 부분이다.

반응형
LIST

컬러이미지를 B, G, R채널별로 히스토그램 구하기


각 채널별로 히스토그램을 별도로 계산해야 한다. Gray이미지의 채널 1개에 대해 하던 방법대로 하되, 다만 B, G, R 채널별로 동일한 작업을 반복해야 한다.


<그림 1> 컬러이미지

<그림 2> Blue 채널의 히스토그램: 하늘이 진한 파랑과 중간 파랑이 많아서 해당 부분의 빈도수가 높은 것을 볼 수 있다.

<그림 3> Green채널의 히스토그램

<그림 4> Red 채널의 히스토그램: 원본이미지의 바위색깔이 전반적으로 불그스레한 계통이므로 진한 빨간 계통의 색이 많이 분포하는 것을 볼 수 있다.


<소스코드 1> 위의 결과를 만들기 위한 소스프로그램

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

#define SCALE 0.2

int main(int argc, char** argv)
{


	if (argc != 2)
	{
		cout << " Provide image name to read" << endl;
		return -1;
	}

	Mat inputImg;

	inputImg = imread(argv[1], CV_LOAD_IMAGE_COLOR);
	resize(inputImg, inputImg, Size(), SCALE, SCALE, CV_INTER_AREA);

	MatND histogramB, histogramG, histogramR;
	const int channel_numbersB[] = { 0 };  // Blue
	const int channel_numbersG[] = { 1 };  // Green
	const int channel_numbersR[] = { 2 };  // Red
	float channel_range[] = { 0.0, 255.0 };
	const float* channel_ranges = channel_range;
	int number_bins = 255;

	// R, G, B별로 각각 히스토그램을 계산한다.
	calcHist(&inputImg, 1, channel_numbersB, Mat(), histogramB, 1, &number_bins, &channel_ranges);
	calcHist(&inputImg, 1, channel_numbersG, Mat(), histogramG, 1, &number_bins, &channel_ranges);
	calcHist(&inputImg, 1, channel_numbersR, Mat(), histogramR, 1, &number_bins, &channel_ranges);

	// Plot the histogram
	int hist_w = 512; int hist_h = 400;
	int bin_w = cvRound((double)hist_w / number_bins);

	Mat histImageB(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));
	normalize(histogramB, histogramB, 0, histImageB.rows, NORM_MINMAX, -1, Mat());

	Mat histImageG(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));
	normalize(histogramG, histogramG, 0, histImageG.rows, NORM_MINMAX, -1, Mat());

	Mat histImageR(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));
	normalize(histogramR, histogramR, 0, histImageR.rows, NORM_MINMAX, -1, Mat());

	for (int i = 1; i < number_bins; i++)
	{
		
		line(histImageB, Point(bin_w*(i - 1), hist_h - cvRound(histogramB.at<float>(i - 1))),
			Point(bin_w*(i), hist_h - cvRound(histogramB.at<float>(i))),
			Scalar(255, 0, 0), 2, 8, 0);
		line(histImageG, Point(bin_w*(i - 1), hist_h - cvRound(histogramG.at<float>(i - 1))),
			Point(bin_w*(i), hist_h - cvRound(histogramG.at<float>(i))),
			Scalar(0, 255, 0), 2, 8, 0);

		line(histImageR, Point(bin_w*(i - 1), hist_h - cvRound(histogramR.at<float>(i - 1))),
			Point(bin_w*(i), hist_h - cvRound(histogramR.at<float>(i))),
			Scalar(0, 0, 255), 2, 8, 0);
	
	}


	namedWindow("Original", CV_WINDOW_AUTOSIZE);
	namedWindow("HistogramB", CV_WINDOW_AUTOSIZE);
	namedWindow("HistogramG", CV_WINDOW_AUTOSIZE);
	namedWindow("HistogramR", CV_WINDOW_AUTOSIZE);

	moveWindow("Original", 100, 100);
	moveWindow("HistogramB", 110, 110);
	moveWindow("HistogramG", 120, 120);
	moveWindow("HistogramR", 130, 130);

	imshow("Original", inputImg);
	imshow("HistogramB", histImageB);
	imshow("HistogramG", histImageG);
	imshow("HistogramR", histImageR);

	waitKey(0);
	return 0;
}



반응형
LIST


Histogram 그리기


이미지에서 픽셀들이 가지는 값들의 출현빈도를 히스토그램 (histogram)이라고 한다. 

예를 들어, gray 이미지에서 각 픽셀은 0부터 255까지의 값을 갖는다.

이미지의 크기를 300 x 300 이라고 한다면, 총 90,000개의 픽셀들을 0~255 값에 따라 분류하여

각 개별값을 갖는 픽셀들이 몇 개씩인지 알아낸 것이 히스토그램이다.


히스토그램은 contranst enhancement (안 보이는 부분을 잘 보이게) 등에 사용되는데,

이 때 사용되는 기술이 histogram equalization이다. 이것의 원리는 이 <<블로그>>를 참조한다.


OpenCV에서는 이미지의 히스토그램 계산이 쉽도록 함수 calcHist( )를 제공한다.


void cv::calcHist(const Mat * images,
int nimages,
const int * channels,
InputArray mask,
OutputArray hist,
int dims,
const int * histSize,
const float ** ranges,
bool uniform = true,
bool accumulate = false 
)



images: Histogram을 계산할 이미지들에 대한 배열이다.

nimages: images 배열에 포함된 이미지의 개수 이다.

channels: Histogram을 계산할 채널 번호들의 배열이다. 예를 들어, 아래 그림과 같이 BGR 이미지 2장에 대해, 첫 번째 이미지는 B 채널, 두 번째 이미지는 G 채널에 대해서 histogram을 구하고자 한다면 {0, 4}를 배열에 넣어서 전달해야 한다.



mask: Histogram을 계산할 영역을 지정할 수 있다. 옵션사항이고, emptry mask (즉, Mat( ))를 전달하면 아무런 동작도 하지 않는다.

hist: Histogram 계산결과를 저장한다.

dims: Histogram 계산결과를 저장한 hist의 차원을 가리킨다. 

histSize: 각 차원의 bin 개수, 즉 빈도수를 분류할 칸의 개수를 의미한다.

ranges: 각 차원의 분류 bin의 최소값과 최대값을 의미한다.


아래는 gray 이미지에 대해 픽셀값 0 ~ 255에 대해 histogram을 계산하여 그래프로 표시한 결과이다.


<그림 1> Histogram을 계산할 대상 이미지 (gray)


<그림 2> 그림 1에 대한 Histogram 계산결과를 시각화한 결과


위의 결과를 만들어 내기 위한 프로그램의 소스코드이다.


<소스코드 1> 히스토그램을 계산하고 그래프로 시각화하는 프로그램

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

#define SCALE 0.2

int main(int argc, char** argv)
{

	if (argc != 2)
	{
		cout << " Provide image name to read" << endl;
		return -1;
	}

	Mat inputImg;
	Mat greyImg;

	inputImg = imread(argv[1], CV_LOAD_IMAGE_COLOR);
	resize(inputImg, inputImg, Size(), SCALE, SCALE, CV_INTER_AREA);

	// inputImg를 gray영상(greyImg)으로 변환한다.
	cvtColor(inputImg, greyImg, CV_BGR2GRAY);

	MatND histogram;
	const int* channel_numbers = { 0 };
	float channel_range[] = { 0.0, 255.0 };
	const float* channel_ranges = channel_range;
	int number_bins = 255;

	calcHist(&greyImg, 1, channel_numbers, Mat(), histogram, 1, &number_bins, &channel_ranges);

	// Plot the histogram
	int hist_w = 512; int hist_h = 400;
	int bin_w = cvRound((double)hist_w / number_bins);

	Mat histImage(hist_h, hist_w, CV_8UC1, Scalar(0, 0, 0));
	normalize(histogram, histogram, 0, histImage.rows, NORM_MINMAX, -1, Mat());

	for (int i = 1; i < number_bins; i++)
	{
		line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(histogram.at<float>(i - 1))),
			Point(bin_w*(i), hist_h - cvRound(histogram.at<float>(i))),
			Scalar(255, 0, 0), 2, 8, 0);
	}

	namedWindow("Original", CV_WINDOW_AUTOSIZE);
	namedWindow("Histogram", CV_WINDOW_AUTOSIZE);
	
	moveWindow("Original", 100, 100);
	moveWindow("Histogram", 120, 120);

	imshow("Original", greyImg);
	imshow("Histogram", histImage);

	waitKey(0);
	return 0;
}


반응형
LIST

Median Filtering을 이용한 잡음제거 (Noise Removal)


잡음을 없애기 위해 local averaging, gaussian smoothing 등을 사용해 보았지만 별로 신통치 않았다.

여기서는 기적처럼 작동하는 방법을 한 가지 소개해 본다. 물론 완벽하게 원래 영상을 복원하지는 못하지만, 최소한 보기 싫은 잡음들은 없앨 수 있다. 다만 사진의 sharpness (선 같은 것들이 명료하게 보이는 정도)가 훼손되기는 한다. 방법에 대해 설명하기 전에 우선 기적같은 결과부터 확인해보자.


그림 1과 2는 원본 영상과 salt and pepper 잡음으로 오염된 영상을 각각 보여준다.

그리고 그림 3은 기적같은 결과를 보여준다. Noise가 싹 다 사라졌다. 그리고 얼핏 보기에 원래 영상처럼 보인다. 하지만 자세히 들여다 보면 에펠탑의 선 같은 것들이 뭉개진 것을 알 수 있다. 


<그림 1> 원본 이미지


<그림 2> Salt & Pepper 잡음이 추가된 영상


<그림 3> Median filtering을 실시한 결과


Median Filtering

이것을 가능하게 한 방법은 주변 픽셀들의 값과 자신의 값들을 크기에 따라 정렬하고 중간값(median)을 선택해서 자신의 픽셀값으로 하는 것이다. 그래서 median filtering이라 불리운다. Noise는 주변 픽셀들과 차이가 많이 나는 값을 가지고 있으므로 local averaging 같이 단순 평균을 구하게 되면, noise에 의해 값이 왜곡되는 정도가 커서 제대로 noise 제거가 되지 않는다. 하지만, 중간값은 주변 픽셀들과 제일 유사한 값이 되기 때문에 noise를 없앨 수 있다. 대신, sharp한 선이나 edge등은 뭉개져 버리는 단점이 있다. OpenCV에서는 meidan filtering을 지원해주는 함수 medianBlur( )를 제공하고 있다.


<소스코드 1> Meidan filtering을 수행하는 코드

line 57-58이 median filtering에 해당하는 부분이고, 주변 픽셀의 범위를 3x3으로 하고 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

/*
img의 전체 픽셀중 noise_ratio 퍼센트 만큼의
픽셀을 salt & pepper noise로 바꾼다.
*/
void addSaltAndPepperNoise(Mat& img, double noise_ratio)
{
	int rows = img.rows;
	int cols = img.cols;
	int ch = img.channels();
	int num_of_noise_pixels = (int)((double)(rows * cols * ch)*noise_ratio);

	for (int i = 0; i < num_of_noise_pixels; i++)
	{
		int r = rand() % rows;  // noise로 바꿀 행을 임의로 선택
		int c = rand() % cols;  // noise로 바꿀 열을 임의로 선택
		int _ch = rand() % ch;  // noise로 바꿀 채널의 임의로 선택

		// img.ptr<uchar>(r)은 r번째 행의 첫번째 픽셀, 첫번째 채널에 대한 주소값을 반환한다.
		uchar* pixel = img.ptr<uchar>(r) +(c*ch) + _ch; // noise로 바꿀 정확한 위치를 계산

		*pixel = (rand() % 2 == 1) ? 255 : 0; // black(0) 혹은 white(255)로 교체
	}
}

int main(int argc, char** argv)
{

	if (argc != 2)
	{
		cout << " Provide image name to read" << endl;
		return -1;
	}

	Mat inputImg;
	Mat spImg;

	inputImg = imread(argv[1], CV_LOAD_IMAGE_COLOR);
	resize(inputImg, inputImg, Size(), 0.25, 0.25, CV_INTER_AREA);

	spImg = inputImg.clone();
	addSaltAndPepperNoise(spImg, 0.05);

	Mat localAvgImg;
	// Noise 제거를 위해 3 by 3 매트릭스를 이용하여
	// local averaing을 시행.
	blur(spImg, localAvgImg, Size(5, 5));  

	Mat gaussianSmoothedImg;
	GaussianBlur(spImg, gaussianSmoothedImg, Size(5, 5), 1.5);

	Mat medianFilteredImg;
	medianBlur(spImg, medianFilteredImg, 3);

	namedWindow("Original", CV_WINDOW_AUTOSIZE);
	namedWindow("SaltAndPepper", CV_WINDOW_AUTOSIZE);
	namedWindow("LocalAveraging", CV_WINDOW_AUTOSIZE);
	namedWindow("GaussianSmoothing", CV_WINDOW_AUTOSIZE);
	namedWindow("MedianFiltered", CV_WINDOW_AUTOSIZE);

	moveWindow("Original", 100, 100);
	moveWindow("SaltAndPepper", 120, 120);
	moveWindow("LocalAveraging", 140, 140);
	moveWindow("GaussianSmoothing", 150, 150);
	moveWindow("MedianFiltered", 160, 160);

	imshow("Original", inputImg);
	imshow("SaltAndPepper", spImg);
	imshow("LocalAveraging", localAvgImg);
	imshow("GaussianSmoothing", gaussianSmoothedImg);
	imshow("MedianFiltered", medianFilteredImg);

	waitKey(0);
	return 0;
}














반응형
LIST

이미지에서 Noise 제거하기


Noise 픽셀들만 정확히 찾아내서 원래 색깔로 돌려놓는 방법이 있으면 좋으련만...

이런 방법을 구현하기는 거의 불가능하다. 왜냐면 어떤 픽셀이 noise인지 알기 쉽지 않을 뿐더러, 설령 알 수 있다하더라도 원래 색깔이 어떤 것인지를 알기는 더더욱 어렵기 때문이다.


Noise를 완전히 없애기는 어려우니, 눈에 덜 띄도록 하는 것이 한 가지 방법이 될 수 있다.

Noise 픽셀의 특징은 주변 픽셀들과 다른 색깔값을 갖는다는 것이다.

그렇다면 주변 픽셀들과 비슷한 색깔을 갖도록 하면 될 것이다.

그런데, 어떤 픽셀이 noise인지 모르기 때문에, 모든 픽셀에 대해서 주변 픽셀들과 비슷한 색을 갖도록 하는 것이 noise를 완화시키는 한 가지 방법이다.


Local Averaging

픽셀이 주변 픽셀들과 유사한 색깔값을 갖도록 하는 한 가지 방법은 자신과 주변픽셀들의 색깔값의 평균값을 구해서 그것으로 자신의 색깔값으로 하는 것이다.

예를들어, 아래 그림과 같이 E픽셀의 값은 주변 8개 픽셀 (A ~ I)들과 자신의 색깔값의 평균으로 하는 것이다.

혹은 이웃의 범위를 조금 더 넓혀서 '파'픽셀의 경우 주변 24개 픽셀 (가 ~ 캬)들과의 평균값으로 한다.


Gaussian Smoothing


Local Averaging은 모든 픽셀들의 값을 같은 비율로 반영한다. 반면에, Gaussian smoothing은 픽셀별로 반영비율을 달리 한 것이다. 동기는 이렇다. 어느 픽셀의 값을 결정하는데, 자기자신의 픽셀값이 가장 많이 반영되는 것이 더 합리적이기 때문이다. 그리고 주변 이웃픽셀들도 멀리 있는 것보다는 가까이 있는 것의 반영비율을 높이는 것이 원래 색깔값을 유지하는 적절한 방법이 될 것이다.


예를 들면 아래 그림은 중앙픽셀값을 계산하기 위한 각 픽셀들의 반영비율을 보여준다. 중앙에 있는 자기자신이 4/16으로 가장 많이 반영되고, 대각선으로 멀이 있는 픽셀들은 1/16로 덜 반영한다. 여기서는 3x3 경우만 보이지만 5x5도 가능하며, 반영비율의 차이도 조절할 수 있다. 


실험결과

Salt and Pepper Noise가 들어간 이미지에 대해 잡음제거 작업을 시도한 결과를 살펴보자.

그림 1과 2는 각각 원본과 인위적으로 noise를 추가한 이미지이다.

그리고 그림 3은 5x5 방식의 local averaging을 noise이미지에 시도한 결과이고, 그림 4는 5x5 방식의 Gaussian smoothing을 시도한 결과이다.

똑같은 5x5 방식일지라도 Gaussain smoothing이 local averaging보다 좀 더 선명한 것을 알 수 있다. 왜냐하면 Gaussian smoothing은 자기자신의 색깔이 좀 더 많이 반영되기 때문에, 어느 정도 윤곽이 남기 때문이다.


<그림1> 원본 이미지


<그림 2> Salt and pepper 잡음이 들어간 이미지


<그림 3> Local averaging (5x5)을 적용한 결과


<그림 4> Gaussian smoothing (5x5)을 적용한 결과


소스코드 1은 그림 <1,2,3,4> 생성에 사용된 것으로, local averaging을 위해 OpenCV가 제공하는 함수 blur와 Gaussain smoothing을 위해서도 OpenCV 함수 GaussianBlur( )를 이용하였다.


<소스코드 1> 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

/*
img의 전체 픽셀중 noise_ratio 퍼센트 만큼의
픽셀을 salt & pepper noise로 바꾼다.
*/
void addSaltAndPepperNoise(Mat& img, double noise_ratio)
{
	int rows = img.rows;
	int cols = img.cols;
	int ch = img.channels();
	int num_of_noise_pixels = (int)((double)(rows * cols * ch)*noise_ratio);

	for (int i = 0; i < num_of_noise_pixels; i++)
	{
		int r = rand() % rows;  // noise로 바꿀 행을 임의로 선택
		int c = rand() % cols;  // noise로 바꿀 열을 임의로 선택
		int _ch = rand() % ch;  // noise로 바꿀 채널의 임의로 선택

		// img.ptr<uchar>(r)은 r번째 행의 첫번째 픽셀, 첫번째 채널에 대한 주소값을 반환한다.
		uchar* pixel = img.ptr<uchar>(r) +(c*ch) + _ch; // noise로 바꿀 정확한 위치를 계산

		*pixel = (rand() % 2 == 1) ? 255 : 0; // black(0) 혹은 white(255)로 교체
	}
}

int main(int argc, char** argv)
{

	if (argc != 2)
	{
		cout << " Provide image name to read" << endl;
		return -1;
	}

	Mat inputImg;
	Mat spImg;

	inputImg = imread(argv[1], CV_LOAD_IMAGE_COLOR);
	resize(inputImg, inputImg, Size(), 0.25, 0.25, CV_INTER_AREA);

	spImg = inputImg.clone();
	addSaltAndPepperNoise(spImg, 0.05);

	Mat localAvgImg;
	// Noise 제거를 위해 3 by 3 매트릭스를 이용하여
	// local averaing을 시행.
	blur(spImg, localAvgImg, Size(5, 5));  

	Mat gaussianSmoothedImg;
	GaussianBlur(spImg, gaussianSmoothedImg, Size(5, 5), 1.5);

	namedWindow("Original", CV_WINDOW_AUTOSIZE);
	namedWindow("SaltAndPepper", CV_WINDOW_AUTOSIZE);
	namedWindow("LocalAveraging", CV_WINDOW_AUTOSIZE);
	namedWindow("GaussianSmoothing", CV_WINDOW_AUTOSIZE);

	moveWindow("Original", 100, 100);
	moveWindow("SaltAndPepper", 120, 120);
	moveWindow("LocalAveraging", 140, 140);
	moveWindow("GaussianSmoothing", 150, 150);

	imshow("Original", inputImg);
	imshow("SaltAndPepper", spImg);
	imshow("LocalAveraging", localAvgImg);
	imshow("GaussianSmoothing", gaussianSmoothedImg);

	waitKey(0);
	return 0;
}


반응형
LIST

계산기 만들기

두 개 숫자의 +, -를 계산하여 출력하는 계산기를 작성하시오.

클래스 CalculatorLogic을 완성하여,

다음과 같은 계산이 동작하도록 하시오.


1 + 1 = 2

1 + 11 = 12

11 + 1 = 12

1 - 1 = 0

1 - 11 = -10

11 - 1 = 10


숫자: 1자리 이상, 10자리까지 처리가 가능해야 함.

연산: +와 -만 처리

CLEAR를 누르면 0으로 초기화하고 0만 표시


입력순서에 맞는 않을 경우, 무시


제출물: 돌아가는 소스코드 시연


소스코드 1.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class CalculatorLogic 
{

	boolean firstNum;
	boolean opSymbol;
	boolean secondNum;
	
	int first;
	int second;
	String op;
	
	public String addSymbol(String s)
	{
		if (s.equalsIgnoreCase("+") == true)
		{
			op = s;
			return "";
		}
		else if (s.equalsIgnoreCase("-") == true)
		{
			op = s;
			return "";
		}
		else if (s.equalsIgnoreCase("=") == true)
		{
			if (op.equalsIgnoreCase("+"))
			{
				return first+second+"";
			}
			else if (op.equalsIgnoreCase("-"))
			{
				return first-second+"";
			}
		}
		else
		{
			return "---";
		}
		return "";

	}
	
	public CalculatorLogic()
	{
		firstNum = false;
		opSymbol = false;
		secondNum = false;
		first = 0;
		second = 0;
		op = "";
	}
	
}


소스코드 2.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import java.awt.*;

import javax.swing.*;

import java.awt.event.*;

public class CalculatorExterior extends JFrame
implements ActionListener
{
	CalculatorLogic cl;
	JPanel mainPanel;
	JPanel bottomPanel;
	JButton[] buttons;
	JTextField resultField;
	String[] buttonLabels = {"0","1","2","3","4",
	                     	 "5", "6", "7", "8", "9",
	                     	 "+", "-", "=", "CLEAR", ""};
	
	private void buildGUI()
	{
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(600, 600);
		setTitle("Calculator");
		
		mainPanel = new JPanel();
		bottomPanel = new JPanel();
		this.setLayout(new GridLayout(2,1));
			
		this.add(mainPanel);
		this.add(bottomPanel);
		
		mainPanel.setLayout(new GridLayout(0,5));
		for (int i = 0; i < 15; i++)
		{
			buttons[i] = new JButton(buttonLabels[i]);
			buttons[i].addActionListener(this);
			
			mainPanel.add(buttons[i]);
		}
		bottomPanel.setLayout(new GridLayout(0,1));
		resultField = new JTextField(40);
		bottomPanel.add(resultField);
		
		setVisible(true);
	}
	
	public CalculatorExterior()
	{
		cl = new CalculatorLogic();
		buttons = new JButton[15];
		buildGUI();
	}

	@Override
	public void actionPerformed(ActionEvent e) 
	{
		JButton btn = (JButton)e.getSource();
		//System.out.println("Button pushed "+btn.getText());
		if (btn == buttons[13]) // clear button
		{
			resultField.setText("0");
		}
		else
		{
			String result = cl.addSymbol(btn.getText());
			resultField.setText(result);
		}
	}

}


소스코드 3.

1
2
3
4
5
6
7
8
9
public class Test {

	public static void main(String[] args) {
		
		CalculatorExterior ce = new CalculatorExterior();

	}

}


반응형
LIST


Salt and Pepper Noise 추가하기


영상잡음의 다른 종류로 salt and pepper noise라는 것이 있다. 

이것은 Gaussian noise와 달리 잡음이 주변 픽셀들과 색깔이 확연히 다르다.

마치 사진 위에 소금과 후추를 떨어뜨려 놓은 것 처럼 보인다고 해서 salt & pepper noise라고 한다.


아래 사진에 salt and pepper noise를 추가해보자 한다.

전체 픽셀중에 약 5%를 무작위로 골라서 해당 위치를 noise로 바꾸는 것이다. 

결과는 아래 사진과 같다.


위 이미지 결과를 만들어내기 위한 소스코드는 아래와 같다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

/*
   img의 전체 픽셀중 noise_ratio 퍼센트 만큼의
   픽셀을 salt & pepper noise로 바꾼다.
*/
void addSaltAndPepperNoise(Mat& img, double noise_ratio)
{
	int rows = img.rows;
	int cols = img.cols;
	int ch = img.channels();
	int num_of_noise_pixels = (int)((double)(rows * cols * ch)*noise_ratio);
	
	for (int i = 0; i < num_of_noise_pixels; i++)
	{
		int r = rand() % rows;  // noise로 바꿀 행을 임의로 선택
		int c = rand() % cols;  // noise로 바꿀 열을 임의로 선택
		int _ch = rand() % ch;  // noise로 바꿀 채널의 임의로 선택

		// img.ptr<uchar>(r)은 r번째 행의 첫번째 픽셀, 첫번째 채널에 대한 주소값을 반환한다.
		uchar* pixel = img.ptr<uchar>(r) +(c*ch) + _ch; // noise로 바꿀 정확한 위치를 계산

		*pixel = (rand() % 2 == 1) ? 255 : 0; // black(0) 혹은 white(255)로 교체
	}
}

int main(int argc, char** argv)
{

	if (argc != 2)
	{
		cout << " Provide image name to read" << endl;
		return -1;
	}

	Mat inputImg;
	Mat spImg;

	inputImg = imread(argv[1], CV_LOAD_IMAGE_COLOR);
	resize(inputImg, inputImg, Size(), 0.3, 0.3, CV_INTER_AREA);

	spImg = inputImg.clone();
	addSaltAndPepperNoise(spImg, 0.05);

	namedWindow("Original", CV_WINDOW_AUTOSIZE);
	namedWindow("SaltAndPepper", CV_WINDOW_AUTOSIZE);


	moveWindow("Original", 100, 100);
	moveWindow("SaltAndPepper", 120, 120);


	imshow("Original", inputImg);
	imshow("SaltAndPepper", spImg);

	waitKey(0);
	return 0;
}


반응형
LIST


잡음이 들어간 사진 만들기 (Gaussian Noise)


사진은 원래 잡음이 없는 것이 좋지만, 일부러 잡음을 만들어 넣더도

새로운 느낌이 난다.

잡음, 즉 noise를 만드는 방법 중, 여기서는 Gaussian Noise를 만들어 넣는 방법을 생각해보자.


우선 결과부터 살펴보면,

아래는 잡음이 없는 깨끗한 영상이다.

아래는 위의 깨끗한 영상에 일부러 잡음을 만들어 넣은 것이다.

특히 이 잡음은 Gaussian noise이다.


Gaussian noise

이것은 평균과 분산을 갖는 정규분포에 따라 임의로 발생한 noise를 말한다.

잡음값이 임의로 막 생겨나기는 하는데, 그 잡음들의 빈도수를 살펴보면 정규분포를 가지고 있다.

즉, 평균값에 가까운 잡음들의 개수가 평균에서 먼 잡음값들보다 많다.


아래는 위의 사진들을 만들어낸 소스코드이다.

line 24: Gaussian noise를 만들어 낸다.

line 28: Gaussian noise를 이미지에 적용하여 잡음이 낀 이미지를 만들어내는 과정이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{

	if (argc != 2)
	{
		cout << " Provide image name to read" << endl;
		return -1;
	}

	Mat inputImg;

	inputImg = imread(argv[1], CV_LOAD_IMAGE_COLOR);
	resize(inputImg, inputImg, Size(), 0.3, 0.3, CV_INTER_AREA);

	Mat noise_image(inputImg.size(), CV_16SC3);
	double average = 0.0;
	double std = 30.0;
	randn(noise_image, Scalar::all(average), Scalar::all(std));

	Mat temp_image;
	inputImg.convertTo(temp_image, CV_16SC3);
	addWeighted(temp_image, 1.0, noise_image, 1.0, 0.0, temp_image);
	temp_image.convertTo(temp_image, inputImg.type());

	
	namedWindow("Original", CV_WINDOW_AUTOSIZE);
	namedWindow("GaussianNoise", CV_WINDOW_AUTOSIZE);


	moveWindow("Original", 100, 100);
	moveWindow("GaussianNoise", 120, 120);


	imshow("Original", inputImg);
	imshow("GaussianNoise", temp_image);

	waitKey(0);
	return 0;
}


반응형
LIST


이미지에서 사람 피부영역만 골라서 표시하기


사람의 피부색은 HLS영상의 기준으로 볼 때 다음과 같은 조건을 만족한다.

Saturation의 값은 0.2보다 크거나 같고,

Hue값은 28도보다 작거나 같고, 또는 330도 보다 크거나 같으며,

Luminance를 Saturation값으로 나눈 값이 0.5와 3.0 사이에 있어야 한다.


이것은 2007년에 Kakumanu라는 사람이 Pattern Recognition 논문지에 "A survey of skin-color modeling and detection methods"라는 제목으로 실은 논문에 나오는 내용이다.


아래와 같은 이미지에서 피부색은 그대로 두고, 나머지 영역은 모두 검게 바꾼다고 하자.

결과는 아래와 같다. 흑인, 백인, 황인에 상관없이 얼굴영역이 많이 검출되었다. 물론 피부인데도 불구하고 제대로 검출되지 못한 부분도 있고, 피부가 아님에도 피부로 오검출된 부분도 있다.

또 다른 이미지를 가지고 실험한 결과이다. 여기서는 각기 다른 인종의 여성 3명을 대상으로 한다. 

결과는 아래와 같이 대부분의 피부 영역이 올바르게 검출되었지만, 일부 머리카락도 피부로 오검출되었다.


위의 피부검출실험에 사용된 소스코드는 아래와 같다.

입력받은 이미지를 HLS영상으로 바꾸어, H, L, S의 값을 이용하였다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{

	if (argc != 2)
	{
		cout << " Provide image name to read" << endl;
		return -1;
	}

	Mat inputImg;
	Mat hlsImg;
	Mat skinImg;

	inputImg = imread(argv[1], CV_LOAD_IMAGE_COLOR);
	//resize(inputImg, inputImg, Size(), 0.4, 0.4, CV_INTER_AREA);
	skinImg = inputImg.clone();

	cvtColor(inputImg, hlsImg, CV_BGR2HLS);
	vector<Mat> hls_images(3);
	split(hlsImg, hls_images);

	for (int row = 0; row < hlsImg.rows; row++)
	{
		for (int col = 0; col < hlsImg.cols; col++)
		{
			uchar H = hlsImg.at<Vec3b>(row, col)[0];
			uchar L = hlsImg.at<Vec3b>(row, col)[1];
			uchar S = hlsImg.at<Vec3b>(row, col)[2];

			double LS_ratio = ((double)L) / ((double)S);
			bool skin_pixel = (S >= 50) && (LS_ratio > 0.5) && (LS_ratio < 3.0) && ((H <= 14) || (H >= 165));

			if (skin_pixel == false)
			{
				skinImg.at<Vec3b>(row, col)[0] = 0;
				skinImg.at<Vec3b>(row, col)[1] = 0;
				skinImg.at<Vec3b>(row, col)[2] = 0;
			}
		}
	}

	namedWindow("Original", CV_WINDOW_AUTOSIZE);
	namedWindow("SkinDetected", CV_WINDOW_AUTOSIZE);


	moveWindow("Original", 100, 100);
	moveWindow("SkinDetected", 120, 120);


	imshow("Original", inputImg);
	imshow("SkinDetected", skinImg);

	waitKey(0);
	return 0;
}



반응형
LIST
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#include <stdio.h>
#include <stdlib.h>  // malloc

struct node
{
    int data;
    struct node *next;
};
struct node *head = 0;

// SLL에 추가하는 함수
void addToSLL(int _data)
{
    struct node *cur;
    cur = (struct node *)malloc(sizeof(struct node));
    cur->data = _data;
    cur->next = 0;

    //1. SLL이 empty
    if (head == 0)
    {
        head = cur;
        return;
    }
    //2. else
    // 2.1 끝을 찾자
    // 2.2 끝에 붙이자.
    {
        struct node *lastNode = head;
        while (lastNode->next != 0)
        {
            lastNode = lastNode->next;
        }
        lastNode->next = cur;
        return;
    }
}

// SLL에 삽입하는 함수
// where을 찾아서, 그 뒤에 what을 추가
// where가 없으면, 맨앞에 what을 추가
void insertToSLL(int where, int what)
{
    // 1. where를 찾자
    struct node *wp = head;
    while (1)
    {
        if (wp == 0)
        {
            break;
        }
        if (wp->data == where)
        {
            break;
        }
        wp = wp->next;
    }
    {
        struct node *cur;  // 새로 추가할 노드
        cur = (struct node *)malloc(sizeof(struct node));
        cur->data = what;
        cur->next = 0;

        if (wp == 0)  // where를 가진 노드가 없으므로, 맨앞에 추가
        {
            cur->next = head;
            head = cur;
            return;
        }
        else  // where를 찾았으므로, 그 뒤에 추가
        {
            cur->next = wp->next;
            wp->next = cur;
            return;
        }
    }
}

// _data를 찾아서 삭제
void delFromSLL(int _data)
{
    struct node *cur = head;

    while (1)
    {
        if (cur == 0)
        {
            break;
        }
        if (cur->data == _data)
        {
            break;
        }
        cur = cur->next;
    }

    if (cur == 0)  // 지울 것을 못 찾았음.
    {
        return;
    }
    {
        //1. 맨앞의 것을 가리키는 경우
        if (cur == head)
        {
            head = head->next;
            free(cur);
            return;
        }
        else
        {
            struct node *prev = head;
            while (prev->next != cur)
            {
                prev = prev->next;
            }
            prev->next = cur->next;
            free(cur);
            return;
        }
    }
}

void destroyReverse()
{
    struct node *cur;
    while (1)
    {
        cur = head;
        if (head->next == 0)  // 노드가 하나 남았을 경우
        {
            free(head);
            head = 0;
            return;
        }
        while (cur->next->next != 0)
        {
            cur = cur->next;
        }
        free(cur->next);
        cur->next = 0;
    }
}

// 완전 삭제
void destroySLL()
{
    struct node *cur = head;

    while (1)
    {
        if (cur == 0)
        {
            return;
        }
        head = head->next;
        free(cur);
        cur = head;

    }
}

// SLL에 들어 있는 데이터를
// 앞에서부터 차례로 출력
void showSLL()
{
    struct node *cur = head;

    while (cur != 0)
    {
        printf("%d-->", cur->data);
        cur = cur->next;
    }
    printf("\n");
    return;
}

int main(void)
{
    int data;
    char op;
    // a 10
    // d 10
    // s 10
    while (1)
    {
        printf("Enter op and data: ");
        scanf(" %c %d", &op, &data);
        switch (op)
        {
        case 'a':
            addToSLL(data);
            break;
        case 'd':
            delFromSLL(data);
            break;
        case 's':
            showSLL();
            break;
        case 'z':
            return 0;
        default:
            break;
        }


    }


    return 0;
}

#if 0
addToSLL(3);
addToSLL(5);
showSLL(); // 3, 5

insertToSLL(3, 10);
showSLL(); // 3, 10, 5

insertToSLL(5, 20);
showSLL(); // 3, 10, 5, 20

insertToSLL(999, 7);
showSLL(); // 7, 3, 10, 5, 20

delFromSLL(7);
delFromSLL(20);
delFromSLL(10);
showSLL(); // 3, 5

destroySLL();
addToSLL(56);
showSLL(); // 56
#endif
반응형
LIST

'데이터구조 > 소스코드' 카테고리의 다른 글

OJ 1132번 해답  (0) 2016.04.11
Online Judge 1117 문제의 해답  (1) 2016.04.04
Queue 구현코드 (큐)  (0) 2016.04.04
Stack 구현 소스  (0) 2016.04.04
Doubly Linked List (DLL) 소스코드  (0) 2016.03.28

함수 scanf ( ) 사용법 제대로 알기


콘솔창을 통해 키보드 입력을 받을 때 유용하게 사용할 수 있는 함수가 scanf이다. 

그런데, scanf의 동작이 미스테리할 때가 있다.

예상치도 않게 줄바꿈이 생기는 현상이 발생할 수 있는데, 여기서는 왜 그런 일이 발생하는지 설명하고, 

고칠 수 있는 방법을 제시한다.


우선, scanf( )가 정상적으로 동작하는 경우를 살펴보자.

간단한 예제 프로그램을 하나 작성해 보자. 

콘솔창에서 숫자를 입력받아서 그 값을 출력하는 동작을 반복하고,

입력값이 -999이면 종료하는 프로그램이다.

소스코드는 아래와 같다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

int main(void)
{
	int data;

	while (1)
	{
		printf("Enter number: ");
		scanf("%d", &data);

		printf("Data is %d\n", data);
		if (data == -999)
		{
			printf("Program ended\n");
			break;
		}
	}

	return 0;
}


실행해 보면, 아래 그림과 같이 예상대로 잘 동작하는 것을 알 수 있다.


미스테리 속으로 

이번에는 문자값을 입력받아 출력을 반복하는 프로그램을 작성해 보자.

다만 문자 'z'가 입력되면 종료된다.

소스코드는 위의 프로그램에서 int 대신 char, 형식지정자는 %d 대신 %c 로 바꾸는 정도로 수정하면 된다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>

int main(void)
{
	char data;

	while (1)
	{
		printf("Enter char: ");
		scanf("%c", &data);

		printf("Data is %c\n", data);
		if (data == 'z')
		{
			printf("Program ended\n");
			break;
		}
	}
}

하지만 실행결과는 아래와 같이 엉망진창이다.

첫 글자 'a'는 정상적으로 출력되었다. 하지만 두 번째 문자 'b'를 입력받기 전에

"Enter char: Data is "와 그리고 공백줄이 출력되면서 꼬이기 시작한다. 

왜 이런 문제가 발생했을까? 숫자를 입력받을 때는 괜찮았는데.. 


미스테리의 원인을 찾아서


위와 같은 문제가 발생한 원인을 추적해보자.

프로그램이 막 시작된 순간을 생각해보자.

사용자는  문자 'a'를 입력하고 엔터키를 눌렀을 것이다.

콘솔창은 scanf에게 그것들을 넘기기 위해 

우선 입력버퍼(input buffer)라는 특수공간에 아래 그림과 같이 문자 'a'와 엔터키값 '\n'을 저장한다.


그러면 scanf는 line 10을 수행하여

		scanf("%c", &data);

입력버퍼에서 한 글자만 읽어서 변수 data에 저장한다.

이렇게 되면, 아래 그림과 같이 scanf는 입력버퍼에서 문자 'a'만 가져가고, '\n'는 남게된다.


가져간 문자 'a'는 출력되고,

다시 while문의 처음으로 와서, line 9와 10을 실행하게 되는데,

		printf("Enter char: ");
		scanf("%c", &data);

이 때, 입력버퍼에 이미 '\n'이 있으므로

사용자의 입력을 기다릴 필요없이 변수 data에는 '\n'이 입력된다.

그래서 "Enter Data : Data is + \n"이 출력되는 것이고, 아래 그림의 빨간 색으로 표시한 부분이다.


그렇다면 첫 번째 프로그램에서는 왜 이런 일이 발생하지 않은 것일까?

scanf("%d")와 scanf("%c")의 차이다.

'\n'는 문자이므로, 첫 번째 프로그램에서는 %d에 해당되지 않으므로

입력대상에서 제외된 것이고, 두 번째 프로그램에서는 입력으로 분류된 것이다.


미스테리를 해결해 보자


문제 해결은 너무나도 간단하다.

변경전



변경후

차이점은 line 10의 scanf에서 "%c" 대신에

공백을 앞에 하나 더 추가하여 " %c"로 하는 것이다.


왜 이것이 해결책이 되는가를 설명해 보자.

scanf는 공백, 줄바꿈, 탭문자 등을 모두 같은 것으로 인식한다는 점을 이용하자.

즉, 입력버퍼에 남아있던 '\n'는 "  %c"에서 앞의 공백으로

인식되기 때문에 data에 입력되지 않고, 실제로 입력한 문자가 data로 들어가게 된다.


아직 의문이 하나 더 남아있다.

처음 프로그램을 시작한 직후에는 '\n'이 입력버퍼에 없는데

어떻게 동작하는 것일까?

scanf는 공백문자가 입력되지 않으면 그냥 pass하고 지나간다는 성질때문이다.

그래서 최초에 "a\n"이 입력되었을 때, 'a'앞에 공백문자가

없어도 data에 'a'를 읽어 들이는데 아무 문제가 없었던 것이다.


수정하여 실행한 결과 예상대로 잘 동작하는 것을 알 수 있다.







반응형
LIST

HLS형식의 이미지 채널별 분리


HLS영상은 Hue, Luminance, Saturation 채널을 가진 영상으로, 각각이 의미하는 바는 다음과 같다.


Hue: 색조라는 뜻. Color와 동의어. 일상적으로 color 단어가 많이 쓰임.

       Hue is synonymous with color.

Luminance: 밝기라는 뜻.

Saturation

1. 색채의 강도, 또는 색깔의 포화도. 즉, 흰색이나 회색이 들어있지 않은 색의 순수한 정도 또는 색의 선명도에 관한 언급.
2. 색의 3속성의 하나로 색의 강도나 순수한 정도.
3. 같은 색 계열에서 나타나는 색의 선명한 정도. 또는 그 차이. 아무 것도 섞지 않아 맑고 깨끗하며 원색에 가까운 것을 채도가 높다고 표현한다.
4. 색의 채도는 검정과 흰색이 포함된 양과 관련한다. 그러나 검정과 흰색, 그리고 회색은 색상이 없고 단지 ‘밝기(brightness)’만 있기 ‘때문에 무채색(achromatic color)’이라고 한다.

[네이버 지식백과] 채도 [saturation, chroma, 彩度] (만화애니메이션사전, 2008. 12. 30., 한국만화영상진흥원)



Hue와 Saturation간의 관계

Saturation이 높을수록 원색에 가깝고, 낮을 수록 회색에 가까와진다.



<그림 1> HLS원본영상

<그림 2> Hue 채널만 분리한 결과

<그림 3> Luminance 채널만 분리한 결과. 흰색영역이 밝은 부분이다.

<그림 4> Saturation 채널영상. 흰색으로 표시한 부분이 원색에 가까운 부분이다.




소스프로그램


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main(int argc, char** argv) { if (argc != 2) { cout << " Provide image name to read" << endl; return -1; } Mat inputImg; Mat hlsImg; inputImg = imread(argv[1], CV_LOAD_IMAGE_COLOR); cvtColor(inputImg, hlsImg, CV_BGR2HLS); vector<Mat> hls_images(3); split(hlsImg, hls_images); // H, L, S로 분리하는 부분 namedWindow("Original", CV_WINDOW_AUTOSIZE); namedWindow("H", CV_WINDOW_AUTOSIZE); namedWindow("L", CV_WINDOW_AUTOSIZE); namedWindow("S", CV_WINDOW_AUTOSIZE); moveWindow("Original", 100, 100); moveWindow("H", 120, 120); moveWindow("L", 140, 140); moveWindow("S", 160, 160); imshow("Original", inputImg); imshow("H", hls_images[0]); imshow("L", hls_images[1]); imshow("S", hls_images[2]); waitKey(0); return 0; }


반응형
LIST


YUV형식의 이미지를 채널별로 출력하기


YUV형식의 이미지는

3개의 채널, Y, U, V를 가지고 있지만

RGB와 달리 

Y채널에는 영상의 밝기,

U, V채널에는 색차정보를 가지고 있다.


아래는 YUV 영상과 이를 각 채널별로 나누어 출력한 것이다.


원본영상


Y채널 영상 (밝기에 대한 정보를 가지고 있다.)


U채널 영상


V채널 영상


소스코드


다음 프로그램은 YUV형식을 가진

이미지를 Y, U, V 채널요소별로 분리하여 출력한다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{

	if (argc != 2)
	{
		cout << " Provide image name to read" << endl;
		return -1;
	}

	Mat inputImg;
	Mat yuvImg;

	inputImg = imread(argv[1], CV_LOAD_IMAGE_COLOR);

	cvtColor(inputImg, yuvImg, CV_BGR2YUV);
	vector<Mat> yuv_images(3);
	split(yuvImg, yuv_images);

	namedWindow("Original", CV_WINDOW_AUTOSIZE);
	namedWindow("Y", CV_WINDOW_AUTOSIZE);
	namedWindow("U", CV_WINDOW_AUTOSIZE);
	namedWindow("V", CV_WINDOW_AUTOSIZE);

	moveWindow("Original", 100, 100);
	moveWindow("Y", 120, 120);
	moveWindow("U", 140, 140);
	moveWindow("V", 160, 160);

	imshow("Original", inputImg);
	imshow("Y", yuv_images[0]);
	imshow("U", yuv_images[1]);
	imshow("V", yuv_images[2]);

	waitKey(0);
	return 0;
}


반응형
LIST


OpenCV 성능향상을 위한 포인터(Pointer) 사용


함수 at( )의 문제점


이미지의 각 픽셀값을 바꿀 때

함수 at( )을 사용했다.

기능상으로는 아무 문제가 없지만, 속도가 느린 단점이 있다.


예를 들어, 다음과 같은 컬러이미지를 반전하는 경우를

생각해보자.


원본 컬러영상


반전한 컬러이미지


함수 at( )을 이용한 이미지 반전코드

	for (int r = 0; r < image_rows; r++)
	{
		for (int c = 0; c < image_cols; c++)
		{
			for (int ch = 0; ch < 3; ch++)
			{
				outputImg.at<Vec3b>(r, c)[ch] = 255 - outputImg.at<Vec3b>(r, c)[ch];
			}
		}
	}

각 픽셀에 대해,

픽셀별로 R, G, B 채널에 대해서 반전시키면 된다.

그런데, 사실 이 방법은 

시간이 오래 걸린다.


시간측정방법

코드가 수행하는데 걸리는 시간을 측정하기 위해서는

함수 getTickCount( )를 이용하면 된다.

	const int64 start = getTickCount();
        .....
	int64 elapsed = (getTickCount() - start);


함수 at( )을 이용하여, 반전이미지 만드는데 걸리는 시간

함수 getTickCount( )를 이용해서

측정한 시간은 평균적으로 210,000 tick 정도 된다.


포인터를 이용한 속도 개선

함수 at ( )을 사용하지 않고,

이미지 데이터에 대해 포인터를 이용하여 직접 픽셀값을

바꾸면 속도를 엄청나게 개선시킬 수 있다.

for (int r = 0; r < image_rows; r++) { uchar* value = inputImg.ptr<uchar>(r); uchar* result = outputImg.ptr<uchar>(r); for (int c = 0; c < image_cols; c++) { *result++ = *value++ ^ 0xff; *result++ = *value++ ^ 0xff; *result++ = *value++ ^ 0xff; } }

위의 코드는 반전이미지를 동일한 방식으로 만들되,

포인터를 이용하여 각 픽셀값을 직접 접근하는 방식이다.

이렇게 했을 때, 측정시간은

평균적으로 4,300 tick이다.


비교 그래프

아래는 함수 at( )과 포인터를 이용했을 때의

각각의 소요시간이다.

포인터를 이용했을 때, 약 50배 정도 속도가 빨라지는 것을 알 수 있다.

따라서, 성능이 중요할 경우에는 포인터 사용을 적극 고려해야 한다.



소스코드

위의 실험수행을 위한 프로그램이다.

중간에 매크로 USE_AT을 #define하면 함수 at( )을 이용하는 방식으로 동작하고,

comment-out하면 포인터를 이용한 방식으로 동작한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{

	if (argc != 2)
	{
		cout << " Provide image name to read" << endl;
		return -1;
	}

	Mat inputImg;
	Mat outputImg;

	inputImg = imread(argv[1], CV_LOAD_IMAGE_COLOR);
	outputImg = inputImg.clone();

	const int64 start = getTickCount();

	int image_rows = inputImg.rows;
	int image_cols = inputImg.cols;

//#define USE_AT
#ifdef USE_AT
	for (int r = 0; r < image_rows; r++)
	{
		for (int c = 0; c < image_cols; c++)
		{
			for (int ch = 0; ch < 3; ch++)
			{
				outputImg.at<Vec3b>(r, c)[ch] = 255 - outputImg.at<Vec3b>(r, c)[ch];
			}
		}
	}
#else if
	for (int r = 0; r < image_rows; r++)
	{
		uchar* value = inputImg.ptr<uchar>(r);
		uchar* result = outputImg.ptr<uchar>(r);
		for (int c = 0; c < image_cols; c++)
		{
			*result++ = *value++ ^ 0xff;
			*result++ = *value++ ^ 0xff;
			*result++ = *value++ ^ 0xff;
		}
	}
#endif
	int64 elapsed = (getTickCount() - start);

	cout << "Elapsed time " << elapsed << endl;

	namedWindow("input", CV_WINDOW_AUTOSIZE);
	namedWindow("output", CV_WINDOW_AUTOSIZE);
	moveWindow("input", 100, 100);
	moveWindow("output", 120, 120);
	imshow("input", inputImg);
	imshow("output", outputImg);

	waitKey(0);
	return 0;
}


반응형
LIST

+ Recent posts