Automatic defect detection in welds with Python (X-Ray)

This article is about how to evaluate X-ray images of welds with just a few lines of Python code. For this purpose, the contrast is adjusted during processing, the colors of the image are inverted and the possible discontinuities are highlighted. For that the skimage library was mostly used. This provides classical algorithms and filters for image processing.



For programming a Jupyter notebook with Python 3.7 was used. Particularly useful is the decoupling from the host system or the use of Docker Containers. But this is not a hard requirement if you just want to use the code. In order to carry out deeper analyzes with machine learning later on, a TensorFlow Docker Image has been chosen. If you do not want to use Docker or just want to see the Python code, you can skip the next sections.

Setting up the Docker image

If Docker is installed, the Jupyter notebook can be initialized as follows. To do this, first download the TensorFlow Image (docker pull) and start it (docker run) afterwards. Then it can be easily programmed through the browser since the localhost port 8888 is directed to the web IDE of the container (-p 8888:8888).  The option –name tf names the container tf (short for TensorFlow), to access it faster later. So that the created code can also be saved, a folder “tf” must be created (mkdir), that is mounted as a volume in the Docker Container (option -v <path>). This can also be called arbitrary, but must be adjusted accordingly in the paths. The following commands must be executed:

cd <my-path>
mkdir tf
docker pull tensorflow/tensorflow:latest-py3-jupyter
docker run -p 8888:8888 --name tf -v <path-to-my-tf-folder>/tf:/tf/Development tensorflow/tensorflow:latest-py3-jupyter

Now a Jupyter Notebook server is hosted on the local machine. Following the instructions in the console, you can then go to the corresponding path<token>  in your Browser and create a notebook inside the folder ‘Development . This can be done by clicking on ‘New‘ and ‘Notebook: Python 3‘.

After that you should save the file by clicking ‘File‘ – ‘Save And Checkpoint‘ , which will create an ‘Untitled.ipynb‘. This is now saved to your Host-System.

Setting up a Python Environment

Now the required Python packages have to be installed. This should be done either in the Docker container or with Virtual Environments (if Docker was not used). The command docker exec can access the console of the container and allows you to install packages with pip. Mostly the package scikit-image or skimage for the image processing and matplotlib for plotting is used. Additionally numpy helps in processing numbers. For installation, the following commands must be executed.

docker exec -it tf /bin/bash 
#Console of container
pip install matplotlib numpy scikit-image

The Python Script

First, the different packages have to be imported. We create a new section in the notebook or our script and add the following lines:

import numpy as np
import matplotlib.pyplot as plt
import base64
from skimage import feature, io, filters, util, exposure
from matplotlib.colors import ListedColormap

Then a function is created that adjusts the colors for marking the discontinuities and weld defects. Namely, different image layers should be superimposed. Therefore, the unmarked locations of the layer must be transparent.

def createTransparentMap(cmap):
# Get the colormap colors
my_cmap = cmap(np.arange(cmap.N))
# Set alpha
my_cmap[:,-1] = np.linspace(0, 1, cmap.N)
# Create new colormap
return ListedColormap(my_cmap)

Then a function is defined, which can use base64 encoded images. For this purpose, the formatting of the image string is first checked and then a pixel matrix is ​​output. It could also load a file using skimage’s io.imread method. However, this would have to be mounted then. Here, the base64 variant was chosen for use within a possible web service, as it is often used as standard for RESTful interfaces.

def decode(base64_string):
# check format
if isinstance(base64_string, bytes):
base64_string = base64_string.decode("utf-8")
# decode
imgdata = base64.b64decode(base64_string)
img = io.imread(imgdata, as_gray=True, plugin='imageio')
# pixel matrix
return img

To continue, some constants are defined in the script afterwards. A sigma value used for edge detection with a canny algorithm. Two custom color palettes to mark the discontinuities and a censure detector to detect features in the image. The weld defects are then automatically detected and marked as a feature of the image.

sigma = 0.6
redMap = createTransparentMap(
blueMap = createTransparentMap(
detector = feature.CENSURE()

Afterwards, the actual analysis function for the marking can be created. For this, the image is first converted into a pixel matrix using the previously created decode method. Then a sigmoid function is applied to adjust the contrast of the image. In image processing, the sigmoid function is often used as standard in addition to the ReLU. The fitted matrix serves as input to the censure detector.

def process_image(baseImage):

  im = decode(baseImage)
  # Correction of image (contrast, noise etc)
  sigmoid_im = exposure.adjust_sigmoid(im)

#Feature detection

Afterwards the results of the edge detection are stored in the variables edges1 and edges2. For that a Canny-Algorithm (with the defined sigma) and a Sobel filter is used. However, these are not applied to the sigmoid-adapted matrix because it causes too much noise in the test image.

  # Compute the Canny filter for two values of sigma
  edges1 = feature.canny(im, sigma=sigma)
  edges2 = filters.sobel(im)

Now that the features and edges of the image have been recognized, they can be displayed with matplotlib. For this, a 1-axis graphic is created and drawn with ax.imshow for each image layer. In the lowest level, the colors of the sigmoid matrix are inverted (util.invert) and then the edges (Canny and Sobel) are drawn with the previously adapted colors. Subsequently, the recognized features of the Censure detector are drawn with a scatter plot and the graphics final presented with

  # display edges
  fig, (ax) = plt.subplots(nrows=1, ncols=1, figsize=(32, 12), sharex=True, sharey=True)

  #draw pixels
  ax.imshow(edges1, cmap=redMap)
  ax.imshow(edges2, cmap=redMap)

  # display censure features
  ax.scatter(detector.keypoints[:, 1], detector.keypoints[:, 0], 2 ** detector.scales, facecolors='none', edgecolors='r')

If you use the process_image with a test image it can look like the following. For this test a JPEG file was encoded with this tool (into a binary format). It represents a typical digital RT image of a weld.

sampleImage = '...' 


This short script shows how to adjust an image of a weld with simple function of the skimage library. The parameters and filter functions have been chosen so that they can detect defects and omissions on the test image. For use in practice, however, it is advisable not to specify these parameters rigidly but to choose them adaptively. Next steps for embedding the algorithm in a cloud service could be, for example, AWS Lambda or its own React frontend. In addition, the TensorFlow environment can be used to provide other algorithms, e.g. to use with machine learning. However, to make the first steps in image processing, skimage still offers enough filters and options.

Do you have any questions or comments? Contact us.

Related Posts