How to detect corner "joints" that connect elements on images?

Viewed 1132

I'm using OpenCV via Python 3.7. I have a set of monochrome images that look like this:

enter image description here

I'd like to find all "joint points" on these images, where a "joint point" - is a center (1 pixel) of every intersection of two planks. These "joints" are roughly represented by red cicrles on the image below:

enter image description here

The first idea was to skeletonize the image and then find all connected edges algorythmically, but all skeletonizations technques gave me wiggly or round corners and extra "sprouts".

import cv2
import numpy as np
from skimage.morphology import skeletonize

image = cv2.imread("SOURCE_IMAGE.jpg", cv2.IMREAD_GRAYSCALE)
binary_image = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 91, 12)
skeleton = (skeletonize(binary_image//255) * 255).astype(np.uint8)

Result:

enter image description here

The second idea was to find inner contours, approximate them to bounding points, find closest neighbours and then somehow calculate centers, but, again, Canny edge detection method gave me wiggly corners and extra points.

import cv2

image = cv2.imread("SOURCE_IMAGE.jpg", cv2.IMREAD_GRAYSCALE)
edged = cv2.Canny(image, 100, 200)

Result:

enter image description here

Are there any reliable approcahes to this problem?

2 Answers

This is my approach to solve this issue:

  1. Determine vertical lines
  2. Determine horizontal lines
  3. Find their intersections which are joints

For first step check each column and determine thin lines and make them black(0). The result will be only vertical lines. For the second step do reverse. At the end compare vertical line image with the horizontal line image. The pixels which are white(255) in both are the intersection points.

Note: Please do not blame me because of coding in C++. I am not familiar with python I just wanted to show my approach and results.

Here is the code and results:

Source:

enter image description here

Vertical Lines:

enter image description here

Horizontal Lines:

enter image description here

Result:

enter image description here

The code:

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;

int main()
{
    Mat img = imread("/ur/image/directory/joints.jpg",1);

    imshow("Source",img);

    int checker = 1,checker2 = 1;
    int begin_y,finish_y2,finish_y,begin_y2;
    Mat vertical_img = img.clone();
    Mat horizontal_img = img.clone();

    cvtColor(vertical_img,vertical_img,CV_BGR2GRAY);
    cvtColor(horizontal_img,horizontal_img,CV_BGR2GRAY);

    int finish_checker = 0,finish_checker2=0;
    for(int i=0;i<horizontal_img.rows;i++)
    {
        for(int j=0;j<horizontal_img.cols;j++)
        {
            if(horizontal_img.at<uchar>(Point(j,i))>100 && checker)
            {
                begin_y = j;
                checker = 0;
            }

            if(horizontal_img.at<uchar>(Point(j,i))<20 && checker==0)
            {
                finish_y = j;
                checker = 1;
                finish_checker = 1;

            }

            if(finish_checker)
            {
                if((finish_y-begin_y)<30)
                {
                    for(int h=begin_y-2;h<=finish_y;h++)
                    {
                        horizontal_img.at<uchar>(Point(h,i)) = 0;
                    }
                }

                finish_checker = 0;
            }
        }
    }

    imshow("Horizontal",horizontal_img);

    for(int i=0;i<vertical_img.cols;i++)
    {
        for(int j=0;j<vertical_img.rows;j++)
        {
            if(vertical_img.at<uchar>(Point(i,j))>100 && checker2)
            {
                begin_y2 = j;
                checker2 = 0;
            }
            if(vertical_img.at<uchar>(Point(i,j))<50 && checker2==0)
            {
                finish_y2 = j;
                checker2 = 1;
                finish_checker2 = 1;
            }
            if(finish_checker2)
            {
                if((finish_y2-begin_y2)<30)
                {
                    for(int h=begin_y2-2;h<=finish_y2;h++)
                    {
                        vertical_img.at<uchar>(Point(i,h)) = 0;
                    }
                }
                finish_checker2 = 0;
            }
        }
    }
    imshow("Vertical",vertical_img);

    for(int y=0;y<img.cols;y++)
    {
        for(int z=0;z<img.rows;z++)
        {
            if(vertical_img.at<uchar>(Point(y,z))>200 && horizontal_img.at<uchar>(Point(y,z))>200)
            {
                img.at<cv::Vec3b>(z,y)[0]=0;
                img.at<cv::Vec3b>(z,y)[1]=0;
                img.at<cv::Vec3b>(z,y)[2]=255;
            }
        }
    }

    imshow("Result",img);
    waitKey(0);
    return 0;
}

Here's a slight modified version of @YunusTemurlenk's approach using Python instead of C++. The idea is:

  1. Obtain binary image. Load image, convert to grayscale, Gaussian blur, then Otsu's threshold.

  2. Obtain horizontal and vertical line masks. Create horizontal and vertical structuring elements with cv2.getStructuringElement then perform cv2.morphologyEx to isolate the lines.

  3. Find joints. We cv2.bitwise_and the two masks together to get the joints.

  4. Find centroid on joint mask. We find contours then calculate the centroid.


Horizontal/vertical line masks

Detected joints in green

enter image description here

Results

enter image description here

import cv2
import numpy as np

# Load image, grayscale, Gaussian blur, Otsus threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Find horizonal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10,1))
horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)

# Find vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,10))
vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)

# Find joints
joints = cv2.bitwise_and(horizontal, vertical)

# Find centroid of the joints
cnts = cv2.findContours(joints, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    # Find centroid and draw center point
    M = cv2.moments(c)
    cx = int(M['m10']/M['m00'])
    cy = int(M['m01']/M['m00'])
    cv2.circle(image, (cx, cy), 3, (36,255,12), -1)

cv2.imshow('thresh', thresh)
cv2.imshow('horizontal', horizontal)
cv2.imshow('vertical', vertical)
cv2.imshow('joints', joints)
cv2.imshow('image', image)
cv2.waitKey()     
Related