diff --git a/Question_41_50/README.md b/Question_41_50/README.md index d3c559fd..bdff1b99 100644 --- a/Question_41_50/README.md +++ b/Question_41_50/README.md @@ -189,6 +189,8 @@ x = - sin(t) / cos(t) * y + r / cos(t) ## Q.47. モルフォロジー処理(膨張) +Morphology Dilate + *imori.jpg*を大津の二値化したものに、モルフォロジー処理による膨張を2回行え。 モルフォロジー処理とは二値化画像の白(255)マス部分を4近傍(上下左右1マス)に膨張、または1マスだけ収縮させる処理をいう。 @@ -198,10 +200,11 @@ x = - sin(t) / cos(t) * y + r / cos(t) モルフォロジー処理の膨張(Dilation)アルゴリズムは、 注目画素I(x, y)=0で、I(x, y-1), I(x-1, y), I(x+1, y), I(x, y+1)のどれか一つが255なら、I(x, y) = 255 とする。 + つまり、上の処理を2回行えば2マス分膨張できることになる。 -例えば、[[0,1,0], [1,0,1], [0,1,0]] のフィルタを掛けた和が255以上なら膨張である、と考える。 +モルフォロジー処理の実装は例えば、[[0,1,0], [1,0,1], [0,1,0]] のフィルタを掛けた和が255以上なら膨張である、と考えることもできる。 |入力 (imori.jpg) |大津の二値化(answers_image/answer_4.jpg)|出力 (answers_image/answer_47.jpg)| |:---:|:---:|:---:| @@ -218,7 +221,9 @@ x = - sin(t) / cos(t) * y + r / cos(t) モルフォロジー処理の収縮(Erosion)アルゴリズムは、 注目画素I(x, y)=255で、I(x, y-1), I(x-1, y), I(x+1, y), I(x, y+1)のどれか一つでも0なら、I(x, y) = 0 とする。 -例えば、[[0,1,0], [1,0,1], [0,1,0]] のフィルタを掛けた和が255*4未満なら収縮である、と考える。 + + +収縮処理は例えば、[[0,1,0], [1,0,1], [0,1,0]] のフィルタを掛けた和が255*4未満なら収縮である、と考えることもできる。 |入力 (imori.jpg) |大津の二値化(answers_image/answer_4.jpg)|出力 (answers_image/answer_48.jpg)| |:---:|:---:|:---:| @@ -234,6 +239,8 @@ x = - sin(t) / cos(t) * y + r / cos(t) オープニング処理とは、モルフォロジー処理の収縮をN回行った後に膨張をN回行う処理である。 + + オープニング処理により、一つだけ余分に存在する画素などを削除できる。 |入力 (imori.jpg) |大津の二値化(answers_image/answer_4.jpg)|出力 (answers_image/answer_49.jpg)| @@ -248,6 +255,8 @@ x = - sin(t) / cos(t) * y + r / cos(t) クロージング処理とは、モルフォロジー処理の膨張をN回行った後に収縮をN回行う処理である。 + + クロージング処理により、途中で途切れた画素を結合することができる。 |入力 (imori.jpg) |Canny(answers_image/answer_43.jpg)|出力 (answers_image/answer_50.jpg)| diff --git a/Question_41_50/answers_cpp/answer_43.cpp b/Question_41_50/answers_cpp/answer_43.cpp index 8f57001b..9440f240 100644 --- a/Question_41_50/answers_cpp/answer_43.cpp +++ b/Question_41_50/answers_cpp/answer_43.cpp @@ -344,7 +344,7 @@ int main(int argc, const char* argv[]){ // read image cv::Mat img = cv::imread("imori.jpg", cv::IMREAD_COLOR); - // Canny step 2 + // Canny cv::Mat edge = Canny(img); //cv::imwrite("out.jpg", out); diff --git a/Question_41_50/answers_cpp/answer_49.cpp b/Question_41_50/answers_cpp/answer_49.cpp new file mode 100644 index 00000000..7e2eed0b --- /dev/null +++ b/Question_41_50/answers_cpp/answer_49.cpp @@ -0,0 +1,217 @@ +#include +#include +#include +#include + +// BGR -> Gray +cv::Mat BGR2GRAY(cv::Mat img){ + // get height and width + int width = img.cols; + int height = img.rows; + + // prepare output + cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1); + + // each y, x + for (int y = 0; y < height; y++){ + for (int x = 0; x < width; x++){ + // BGR -> Gray + out.at(y, x) = 0.2126 * (float)img.at(y, x)[2] \ + + 0.7152 * (float)img.at(y, x)[1] \ + + 0.0722 * (float)img.at(y, x)[0]; + } + } + + return out; +} + +// Gray -> Binary +cv::Mat Binarize_Otsu(cv::Mat gray){ + int width = gray.cols; + int height = gray.rows; + + // determine threshold + double w0 = 0, w1 = 0; + double m0 = 0, m1 = 0; + double max_sb = 0, sb = 0; + int th = 0; + int val; + + // Get threshold + for (int t = 0; t < 255; t++){ + w0 = 0; + w1 = 0; + m0 = 0; + m1 = 0; + for (int y = 0; y < height; y++){ + for (int x = 0; x < width; x++){ + val = (int)(gray.at(y, x)); + + if (val < t){ + w0++; + m0 += val; + } else { + w1++; + m1 += val; + } + } + } + + m0 /= w0; + m1 /= w1; + w0 /= (height * width); + w1 /= (height * width); + sb = w0 * w1 * pow((m0 - m1), 2); + + if(sb > max_sb){ + max_sb = sb; + th = t; + } + } + + std::cout << "threshold:" << th << std::endl; + + // prepare output + cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1); + + // each y, x + for (int y = 0; y < height; y++){ + for (int x = 0; x < width; x++){ + // Binarize + if (gray.at(y, x) > th){ + out.at(y, x) = 255; + } else { + out.at(y, x) = 0; + } + + } + } + + return out; +} + +// Morphology Erode +cv::Mat Morphology_Erode(cv::Mat img, int Erode_time){ + int height = img.cols; + int width = img.rows; + + // output image + cv::Mat tmp_img; + cv::Mat out = img.clone(); + + // for erode time + for (int i = 0; i < Erode_time; i++){ + tmp_img = out.clone(); + + // each pixel + for (int y = 0; y < height; y++){ + for (int x = 0; x < width; x++){ + // check left pixel + if ((x > 0) && (tmp_img.at(y, x - 1) == 255)){ + out.at(y, x) = 255; + continue; + } + + // check up pixel + if ((y > 0) && (tmp_img.at(y - 1, x) == 255)){ + out.at(y, x) = 255; + continue; + } + + // check right pixel + if ((x < width - 1) && (tmp_img.at(y, x + 1) == 255)){ + out.at(y, x) = 255; + continue; + } + + // check left pixel + if ((y < height - 1) && (tmp_img.at(y + 1, x) == 255)){ + out.at(y, x) = 255; + continue; + } + } + } + } + + return out; +} + +// Morphology Dilate +cv::Mat Morphology_Dilate(cv::Mat img, int Dilate_time){ + int height = img.cols; + int width = img.rows; + + // output image + cv::Mat tmp_img; + cv::Mat out = img.clone(); + + // for erode time + for (int i = 0; i < Dilate_time; i++){ + tmp_img = out.clone(); + + // each pixel + for (int y = 0; y < height; y++){ + for (int x = 0; x < width; x++){ + // check left pixel + if ((x > 0) && (tmp_img.at(y, x - 1) == 0)){ + out.at(y, x) = 0; + continue; + } + + // check up pixel + if ((y > 0) && (tmp_img.at(y - 1, x) == 0)){ + out.at(y, x) = 0; + continue; + } + + // check right pixel + if ((x < width - 1) && (tmp_img.at(y, x + 1) == 0)){ + out.at(y, x) = 0; + continue; + } + + // check left pixel + if ((y < height - 1) && (tmp_img.at(y + 1, x) == 0)){ + out.at(y, x) = 0; + continue; + } + } + } + } + + return out; +} + +// Morphology opening +cv::Mat Morphology_Opening(cv::Mat img, int open_time){ + + // Morphology dilate + img = Morphology_Dilate(img, open_time); + + // Morphology erode + img = Morphology_Erode(img, open_time); + + return img; +} + + +int main(int argc, const char* argv[]){ + // read image + cv::Mat img = cv::imread("imori.jpg", cv::IMREAD_COLOR); + + // BGR -> Gray + cv::Mat gray = BGR2GRAY(img); + + // Gray -> Binary + cv::Mat bin = Binarize_Otsu(gray); + + // Morphology Opening + cv::Mat out = Morphology_Opening(bin, 1); + + //cv::imwrite("out.jpg", out); + cv::imshow("sample", out); + cv::waitKey(0); + cv::destroyAllWindows(); + + return 0; +} diff --git a/Question_41_50/answers_cpp/answer_50.cpp b/Question_41_50/answers_cpp/answer_50.cpp new file mode 100644 index 00000000..fe491652 --- /dev/null +++ b/Question_41_50/answers_cpp/answer_50.cpp @@ -0,0 +1,464 @@ +#include +#include +#include +#include + +// BGR -> Gray +cv::Mat BGR2GRAY(cv::Mat img){ + // get height and width + int width = img.cols; + int height = img.rows; + + // prepare output + cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1); + + // each y, x + for (int y = 0; y < height; y++){ + for (int x = 0; x < width; x++){ + // BGR -> Gray + out.at(y, x) = 0.2126 * (float)img.at(y, x)[2] \ + + 0.7152 * (float)img.at(y, x)[1] \ + + 0.0722 * (float)img.at(y, x)[0]; + } + } + + return out; +} + +float clip(float value, float min, float max){ + return fmin(fmax(value, 0), 255); +} + +// gaussian filter +cv::Mat gaussian_filter(cv::Mat img, double sigma, int kernel_size){ + int height = img.rows; + int width = img.cols; + int channel = img.channels(); + + // prepare output + cv::Mat out = cv::Mat::zeros(height, width, CV_8UC3); + if (channel == 1) { + out = cv::Mat::zeros(height, width, CV_8UC1); + } + + // prepare kernel + int pad = floor(kernel_size / 2); + int _x = 0, _y = 0; + double kernel_sum = 0; + + // get gaussian kernel + float kernel[kernel_size][kernel_size]; + + for (int y = 0; y < kernel_size; y++){ + for (int x = 0; x < kernel_size; x++){ + _y = y - pad; + _x = x - pad; + kernel[y][x] = 1 / (2 * M_PI * sigma * sigma) * exp( - (_x * _x + _y * _y) / (2 * sigma * sigma)); + kernel_sum += kernel[y][x]; + } + } + + for (int y = 0; y < kernel_size; y++){ + for (int x = 0; x < kernel_size; x++){ + kernel[y][x] /= kernel_sum; + } + } + + // filtering + double v = 0; + + for (int y = 0; y < height; y++){ + for (int x = 0; x < width; x++){ + // for BGR + if (channel == 3){ + for (int c = 0; c < channel; c++){ + v = 0; + for (int dy = -pad; dy < pad + 1; dy++){ + for (int dx = -pad; dx < pad + 1; dx++){ + if (((x + dx) >= 0) && ((y + dy) >= 0) && ((x + dx) < width) && ((y + dy) < height)){ + v += (double)img.at(y + dy, x + dx)[c] * kernel[dy + pad][dx + pad]; + } + } + } + out.at(y, x)[c] = (uchar)clip(v, 0, 255); + } + } else { + // for Gray + v = 0; + for (int dy = -pad; dy < pad + 1; dy++){ + for (int dx = -pad; dx < pad + 1; dx++){ + if (((x + dx) >= 0) && ((y + dy) >= 0) && ((x + dx) < width) && ((y + dy) < height)){ + v += (double)img.at(y + dy, x + dx) * kernel[dy + pad][dx + pad]; + } + } + } + out.at(y, x) = (uchar)clip(v, 0, 255); + } + } + } + return out; +} + +// Sobel filter +cv::Mat sobel_filter(cv::Mat img, int kernel_size, bool horizontal){ + int height = img.rows; + int width = img.cols; + int channel = img.channels(); + + // prepare output + cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1); + + // prepare kernel + double kernel[kernel_size][kernel_size] = {{1, 2, 1}, {0, 0, 0}, {-1, -2, -1}}; + + if (horizontal){ + kernel[0][1] = 0; + kernel[0][2] = -1; + kernel[1][0] = 2; + kernel[1][2] = -2; + kernel[2][0] = 1; + kernel[2][1] = 0; + } + + int pad = floor(kernel_size / 2); + + double v = 0; + + // filtering + for (int y = 0; y < height; y++){ + for (int x = 0; x < width; x++){ + v = 0; + for (int dy = -pad; dy < pad + 1; dy++){ + for (int dx = -pad; dx < pad + 1; dx++){ + if (((y + dy) >= 0) && (( x + dx) >= 0) && ((y + dy) < height) && ((x + dx) < width)){ + v += (double)img.at(y + dy, x + dx) * kernel[dy + pad][dx + pad]; + } + } + } + out.at(y, x) = (uchar)clip(v, 0, 255); + } + } + return out; +} + +// get edge +cv::Mat get_edge(cv::Mat fx, cv::Mat fy){ + // get height and width + int height = fx.rows; + int width = fx.cols; + + // prepare output + cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1); + + double _fx, _fy; + + for(int y = 0; y < height; y++){ + for(int x = 0; x < width; x++){ + _fx = (double)fx.at(y, x); + _fy = (double)fy.at(y, x); + + out.at(y, x) = (uchar)clip(sqrt(_fx * _fx + _fy * _fy), 0, 255); + } + } + + return out; +} + +// get angle +cv::Mat get_angle(cv::Mat fx, cv::Mat fy){ + // get height and width + int height = fx.rows; + int width = fx.cols; + + // prepare output + cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1); + + double _fx, _fy; + double angle; + + for(int y = 0; y < height; y++){ + for(int x = 0; x < width; x++){ + _fx = fmax((double)fx.at(y, x), 0.000001); + _fy = (double)fy.at(y, x); + + angle = atan2(_fy, _fx); + angle = angle / M_PI * 180; + + if(angle < -22.5){ + angle = 180 + angle; + } else if (angle >= 157.5) { + angle = angle - 180; + } + + //std::cout << angle << " " ; + + // quantization + if (angle <= 22.5){ + out.at(y, x) = 0; + } else if (angle <= 67.5){ + out.at(y, x) = 45; + } else if (angle <= 112.5){ + out.at(y, x) = 90; + } else { + out.at(y, x) = 135; + } + } + } + + return out; +} + + +// non maximum suppression +cv::Mat non_maximum_suppression(cv::Mat angle, cv::Mat edge){ + int height = angle.rows; + int width = angle.cols; + int channel = angle.channels(); + + int dx1, dx2, dy1, dy2; + int now_angle; + + // prepare output + cv::Mat _edge = cv::Mat::zeros(height, width, CV_8UC1); + + for (int y = 0; y < height; y++){ + for (int x = 0; x < width; x++){ + now_angle = angle.at(y, x); + // angle condition + if (now_angle == 0){ + dx1 = -1; + dy1 = 0; + dx2 = 1; + dy2 = 0; + } else if(now_angle == 45) { + dx1 = -1; + dy1 = 1; + dx2 = 1; + dy2 = -1; + } else if(now_angle == 90){ + dx1 = 0; + dy1 = -1; + dx2 = 0; + dy2 = 1; + } else { + dx1 = -1; + dy1 = -1; + dx2 = 1; + dy2 = 1; + } + + if (x == 0){ + dx1 = fmax(dx1, 0); + dx2 = fmax(dx2, 0); + } + if (x == (width - 1)){ + dx1 = fmin(dx1, 0); + dx2 = fmin(dx2, 0); + } + if (y == 0){ + dy1 = fmax(dy1, 0); + dy2 = fmax(dy2, 0); + } + if (y == (height - 1)){ + dy1 = fmin(dy1, 0); + dy2 = fmin(dy2, 0); + } + + // if pixel is max among adjuscent pixels, pixel is kept + if (fmax(fmax(edge.at(y, x), edge.at(y + dy1, x + dx1)), edge.at(y + dy2, x + dx2)) == edge.at(y, x)) { + _edge.at(y, x) = edge.at(y, x); + } + } + } + + return _edge; +} + +// histerisis +cv::Mat histerisis(cv::Mat edge, int HT, int LT){ + int height = edge.rows; + int width = edge.cols; + int channle = edge.channels(); + + // prepare output + cv::Mat _edge = cv::Mat::zeros(height, width, CV_8UC1); + + int now_pixel; + + for (int y = 0; y < height; y++){ + for (int x = 0; x < width; x++){ + // get pixel + now_pixel = edge.at(y, x); + + // if pixel >= HT + if (now_pixel >= HT){ + _edge.at(y, x) = 255; + } + // if LT < pixel < HT + else if (now_pixel > LT) { + for (int dy = -1; dy < 2; dy++){ + for (int dx = -1; dx < 2; dx++){ + // if 8 nearest neighbor pixel >= HT + if (edge.at(fmin(fmax(y + dy, 0), 255), fmin(fmax(x + dx, 0), 255)) >= HT){ + _edge.at(y, x) = 255; + } + } + } + } + } + } + return _edge; +} + + +// Canny +cv::Mat Canny(cv::Mat img){ + // BGR -> Gray + cv::Mat gray = BGR2GRAY(img); + + // gaussian filter + cv::Mat gaussian = gaussian_filter(gray, 1.4, 5); + + // sobel filter (vertical) + cv::Mat fy = sobel_filter(gaussian, 3, false); + + // sobel filter (horizontal) + cv::Mat fx = sobel_filter(gaussian, 3, true); + + // get edge + cv::Mat edge = get_edge(fx, fy); + + // get angle + cv::Mat angle = get_angle(fx, fy); + + // edge non-maximum suppression + edge = non_maximum_suppression(angle, edge); + + // histerisis + edge = histerisis(edge, 50, 20); + + return edge; +} + +// Morphology Erode +cv::Mat Morphology_Erode(cv::Mat img, int Erode_time){ + int height = img.cols; + int width = img.rows; + + // output image + cv::Mat tmp_img; + cv::Mat out = img.clone(); + + // for erode time + for (int i = 0; i < Erode_time; i++){ + tmp_img = out.clone(); + + // each pixel + for (int y = 0; y < height; y++){ + for (int x = 0; x < width; x++){ + // check left pixel + if ((x > 0) && (tmp_img.at(y, x - 1) == 255)){ + out.at(y, x) = 255; + continue; + } + + // check up pixel + if ((y > 0) && (tmp_img.at(y - 1, x) == 255)){ + out.at(y, x) = 255; + continue; + } + + // check right pixel + if ((x < width - 1) && (tmp_img.at(y, x + 1) == 255)){ + out.at(y, x) = 255; + continue; + } + + // check left pixel + if ((y < height - 1) && (tmp_img.at(y + 1, x) == 255)){ + out.at(y, x) = 255; + continue; + } + } + } + } + + return out; +} + +// Morphology Dilate +cv::Mat Morphology_Dilate(cv::Mat img, int Dilate_time){ + int height = img.cols; + int width = img.rows; + + // output image + cv::Mat tmp_img; + cv::Mat out = img.clone(); + + // for erode time + for (int i = 0; i < Dilate_time; i++){ + tmp_img = out.clone(); + + // each pixel + for (int y = 0; y < height; y++){ + for (int x = 0; x < width; x++){ + // check left pixel + if ((x > 0) && (tmp_img.at(y, x - 1) == 0)){ + out.at(y, x) = 0; + continue; + } + + // check up pixel + if ((y > 0) && (tmp_img.at(y - 1, x) == 0)){ + out.at(y, x) = 0; + continue; + } + + // check right pixel + if ((x < width - 1) && (tmp_img.at(y, x + 1) == 0)){ + out.at(y, x) = 0; + continue; + } + + // check left pixel + if ((y < height - 1) && (tmp_img.at(y + 1, x) == 0)){ + out.at(y, x) = 0; + continue; + } + } + } + } + + return out; +} + +// Morphology closing +cv::Mat Morphology_Closing(cv::Mat img, int open_time){ + + // Morphology erode + img = Morphology_Erode(img, open_time); + + // Morphology dilate + img = Morphology_Dilate(img, open_time); + + return img; +} + + +int main(int argc, const char* argv[]){ + // read image + cv::Mat img = cv::imread("imori.jpg", cv::IMREAD_COLOR); + + // Canny + cv::Mat edge = Canny(img); + + // Morphology Opening + cv::Mat out = Morphology_Closing(edge, 1); + + //cv::imwrite("out.jpg", out); + cv::imshow("sample", out); + cv::waitKey(0); + cv::destroyAllWindows(); + + return 0; +} diff --git a/Question_41_50/answers_image/answer_50.jpg b/Question_41_50/answers_image/answer_50.jpg index 22cf9ebc..7f81380b 100644 Binary files a/Question_41_50/answers_image/answer_50.jpg and b/Question_41_50/answers_image/answer_50.jpg differ diff --git a/Question_41_50/answers_py/answer_47.py b/Question_41_50/answers_py/answer_47.py index 9009f066..6b0d0599 100644 --- a/Question_41_50/answers_py/answer_47.py +++ b/Question_41_50/answers_py/answer_47.py @@ -45,8 +45,8 @@ def otsu_binarization(img, th=128): return out -# Dilation -def Dilate(img, Dil_time=1): +# Morphology Erode +def Morphology_Erode(img, Dil_time=1): H, W = img.shape # kernel @@ -76,7 +76,7 @@ def Dilate(img, Dil_time=1): otsu = otsu_binarization(gray) # Morphology - dilate -out = Dilate(otsu, Dil_time=2) +out = Morphology_Erode(otsu, Dil_time=2) # Save result cv2.imwrite("out.jpg", out) diff --git a/Question_41_50/answers_py/answer_48.py b/Question_41_50/answers_py/answer_48.py index d0d29470..bd459e2b 100644 --- a/Question_41_50/answers_py/answer_48.py +++ b/Question_41_50/answers_py/answer_48.py @@ -44,8 +44,8 @@ def otsu_binarization(img, th=128): return out -# Erosion -def Erode(img, Erode_time=1): +# Morphology Dilate +def Morphology_Dilate(img, Erode_time=1): H, W = img.shape out = img.copy() @@ -77,7 +77,7 @@ def Erode(img, Erode_time=1): otsu = otsu_binarization(gray) # Morphology - dilate -out = Erode(otsu, Erode_time=2) +out = Morphology_Dilate(otsu, Erode_time=2) # Save result diff --git a/Question_41_50/answers_py/answer_49.py b/Question_41_50/answers_py/answer_49.py index 6785ff22..410eb5fd 100644 --- a/Question_41_50/answers_py/answer_49.py +++ b/Question_41_50/answers_py/answer_49.py @@ -44,8 +44,8 @@ def otsu_binarization(img, th=128): return out -# Erosion -def Erode(img, Erode_time=1): +# Morphology Dilate +def Morphology_Dilate(img, Erode_time=1): H, W = img.shape out = img.copy() @@ -66,8 +66,8 @@ def Erode(img, Erode_time=1): return out -# Dilation -def Dilate(img, Dil_time=1): +# Morphology Erode +def Morphology_Erode(img, Dil_time=1): H, W = img.shape # kernel @@ -89,8 +89,8 @@ def Dilate(img, Dil_time=1): # Opening morphology def Morphology_Opening(img, time=1): - out = Erode(img, Erode_time=time) - out = Dilate(out, Dil_time=time) + out = Morphology_Dilate(img, Erode_time=time) + out = Morphology_Erode(out, Dil_time=time) return out diff --git a/Question_41_50/answers_py/answer_50.py b/Question_41_50/answers_py/answer_50.py index df5198a6..1353898a 100644 --- a/Question_41_50/answers_py/answer_50.py +++ b/Question_41_50/answers_py/answer_50.py @@ -24,21 +24,24 @@ def gaussian_filter(img, K_size=3, sigma=1.3): if len(img.shape) == 3: H, W, C = img.shape + gray = False else: img = np.expand_dims(img, axis=-1) H, W, C = img.shape + gray = True ## Zero padding pad = K_size // 2 out = np.zeros([H + pad * 2, W + pad * 2, C], dtype=np.float) - out[pad: pad + H, pad: pad + W] = img.copy().astype(np.float) + out[pad : pad + H, pad : pad + W] = img.copy().astype(np.float) ## prepare Kernel K = np.zeros((K_size, K_size), dtype=np.float) for x in range(-pad, -pad + K_size): for y in range(-pad, -pad + K_size): - K[y+pad, x+pad] = np.exp( -(x ** 2 + y ** 2) / (2 * (sigma ** 2))) - K /= (sigma * np.sqrt(2 * np.pi)) + K[y + pad, x + pad] = np.exp( - (x ** 2 + y ** 2) / (2 * sigma * sigma)) + #K /= (sigma * np.sqrt(2 * np.pi)) + K /= (2 * np.pi * sigma * sigma) K /= K.sum() tmp = out.copy() @@ -47,22 +50,29 @@ def gaussian_filter(img, K_size=3, sigma=1.3): for y in range(H): for x in range(W): for c in range(C): - out[pad + y, pad + x, c] = np.sum(K * tmp[y: y + K_size, x: x + K_size, c]) + out[pad + y, pad + x, c] = np.sum(K * tmp[y : y + K_size, x : x + K_size, c]) - out = out[pad: pad + H, pad: pad + W].astype(np.uint8) - out = out[..., 0] + out = np.clip(out, 0, 255) + out = out[pad : pad + H, pad : pad + W] + out = out.astype(np.uint8) + + if gray: + out = out[..., 0] return out # sobel filter def sobel_filter(img, K_size=3): - H, W = img.shape + if len(img.shape) == 3: + H, W, C = img.shape + else: + H, W = img.shape # Zero padding pad = K_size // 2 out = np.zeros((H + pad * 2, W + pad * 2), dtype=np.float) - out[pad: pad + H, pad: pad + W] = gray.copy().astype(np.float) + out[pad : pad + H, pad : pad + W] = img.copy().astype(np.float) tmp = out.copy() out_v = out.copy() @@ -76,44 +86,49 @@ def sobel_filter(img, K_size=3): # filtering for y in range(H): for x in range(W): - out_v[pad + y, pad + x] = np.sum(Kv * (tmp[y: y + K_size, x: x + K_size])) - out_h[pad + y, pad + x] = np.sum(Kh * (tmp[y: y + K_size, x: x + K_size])) + out_v[pad + y, pad + x] = np.sum(Kv * (tmp[y : y + K_size, x : x + K_size])) + out_h[pad + y, pad + x] = np.sum(Kh * (tmp[y : y + K_size, x : x + K_size])) out_v = np.clip(out_v, 0, 255) out_h = np.clip(out_h, 0, 255) - out_v = out_v[pad: pad + H, pad: pad + W].astype(np.uint8) - out_h = out_h[pad: pad + H, pad: pad + W].astype(np.uint8) + out_v = out_v[pad : pad + H, pad : pad + W] + out_v = out_v.astype(np.uint8) + out_h = out_h[pad : pad + H, pad : pad + W] + out_h = out_h.astype(np.uint8) return out_v, out_h - def get_edge_tan(fx, fy): + def get_edge_angle(fx, fy): # get edge strength edge = np.sqrt(np.power(fx.astype(np.float32), 2) + np.power(fy.astype(np.float32), 2)) edge = np.clip(edge, 0, 255) - fx = np.maximum(fx, 1e-5) + fx = np.maximum(fx, 1e-10) #fx[np.abs(fx) <= 1e-5] = 1e-5 # get edge angle - tan = np.arctan(fy / fx) + angle = np.arctan(fy / fx) - return edge, tan + return edge, angle - def angle_quantization(tan): - angle = np.zeros_like(tan, dtype=np.uint8) - angle[np.where((tan > -0.4142) & (tan <= 0.4142))] = 0 - angle[np.where((tan > 0.4142) & (tan < 2.4142))] = 45 - angle[np.where((tan >= 2.4142) | (tan <= -2.4142))] = 95 - angle[np.where((tan > -2.4142) & (tan <= -0.4142))] = 135 + def angle_quantization(angle): + angle = angle / np.pi * 180 + angle[angle < -22.5] = 180 + angle[angle < -22.5] + _angle = np.zeros_like(angle, dtype=np.uint8) + _angle[np.where(angle <= 22.5)] = 0 + _angle[np.where((angle > 22.5) & (angle <= 67.5))] = 45 + _angle[np.where((angle > 67.5) & (angle <= 112.5))] = 90 + _angle[np.where((angle > 112.5) & (angle <= 157.5))] = 135 - return angle + return _angle def non_maximum_suppression(angle, edge): H, W = angle.shape + _edge = edge.copy() for y in range(H): for x in range(W): @@ -137,10 +152,10 @@ def non_maximum_suppression(angle, edge): if y == H-1: dy1 = min(dy1, 0) dy2 = min(dy2, 0) - if max(max(edge[y, x], edge[y+dy1, x+dx1]), edge[y+dy2, x+dx2]) != edge[y, x]: - edge[y, x] = 0 + if max(max(edge[y, x], edge[y + dy1, x + dx1]), edge[y + dy2, x + dx2]) != edge[y, x]: + _edge[y, x] = 0 - return edge + return _edge def hysterisis(edge, HT=100, LT=30): H, W = edge.shape @@ -149,8 +164,8 @@ def hysterisis(edge, HT=100, LT=30): edge[edge >= HT] = 255 edge[edge <= LT] = 0 - _edge = np.zeros((H+2, W+2), dtype=np.float32) - _edge[1:H+1, 1:W+1] = edge + _edge = np.zeros((H + 2, W + 2), dtype=np.float32) + _edge[1 : H + 1, 1 : W + 1] = edge ## 8 - Nearest neighbor nn = np.array(((1., 1., 1.), (1., 0., 1.), (1., 1., 1.)), dtype=np.float32) @@ -178,22 +193,22 @@ def hysterisis(edge, HT=100, LT=30): fy, fx = sobel_filter(gaussian, K_size=3) # get edge strength, angle - edge, tan = get_edge_tan(fx, fy) + edge, angle = get_edge_angle(fx, fy) # angle quantization - angle = angle_quantization(tan) + angle = angle_quantization(angle) # non maximum suppression edge = non_maximum_suppression(angle, edge) # hysterisis threshold - out = hysterisis(edge) + out = hysterisis(edge, 50, 20) return out -# Erosion -def Erode(img, Erode_time=1): +# Morphology Dilate +def Morphology_Dilate(img, Erode_time=1): H, W = img.shape out = img.copy() @@ -214,8 +229,8 @@ def Erode(img, Erode_time=1): return out -# Dilation -def Dilate(img, Dil_time=1): +# Morphology Erode +def Morphology_Erode(img, Dil_time=1): H, W = img.shape # kernel @@ -234,9 +249,10 @@ def Dilate(img, Dil_time=1): return out +# Morphology Closing def Morphology_Closing(img, time=1): - out = Dilate(img, Dil_time=time) - out = Erode(out, Erode_time=time) + out = Morphology_Erode(img, Dil_time=time) + out = Morphology_Dilate(out, Erode_time=time) return out diff --git a/Question_41_50/assets/canny_erode2.jpg b/Question_41_50/assets/canny_erode2.jpg new file mode 100644 index 00000000..3dd6a98a Binary files /dev/null and b/Question_41_50/assets/canny_erode2.jpg differ diff --git a/Question_41_50/assets/morphology_closing.png b/Question_41_50/assets/morphology_closing.png new file mode 100644 index 00000000..639688f7 Binary files /dev/null and b/Question_41_50/assets/morphology_closing.png differ diff --git a/Question_41_50/assets/morphology_dilate.png b/Question_41_50/assets/morphology_dilate.png new file mode 100644 index 00000000..8d16f422 Binary files /dev/null and b/Question_41_50/assets/morphology_dilate.png differ diff --git a/Question_41_50/assets/morphology_erode.png b/Question_41_50/assets/morphology_erode.png new file mode 100644 index 00000000..e61c7d7e Binary files /dev/null and b/Question_41_50/assets/morphology_erode.png differ diff --git a/Question_41_50/assets/morphology_opening.png b/Question_41_50/assets/morphology_opening.png new file mode 100644 index 00000000..7cfbb764 Binary files /dev/null and b/Question_41_50/assets/morphology_opening.png differ diff --git a/README.md b/README.md index a925637e..39bb9878 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Twitterで更新を発信してますぅ https://twitter.com/curry_frog +- 2019.11.22 [C++] Q.49~50 モルフォロジー処理(オープンイング、クロージング)を追加 - 2019.11.21 [C++] Q.48 モルフォロジー処理(収縮)を追加 - 2019.11.20 [C++] Q.47 モルフォロジー処理(膨張)を追加 - 2019.10.27 [C++] Q.44~46 Hough直線検出を追加、[Python]の解答を修正 @@ -147,8 +148,8 @@ $ git clone https://github.com/yoyoyo-yo/Gasyori100knock.git | 41 | [Cannyエッジ検出 (Step.1) エッジ強度](Question_41_50#q41-canny%E3%82%A8%E3%83%83%E3%82%B8%E6%A4%9C%E5%87%BA-step1-%E3%82%A8%E3%83%83%E3%82%B8%E5%BC%B7%E5%BA%A6) | [✓](Question_41_50/answers_py/answer_41.py) | [✓](Question_41_50/answers_cpp/answer_41.cpp) | | 46 | [Hough変換・直線検出 (Step.3) Hough逆変換](Question_41_50#q46-hough%E5%A4%89%E6%8F%9B%E7%9B%B4%E7%B7%9A%E6%A4%9C%E5%87%BA-step3-hough%E9%80%86%E5%A4%89%E6%8F%9B) | [✓](Question_41_50/answers_py/answer_46.py) | [✓](Question_41_50/answers_cpp/answer_46.cpp) | | 42 | [Cannyエッジ検出 (Step.2) 細線化](Question_41_50#q42-canny%E3%82%A8%E3%83%83%E3%82%B8%E6%A4%9C%E5%87%BA-step2-%E7%B4%B0%E7%B7%9A%E5%8C%96) | [✓](Question_41_50/answers_py/answer_42.py) | [✓](Question_41_50/answers_cpp/answer_42.cpp) | | 47 | [モルフォロジー処理(膨張)](Question_41_50#q47-%E3%83%A2%E3%83%AB%E3%83%95%E3%82%A9%E3%83%AD%E3%82%B8%E3%83%BC%E5%87%A6%E7%90%86%E8%86%A8%E5%BC%B5) | [✓](Question_41_50/answers_py/answer_47.py) | [✓](Question_41_50/answers_cpp/answer_47.cpp) | | 43 | [Cannyエッジ検出 (Step.3) ヒステリシス閾処理](Question_41_50#q43-canny%E3%82%A8%E3%83%83%E3%82%B8%E6%A4%9C%E5%87%BA-step3-%E3%83%92%E3%82%B9%E3%83%86%E3%83%AA%E3%82%B7%E3%82%B9%E9%96%BE%E5%87%A6%E7%90%86) | [✓](Question_41_50/answers_py/answer_43.py) | [✓](Question_41_50/answers_cpp/answer_43.cpp) | | 48 | [モルフォロジー処理(収縮)](Question_41_50#q48-%E3%83%A2%E3%83%AB%E3%83%95%E3%82%A9%E3%83%AD%E3%82%B8%E3%83%BC%E5%87%A6%E7%90%86%E5%8F%8E%E7%B8%AE) | [✓](Question_41_50/answers_py/answer_48.py) | [✓](Question_41_50/answers_cpp/answer_48.cpp)| -| 44| [Hough変換・直線検出 (Step.1) Hough変換](Question_41_50#q44-hough%E5%A4%89%E6%8F%9B%E7%9B%B4%E7%B7%9A%E6%A4%9C%E5%87%BA-step1-hough%E5%A4%89%E6%8F%9B) | [✓](Question_41_50/answers_py/answer_44.py) | [✓](Question_41_50/answers_cpp/answer_44.cpp) | | 49 | [オープニング処理](Question_41_50#q49-%E3%82%AA%E3%83%BC%E3%83%97%E3%83%8B%E3%83%B3%E3%82%B0%E5%87%A6%E7%90%86) | [✓](Question_41_50/answers_py/answer_49.py) | | -| 45| [Hough変換・直線検出 (Step.2) NMS](Question_41_50#q45-hough%E5%A4%89%E6%8F%9B%E7%9B%B4%E7%B7%9A%E6%A4%9C%E5%87%BA-step2-nms) | [✓](Question_41_50/answers_py/answer_45.py) | [✓](Question_41_50/answers_cpp/answer_45.cpp) | | 50 | [クロージング処理](Question_41_50#q50-%E3%82%AF%E3%83%AD%E3%83%BC%E3%82%B8%E3%83%B3%E3%82%B0%E5%87%A6%E7%90%86) | [✓](Question_41_50/answers_py/answer_50.py) | | +| 44| [Hough変換・直線検出 (Step.1) Hough変換](Question_41_50#q44-hough%E5%A4%89%E6%8F%9B%E7%9B%B4%E7%B7%9A%E6%A4%9C%E5%87%BA-step1-hough%E5%A4%89%E6%8F%9B) | [✓](Question_41_50/answers_py/answer_44.py) | [✓](Question_41_50/answers_cpp/answer_44.cpp) | | 49 | [オープニング処理](Question_41_50#q49-%E3%82%AA%E3%83%BC%E3%83%97%E3%83%8B%E3%83%B3%E3%82%B0%E5%87%A6%E7%90%86) | [✓](Question_41_50/answers_py/answer_49.py) |[✓](Question_41_50/answers_cpp/answer_49.cpp) | +| 45| [Hough変換・直線検出 (Step.2) NMS](Question_41_50#q45-hough%E5%A4%89%E6%8F%9B%E7%9B%B4%E7%B7%9A%E6%A4%9C%E5%87%BA-step2-nms) | [✓](Question_41_50/answers_py/answer_45.py) | [✓](Question_41_50/answers_cpp/answer_45.cpp) | | 50 | [クロージング処理](Question_41_50#q50-%E3%82%AF%E3%83%AD%E3%83%BC%E3%82%B8%E3%83%B3%E3%82%B0%E5%87%A6%E7%90%86) | [✓](Question_41_50/answers_py/answer_50.py) | [✓](Question_41_50/answers_cpp/answer_50.cpp)| ### [問題51 - 60](Question_51_60)