印刷檢測

客戶需求:使用者會將PDF中的標籤內容印刷到白色的織布上,希望透過視覺檢測可以將印刷不良的情況偵測出來。

我們將計劃使用局部可變形模型來進行標籤內容在織布上的視覺檢測,提供更精細的測試。

局部可變形模型是一種用於視覺檢測的技術,它可以在圖像中尋找局部特定形狀的實例。這種模型具有一定的靈活性,可以應對目標物體在圖像中的變形、旋轉和縮放等情況。

以下是局部可變形模型的一些重要特點和工作原理:

  1. 創建模型:創建局部可變形模型的過程通常包括以下步驟:
    • 選擇目標物體的形狀,可以是點、線、圓等。
    • 從圖像中提取這些形狀的特徵,比如邊緣、角點等。
    • 構建模型,包括特徵點的位置、連接關係和形狀特徵。
  2. 模型搜索:一旦模型被創建,它可以應用於圖像中搜索符合該模型的實例。搜索過程通常包括以下步驟:
    • 在圖像中的每個位置上應用模型,計算模型與圖像的匹配程度。
    • 根據匹配程度選擇最佳的實例,通常使用分數或相似性度量。
    • 如果模型具有變形能力,可以應對目標物體的旋轉、縮放和畸變等變化。


實驗一

我們故意將這張影像上幾處位置,故以把黑色的區域塗白。
看看是否可以將差異處檢測出來?
可以看到左像影像比對出來異常的位置。
這幾處位置是塗白的位置。

實驗二

可以看到QRCode被圖白的區域也有偵測出來。

AOI程式比對結果

  • 最左側下方是當初建檔的模型「局部可變形匹配的可變形模型
  • 中間是此次要比對的「樣品影像」。
  • 最右側是「比對結果」可以看到差異處

比對原始資料一

比對原始資料二

比對原始資料三

分類: AOI | 發佈留言

顏色檢測

客戶需求∶  我需要檢測車燈的打出來的光的顏色組成,如果超出範圍則發出警示。

檢測手法∶

  1. 車燈點亮,照射到前方白板。
  2. 相機拍攝白板,取得影像。
  3. 視覺檢測系統進行分析使用圖像分割技術(例如閾值處理、分水嶺算法、區域增長等)來檢測影像中光亮處的影像。
  4. 顏色檢測: 分析影像的色相分佈,與Golden樣本的色相分佈,使用歸一相似度”(Normalized Similarity)來比對,當相似度超出設定範圍時,則發出警報。

色相直方圖分佈∶

 影像的色相直方圖分佈是描述影像中不同色相值的統計分佈情況。在色相直方圖中,色相值通常被劃分成若干個區間,例如0到180度之間的若干個小區間。然後,統計圖像中每個區間內像素的數量或者像素的比例,形成一個直方圖。

色相直方圖可以反映出圖像中各個色相的分布情況,即哪些色相在圖像中佔據主導地位,哪些色相較少出現。通過觀察色相直方圖,可以直觀地了解圖像的整體顏色分布情況,以及圖像中具體顏色的特徵。

通常,色相直方圖是基於圖像的色彩空間進行計算的,例如RGB色彩空間或者HSV色彩空間。在HSV色彩空間中,色相(Hue)表示顏色的種類或者色調,因此色相直方圖反映了圖像中不同種類顏色的分布情況。

通過比較不同圖像的色相直方圖分佈,我們可以評估它們之間的顏色相似度或者差異度,從而用於圖像檢索、圖像分類、圖像匹配等領域。

歸一相似度∶

“歸一相似度”(Normalized Similarity)指的是將相似度值歸一化到特定的範圍內,通常是0到1之間。這樣做的目的是使得相似度值更易於理解和比較,不受圖像尺寸、亮度等因素的影響。

在計算兩個直方圖的相似度時,我們可以得到一個原始的相似度值,表示它們之間的相似程度。但是這個值的範圍通常不是固定的,並且可能受到直方圖的大小、圖像的亮度等因素的影響。因此,將這個相似度值歸一化到0到1之間可以消除這些影響,使得相似度更具有可比性。

在歸一化相似度中,常見的做法是使用以下公式:

歸一化相似度=原始相似度直方圖1的總和+直方圖2的總和−原始相似度

歸一化相似度=

直方圖1的總和+直方圖2的總和−原始相似度

原始相似度

這樣得到的歸一化相似度值會保證在0到1之間,其中1表示完全相同,0表示完全不同。

合格
相似度:0.987
偏紅
相似度:0.552
偏黃
相似度:0.231
偏綠
相似度:0.318
偏藍
相似度:0.239
分類: AOI | 標籤: | 發佈留言

車燈光型檢測

右駕光型量測規範示意

左駕光型量測規範示意

車燈配光室

AOI 輔助車燈燈型量測架設示意圖

使用 High Dynamic Range (HDR) Imaging 技術可以有效解決影像過曝所造成的影像失真問題,並得到一個明暗對比清晰的影像。這可以通過以下步驟實現:

  1. 拍攝多張照片:使用不同的曝光時間來拍攝同一場景的多張照片。這些照片包括適度曝光、過曝和曝光不足的版本。
  2. 合併照片:將這些多張照片合併成一張 HDR 圖像。這可以通過將照片的亮度信息合併起來,從而獲得一張具有更廣動態範圍的 HDR 圖像。
  3. 色調映射(Tone Mapping):對 HDR 圖像進行色調映射,將其轉換為顯示設備(如顯示器或打印機)可以顯示的標準動態範圍圖像。這一步旨在保留 HDR 圖像的細節和對比度,同時使其在一般顯示設備上呈現出自然的外觀。

AOI 視覺分析

透過HDR技術取得 Tonemap HDR image 及相機反應函數

得到的Tonemap HDR Image, 用於光形的分析,可以得到下列數值

  • 中心點
  • 光型輪廓
  • 光型旋轉矩型 (角度)
  • 光型與Golden的差異處
  • 透過相機反應函式可以亮度值
Golden 光型
中心點、輪廓、角度亮度分佈
測試品  光型
中心點、輪廓、角度亮度分佈
光型差異
分類: AOI | 標籤: | 發佈留言

Numpy Peak To Peak

NumPy 函式 ptp() 計算陣列沿指定軸的峰值之差(即峰-峰值)。

這是它的功能:

  • 峰-峰值:它計算沿指定軸的最大值和最小值之間的差值。

以下是語法:

numpy.ptp(a, axis=None, out=None, keepdims=<no value>)
  • a:輸入陣列。
  • axis:沿其查找峰值的軸。默認情況下,它會將陣列展平。
  • out:可選的輸出陣列,用於放置結果。
  • keepdims:如果設置為 True,則保留大小為一的減少的維度。

例如:

import numpy as np 
# 創建一個陣列 x = np.array([[4, 9, 2, 10], [6, 9, 7, 12]]) 

# x.shape is (2, 4)

# 沿著軸 0 計算峰-峰值 

result = np.ptp(x, axis=0) 

#x[0] => [4, 9, 2, 10]
#x[1] => [6, 9, 7, 12]

#[6-4, 9-9, 7-2, 12-10]

print(result)  

# 輸出: [2 0 5 2]

在這個例子中,np.ptp(x, axis=0) 計算了沿著軸 0(列)的峰-峰值,結果是一個陣列 [2, 0, 5, 2],其中每個值代表相應列的峰-峰值。

import numpy as np 
# 創建一個陣列 x = np.array([[4, 9, 2, 10], [6, 9, 7, 12]]) 

# x.shape is (2, 4)

# 沿著軸 1 計算峰-峰值 

result = np.ptp(x, axis=1) 

#x[:, 0] => [4, 6]
#x[:, 1] => [9, 9]
#x[:, 2] => [2, 7]
#x[:, 3] => [10, 12]

#[10 - 2, 12 - 6]
print(result)  

# 輸出: [8, 6]

在這個例子中,np.ptp(x, axis=1) 計算了沿著軸 1(行)的峰-峰值,結果是一個陣列 [8, 6],其中每個值代表相應列的峰-峰值

分類: 未分類 | 標籤: | 發佈留言

numpy.ndarray

  • ndarray.size
  • ndarray.itemsize
  • ndarray.nbytes
  • ndarray.resize
  • ndarray.T
  • ndarray.ndim
  • ndarray.real
  • ndarray.imag
  • ndarray.dtype
  • ndarray.flat
  • ndarray.tolist()
  • ndarray.astype
分類: 未分類 | 發佈留言

Numpy Spliting

水平分割

  • numpy.hsplit(a, 3)
  • numpy.split(a, 3, axis=1)

垂直分割

  • numpy.vsplit(a, 3)
  • numpy.split(a, 3, axis=0)

深度分割

  • numpy.dsplit(a, 3)
分類: 未分類 | 發佈留言

Numpy Stacking

水平、橫向合併

  • numpy.hstack((a, b))
  • numpy.concatenate((a, b), axis=1)
  • numpy.column_stack((a, b))

垂直、直立合併

  • numpy.vstack((a, b))
  • numpy.concatenate((a, b), axis=0)
  • numpy.row_stack((a, b))

深度合併

  • numpy.dstack((a, b))
import cv2
import numpy as np
import matplotlib.pyplot as plt

# Load the two sample images
image1 = cv2.imread('space/1.jpg')
image2 = cv2.imread('space/2.jpg')

# Convert images to grayscale (optional)
image1_gray = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
image2_gray = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)

# Perform image stitching (depth-wise stacking)
stitched_image = np.dstack((image1_gray, image2_gray))

# Plot the original images
plt.figure(figsize=(12, 6))

plt.subplot(2, 2, 1)
plt.imshow(cv2.cvtColor(image1, cv2.COLOR_BGR2RGB))
plt.title('Image 1')

plt.subplot(2, 2, 2)
plt.imshow(cv2.cvtColor(image2, cv2.COLOR_BGR2RGB))
plt.title('Image 2')

# Plot one channel of the stitched image (for visualization)
plt.subplot(2, 2, 3)
plt.imshow(stitched_image[:, :, 0], cmap='gray')  # Display the first channel
plt.title('Stitched Image (Channel 0)')

# Plot one channel of the stitched image (for visualization)
plt.subplot(2, 2, 4)
plt.imshow(stitched_image[:, :, 1], cmap='gray')  # Display the first channel
plt.title('Stitched Image (Channel 1)')

plt.tight_layout()
plt.show()
分類: 未分類 | 標籤: | 發佈留言

線性回歸(Linear Regression)

首先,使用make_regression函數生成了一些合成數據,然後將數據分成訓練集和測試集。接著創建了LinearRegression模型的實例,並使用訓練集對模型進行訓練。訓練完成後,打印出了模型的係數和對新數據的預測結果。最後,通過可視化將訓練集和測試集的散點圖以及線性回歸的平面呈現出來。

import numpy as np
import matplotlib.pyplot as mpl
from mpl_toolkits.mplot3d import Axes3D
from sklearn import linear_model
from sklearn.datasets import make_regression

# Generating synthetic data for training and testing
X, y = make_regression(n_samples=100, n_features=2, n_informative=1, random_state=0, noise=50)

# X and y are values for 3D space. We first need to train
# the machine, so we split X and y into X_train, X_test,
# y_train, and y_test. The *_train data will be given to the 
# model to train it.
X_train, X_test = X[:80], X[-20:]
y_train, y_test = y[:80], y[-20:]

# Creating instance of model
regr = linear_model.LinearRegression()

# Training the model 
regr.fit(X_train, y_train)

# Printing the coefficients 
print(regr.coef_)
# [-10.25691752 90.5463984 ]
# Predicting y-value based on training 
X1 = np.array([1.2, 4])
print(regr.predict([X1]))
# 350.860363861
# With the *_test data we can see how the result matches 
# the data the model was trained with.
# It should be a good match as the *_train and *_test
# data come from the same sample. Output: 1 is perfect
# prediction and anything lower is worse. 
print(regr.score(X_test, y_test))
# 0.949827492261

fig = mpl.figure(figsize=(8, 5))
ax = fig.add_subplot(111, projection='3d') 
ax.view_init(elev=20, azim=0)
# ax = Axes3D(fig)
# Data
ax.scatter(X_train[:,0], X_train[:,1], y_train, facecolor='#00CC00') 
ax.scatter(X_test[:,0], X_test[:,1], y_test, facecolor='#FF7800')

# Function with coefficient variables
coef = regr.coef_
line = lambda x1, x2: coef[0] * x1 + coef[1] * x2
grid_x1, grid_x2 = np.mgrid[-2:2:10j, -2:2:10j] 
ax.plot_surface(grid_x1, grid_x2, line(grid_x1, grid_x2), alpha=0.1, color='k') 
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
ax.zaxis.set_visible(False)
分類: AI, AOI | 標籤: , | 發佈留言

密度聚類算法(DBSCAN)

DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一種密度聚類算法,用於將數據點劃分為多個集群,同時可以識別和排除噪音點。該算法基於以下概念:

  1. 核心點(Core Points):對於給定的半徑 $\varepsilon$ (epsilon)內至少包含 $min_samples$ 個數據點的點被視為核心點。
  2. 邊界點(Border Points):如果一個點不是核心點,但位於某個核心點的 $\varepsilon$ 鄰域內,則它被視為邊界點。
  3. 噪音點(Noise Points):不是核心點也不是邊界點的數據點被視為噪音點。

DBSCAN算法運行步驟如下:

  1. 選擇一個未訪問的數據點作為起始點,檢查其 $\varepsilon$ 鄰域內的點數量:
    • 如果該點是核心點,則將其與其 $\varepsilon$ 鄰域內的所有點標記為同一個集群。
    • 如果該點是邊界點,則將其標記為集群的一部分。
  2. 對於已訪問的核心點,擴展集群並標記所有可達的點。
  3. 重複步驟1和步驟2,直到所有點都被訪問過。

DBSCAN的主要優勢是:

  • 能夠在集群之間具有不同的形狀和大小。
  • 能夠識別和排除噪音點。
  • 不需要事先指定要劃分的集群數量。

總的來說,DBSCAN是一種強大的聚類算法,特別適用於處理具有不同密度和形狀的數據集。

import numpy as np
import matplotlib.pyplot as mpl 
from scipy.spatial import distance 
from sklearn.cluster import DBSCAN
# Creating data
c1 = np.random.randn(100, 2) + 5 
c2 = np.random.randn(50, 2)
# Creating a uniformly distributed background
u1 = np.random.uniform(low=-10, high=10, size=100) 
u2 = np.random.uniform(low=-10, high=10, size=100) 
c3 = np.column_stack([u1, u2])
# Pooling all the data into one 150 x 2 array 
data = np.vstack([c1, c2, c3])

# Calculating the cluster with DBSCAN function.
# db.labels_ is an array with identifiers to the 
# different clusters in the data.
#db = DBSCAN().fit(data, eps=0.95, min_samples=10) 
db = DBSCAN().fit(data) 
labels = db.labels_
# Retrieving coordinates for points in each
# identified core. There are two clusters
# denoted as 0 and 1 and the noise is denoted 
# as -1. Here we split the data based on which 
# component they belong to.
dbc1 = data[labels == 0]
dbc2 = data[labels == 1]
noise = data[labels == -1]
# Setting up plot details 
x1, x2 = -12, 12
y1, y2 = -12, 12
fig = mpl.figure() 
fig.subplots_adjust(hspace=0.1, wspace=0.1)
ax1 = fig.add_subplot(121, aspect='equal') 
ax1.scatter(c1[:,0], c1[:,1], lw=0.5, color='#00CC00') 
ax1.scatter(c2[:,0], c2[:,1], lw=0.5, color='#028E9B') 
ax1.scatter(c3[:,0], c3[:,1], lw=0.5, color='#FF7800') 
ax1.xaxis.set_visible(False) 
ax1.yaxis.set_visible(False)
ax1.set_xlim(x1, x2)
ax1.set_ylim(y1, y2)
ax1.text(-11, 10, 'Original')
ax2 = fig.add_subplot(122, aspect='equal') 
ax2.scatter(dbc1[:,0], dbc1[:,1], lw=0.5, color='#00CC00') 
ax2.scatter(dbc2[:,0], dbc2[:,1], lw=0.5, color='#028E9B') 
ax2.scatter(noise[:,0], noise[:,1], lw=0.5, color='#FF7800') 
ax2.xaxis.set_visible(False)
ax2.yaxis.set_visible(False)
ax2.set_xlim(x1, x2)
ax2.set_ylim(y1, y2)
ax2.text(-11, 10, 'DBSCAN identified')
分類: AI, AOI | 標籤: , , | 發佈留言

自適應或動態閾值化(Dynamic Threshold)

在影像科學中的一個常見應用是將圖像組件從彼此分割開來,這稱為閾值化。傳統的閾值化技術在圖像的背景是平坦的情況下效果很好。不幸的是,這種情況並不常見;相反,圖像中的背景在視覺上會在整個圖像中發生變化。因此,人們開發了自適應閾值化技術,我們可以很容易地在 scikit-image 中使用它們。

import numpy as np
import matplotlib.pyplot as mpl 
import scipy.ndimage as ndimage 
import skimage.filters as skif
import matplotlib.pyplot as plt
# Generating data points with a non-uniform background
x = np.random.uniform(low=0, high=100, size=20).astype(int) 
y = np.random.uniform(low=0, high=100, size=20).astype(int)
# Creating image with non-uniform background 
func = lambda x, y: x**2 + y**2
grid_x, grid_y = np.mgrid[-1:1:100j, -2:2:100j] 
bkg = func(grid_x, grid_y)
bkg = bkg / np.max(bkg)
# Creating points
clean = np.zeros((100,100))
clean[(x,y)] += 5
clean = ndimage.gaussian_filter(clean, 3) 
clean = clean / np.max(clean)
# Combining both the non-uniform background 
# and points
fimg = bkg + clean
fimg = fimg / np.max(fimg)

# Defining minimum neighboring size of objects 
block_size = 3
# Adaptive threshold function which returns image
# map of structures that are different relative to
# background
adaptive_cut = fimg > skif.threshold_local(fimg, block_size, 'gaussian')

# Global threshold
global_thresh = skif.threshold_otsu(fimg) 
global_cut = fimg > global_thresh

# Creating figure to highlight difference between 
# adaptive and global threshold methods
fig = mpl.figure(figsize=(8, 4)) 
fig.subplots_adjust(hspace=0.05, wspace=0.05)

ax1 = fig.add_subplot(131) 
ax1.imshow(fimg, cmap='gray') 
ax1.xaxis.set_visible(False) 
ax1.yaxis.set_visible(False)

ax2 = fig.add_subplot(132) 
ax2.imshow(global_cut, cmap='gray') 
ax2.xaxis.set_visible(False) 
ax2.yaxis.set_visible(False)

ax3 = fig.add_subplot(133) 
ax3.imshow(adaptive_cut, cmap='gray') 
ax3.xaxis.set_visible(False) 
ax3.yaxis.set_visible(False)
分類: AOI | 標籤: | 發佈留言