Spread Color Salt
주어진 영상(img1.jpg)에 빨강, 파랑, 초록 색의 점을 각각 설정한 개수만큼 무작위로 생성하는 프로그램을 작성하고 생성한 영상에서 빨강,파랑, 초록색의 점을 각각 카운트하는 프로그램을 작성하고 카운트 결과가 실제와 일치하는지 검증하라.
처음에는 SpreadSalt함수를 그대로 변형하여 SpreadRed, SpreadBlue, SpreadGreen함수를 만들어서 이미 색이 있는 점을 찍은 곳에 또다시 점을 찍어서 count를 했을때 원하는 숫자만큼 세지지 않는 문제가 있었다.
이를 해결하기 위해 조건문 하나를 더 넣어줘서 빨간점, 파란점, 초록점이 이미 있는 곳에는 다시 찍지 않고 그냥 넘어가되 for문을 돌리는 횟수의 기준이 되는 n을 1감소 시켜서 찍지 않은 횟수만큼 다시 반복하여 결국 원하는 개수의 점을 모두 찍을 수 있도록 수정하였다.
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void SpreadRed(Mat img, int num) {
// num: 전역적인 개수
for (int n = 0; n < num; n++) {
int x = rand() % img.cols; // img.cols는 이미지의 총 너비를 저장
int y = rand() % img.rows; // img.rows는 이미지의 총 높이를 저장
// 이미 점이 찍혀있다면 n을 낮춰서 찍는 횟수에 포함되지 않도록함
Vec3b& pixel = img.at<Vec3b>(y, x);
if ((pixel[0] == 255 && pixel[1] == 0 && pixel[2] == 0) ||
(pixel[0] == 0 && pixel[1] == 255 && pixel[2] == 0) ||
(pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 255)) {
n--;
continue;
}
if (img.channels() == 1) {
// img.channels()는 이미지의 채널 수를 반환
img.at<uchar>(y, x) = 255; // 단일 채널 값 변경
}
else {
img.at<Vec3b>(y, x)[0] = 0; // Blue 채널 값 변경
img.at<Vec3b>(y, x)[1] = 0; // Green 채널 값 변경
img.at<Vec3b>(y, x)[2] = 255; // Red 채널 값 변경
}
}
}
void SpreadBlue(Mat img, int num) {
// num: 전역적인 개수
for (int n = 0; n < num; n++) {
int x = rand() % img.cols; // img.cols는 이미지의 총 너비를 저장
int y = rand() % img.rows; // img.rows는 이미지의 총 높이를 저장
/*
난수지점 난수는 수를 불명확 수 양으로 무작위 위치가
이미지의 크기를 벗어나지 않도록 제한하는 역할을 하여줌
*/
// 이미 점이 찍혀있다면 n을 낮춰서 찍는 횟수에 포함되지 않도록함
Vec3b& pixel = img.at<Vec3b>(y, x);
if ((pixel[0] == 255 && pixel[1] == 0 && pixel[2] == 0) ||
(pixel[0] == 0 && pixel[1] == 255 && pixel[2] == 0) ||
(pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 255)) {
n--;
continue;
}
if (img.channels() == 1) {
// img.channels()는 이미지의 채널 수를 반환
img.at<uchar>(y, x) = 255; // 단일 채널 값 변경
}
else {
img.at<Vec3b>(y, x)[0] = 255; // Blue 채널 값 변경
img.at<Vec3b>(y, x)[1] = 0; // Green 채널 값 변경
img.at<Vec3b>(y, x)[2] = 0; // Red 채널 값 변경
}
}
}
void SpreadGreen(Mat img, int num) {
// num: 전역적인 개수
for (int n = 0; n < num; n++) {
int x = rand() % img.cols; // img.cols는 이미지의 총 너비를 저장
int y = rand() % img.rows; // img.rows는 이미지의 총 높이를 저장
/*
난수지점 난수는 수를 불명확 수 양으로 무작위 위치가
이미지의 크기를 벗어나지 않도록 제한하는 역할을 하여줌
*/
// 이미 점이 찍혀있다면 n을 낮춰서 찍는 횟수에 포함되지 않도록함
Vec3b& pixel = img.at<Vec3b>(y, x);
if ((pixel[0] == 255 && pixel[1] == 0 && pixel[2] == 0) ||
(pixel[0] == 0 && pixel[1] == 255 && pixel[2] == 0) ||
(pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 255)) {
n--;
continue;
}
if (img.channels() == 1) {
// img.channels()는 이미지의 채널 수를 반환
img.at<uchar>(y, x) = 255; // 단일 채널 값 변경
}
else {
img.at<Vec3b>(y, x)[0] = 0; // Blue 채널 값 변경
img.at<Vec3b>(y, x)[1] = 255; // Green 채널 값 변경
img.at<Vec3b>(y, x)[2] = 0; // Red 채널 값 변경
}
}
}
void countSpecificColorPixels(const Mat img) {
int redCount = 0, greenCount = 0, blueCount = 0;
for (int i = 0; i < img.rows; i++) {
for (int j = 0; j < img.cols; j++) {
Vec3b color = img.at<Vec3b>(i, j);
// 빨간색 픽셀을 세기
if (color[2] == 255 && color[1] == 0 && color[0] == 0)
redCount++;
// 초록색 픽셀을 세기
if (color[1] == 255 && color[2] == 0 && color[0] == 0)
greenCount++;
// 파란색 픽셀을 세기
if (color[0] == 255 && color[1] == 0 && color[2] == 0)
blueCount++;
}
}
cout << "Red pixels: " << redCount << endl;
cout << "Green pixels: " << greenCount << endl;
cout << "Blue pixels: " << blueCount << endl;
}
int main() {
//과제 1번 RGB점 칠하기
Mat src_img = imread("img1.jpg", 1); // 이미지 읽기
if (src_img.empty()) {
cerr << "Image load failed!" << endl; // 이미지 로딩 실패 시 메시지 출력
return -1;
}
SpreadRed(src_img, 2000); // 이미지에 빨간점 추가
SpreadGreen(src_img, 2000); // 이미지에 초록점 추가
SpreadBlue(src_img, 2000); // 이미지에 파란점 추가
imshow("Test window", src_img); // 이미지 출력
countSpecificColorPixels(src_img);
imwrite("modified_img1.jpg", src_img);
waitKey(0); // 키 입력 대기(0: 키가 입력될 때 까지 프로그램 멈춤)
destroyWindow("Test window"); // 이미지 출력창 종료
return 0;
}
제대로 RGB점들이 찍힌 것을 볼 수 있고 main함수에서 볼 수 있듯이 2000개씩 점을 찍었는데 정확히 2000개씩 count하는 것에 성공하였다.
주어진 영상을 이용해(img2 jpg) 다음과 같은 두 영상을 생성하는 프로그램을 작성하고(픽셀 값 접근을 이용) 히스토그램 일치 여부를 확인 및 그러한 결과가 나온 이유를 분석하라.
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
// 위로 갈수록 어두워지게 만드는 그라데이션 처리
void gradientTop(Mat img) {
for (int i = 0; i < img.rows; i++) {
for (int j = 0; j < img.cols; j++) {
uchar color = img.at<uchar>(i, j);
// 색상 값 변경으로 그라데이션 효과 적용
color = color * 0.5 * i / img.rows;
img.at<uchar>(i, j) = color;
}
}
}
// 아래로 갈수록 어두워지게 만드는 그라데이션 처리
void gradientBottom(Mat img) {
for (int i = img.rows - 1; i >= 0; i--) {
for (int j = 0; j < img.cols; j++) {
uchar color = img.at<uchar>(i, j);
// 색상 값 변경으로 그라데이션 효과 적용
color = color * 0.5 * (img.rows - i) / img.rows;
img.at<uchar>(i, j) = color;
}
}
}
Mat GetHistogram(Mat src) {
Mat histogram;
const int* channel_numbers = { 0 };
float channel_range[] = { 0.0, 255.0 };
const float* channel_ranges = channel_range;
int number_bins = 255;
calcHist(&src, 1, channel_numbers, Mat(), histogram, 1, &number_bins, &channel_ranges);
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);
}
return histImage;
}
// 메인 함수, 이미지 로드 및 처리
int main() {
// 원본 이미지 로드
Mat img = imread("img2.jpg", IMREAD_GRAYSCALE);
if (img.empty()) {
cerr << "이미지 로드 실패";
return -1;
}
// 위로 어두워지는 그라데이션 적용 이미지 저장
Mat imgGradientTop = img.clone();
gradientTop(imgGradientTop);
Mat histImage1 = GetHistogram(imgGradientTop); // 히스토그램 계산 및 시각화 이미지 반환
imshow("Test window", imgGradientTop); // 이미지 출력
imshow("histogram1", histImage1); // 히스토그램 이미지 출력
waitKey(0); // 키 입력 대기(0: 키가 입력될 때 까지 프로그램 멈춤)
destroyAllWindows(); // 이미지 출력창 종료
imwrite("gradient_top.jpg", imgGradientTop);
// 아래로 어두워지는 그라데이션 적용 이미지 저장
Mat imgGradientBottom = img.clone();
gradientBottom(imgGradientBottom);
Mat histImage2 = GetHistogram(imgGradientBottom); // 히스토그램 계산 및 시각화 이미지 반환
imshow("Test window", imgGradientBottom); // 이미지 출력
imshow("histogram2", histImage2); // 히스토그램 이미지 출력
waitKey(0); // 키 입력 대기(0: 키가 입력될 때 까지 프로그램 멈춤)
destroyAllWindows(); // 이미지 출력창 종료
imwrite("gradient_bottom.jpg", imgGradientBottom);
return 0;
}
위쪽으로 갈수록 어두워지는 그라데이션 처리와 아래로 갈수록 어두워지는 그라데이션 처리는 몇번째 row냐에 따라 전자는 그 수가 작을수록, 후자는 그 수가 클수록 더 검정색에 가까워질 수 있도록 값의 비율을 낮춰줬다.
어둡게 처리가 되었기 때문에 낮은 밝기의 픽셀이 많이 검출되어 histogram이 변형된 것을 확인할 수 있었다.
위의 img3,4,5를 이용하여 아래의 결과를 만들어라.
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
int main() {
Mat main_img = imread("img3.jpg", 1); // 로고를 넣을 이미지
Mat filter = imread("img4.jpg", 1); // 배경을
Mat logo = imread("img5.jpg", 1); // 로고
Mat logo_gray = imread("img5.jpg", 0); // 마스크로 쓸 로고 이미지를 gray level 불러옴
Mat result; // 최종 이미지 저장할 변수 선언
resize(filter, filter, Size(main_img.cols, main_img.rows)); // 이미지 사이즈 통일
subtract(main_img, filter, result); // 두 이미지를 subtract하여 중심부를 남김
// 로고의 위치를 조정
Mat logo_xy(result, Rect(4 * (result.cols - logo.cols) / 7, 3 * (result.rows - logo.rows) / 5, logo.cols, logo.rows));
Mat mask(200 - logo_gray); // 흰색 배경을 제거하는 mask
logo.copyTo(logo_xy, mask); // mask를 이용해 지정한 위치에 로고를 삽입
imshow("Test window", result);
waitKey(0);
destroyWindow("Test window");
}
우선 img3, img4를 불러와 resize 함수를 통해 이미지 사이즈를 맞춰주었다. 이후 제시된 영상의 배경이미지를 만들기 위해 가장자리가 어둡게 만들기 위해 img3에서 img4를 subtract했다. 이후 image_xy 변수에 로고가 들어갈 위치를 가진 이미지 변수를 설정했다. 로고가 x축으로 4/7 지점, y축으로 3/5 지점으로 설정했을때 예시와 가장 비슷하였다. 로고를 넣어주려고 하는데 흰 배경이 문제였다. 이를 제거하기 위한 mask를 만들었다. mask는 gray scale로 변환된 logo이미지를 (200-logo)와 같이 변환하여 200보다 큰 (흰색에 가까운색) 값들은 0이 되도록하고 200보다 작은 값들은 0이상이 되도록하여 copyTo함수에 mask를 넣으면 글씨 부분만 남도록 하였다.
'Quality control (Univ. Study) > Digital Image Processing' 카테고리의 다른 글
Digital Image Processing - Subsampling (3) | 2024.04.10 |
---|---|
Digital Image Processing - Smoothing / Edging (0) | 2024.04.05 |
Digital Image Processing - Denoising / Filter / Edge Detection (1) | 2024.04.03 |
Digital Image Processing - Filtering in Spatial Domain (0) | 2024.03.29 |
Digital Image Processing - Pixel Processing / 히스토그램 (1) | 2024.03.26 |