Dans cet article, nous effectuerons l'analyse suivante à l'aide d'une image cristalline colloïdale bidimensionnelle.
J'ai utilisé les images colloïdales suivantes: miam:
Auteur Zephyris [CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0) ou GFDL (http://www.gnu.org/copyleft/fdl.html)], [Wikimedia Depuis Commons (lien)
C'est comme suit.
import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from scipy import ndimage as ndi
from sklearn.neighbors import NearestNeighbors
Un filtre maximum est utilisé pour détecter la position colloïdale. Cette méthode fait à peu près la même chose que Find Maxima d'ImageJ (https://imagej.nih.gov/ij/docs/menus/process.html#find-maxima).
Tout d'abord, chargez l'image comme indiqué ci-dessous et convertissez-la en échelle de gris. Obtenez également la taille de l'image.
#Chargement d'image
img_raw = cv2.imread('ColloidCrystal_10xBrightField_GlassInWater.jpg', 1)
#Échelle de gris
img = cv2.cvtColor(img_raw, cv2.COLOR_BGR2GRAY)
#Hauteur de l'image,Obtenir la largeur
h, w = img.shape[:2]
Comme son nom l'indique, le filtre de valeur maximale est un filtre qui fait référence à la luminosité maximale dans une certaine plage autour du pixel d'intérêt. En utilisant cela, il est possible de détecter la valeur maximale locale de l'image, c'est-à-dire la position des particules colloïdales.
Appliquons le filtre avec la taille de la plage d'attention (noyau) définie sur 9x9. (Aucun bruit n'est éliminé par lissage avant application)
#Brouiller(Lissage)Élimination du bruit par
img = cv2.blur(img,(3,3))
#Appliquer le filtre maximum
img_max = ndi.maximum_filter(img, size=9, mode='constant')
Voici une comparaison des images avant et après l'application du filtre.
Dans ces deux images, la position où la valeur de luminosité est la même ** est la valeur maximale locale, c'est-à-dire la position de la particule. (Référence) Cela peut être facilement déterminé en utilisant numpy.where.
#Extraction de la valeur maximale locale
pts_list = np.where(img == img_max)
pts_list = np.array(pts_list)
#Tracer sur l'image d'origine
fig = plt.figure(dpi=150,facecolor='white')
plt.gray()
plt.imshow(img)
plt.scatter(pts_list[1],pts_list[0],s=3,c='red',alpha=0.7)
plt.xlim(0,300)
plt.ylim(0,300)
Les points ainsi obtenus sont affichés au-dessus de l'image d'origine et indiqués ci-dessous.
Vous pouvez détecter approximativement la position des particules. Cependant, certaines particules ont été détectées deux fois. Afin de supprimer cela, nous allons résumer les points qui sont proches les uns des autres avec leur centre de gravité comme représentant.
Utilisez sklearn.neighbors.NearestNeighbors pour calculer la distance entre les points.
nbrs = NearestNeighbors(n_neighbors=10, algorithm='ball_tree').fit(pts_list.T)
distances, indices = nbrs.kneighbors(pts_list.T)
Nearest Neighbor renvoie la distance au point de proximité et l'index en spécifiant le nombre de points de proximité.
Cette fois, nous calculerons que les points existant à moins de 3 pixels sont dérivés de la même particule.
#Seuil de proximité
th = 3
#Calculez le centre de gravité avec un point de proximité en dessous du seuil.
center_list = []
for d,i in zip(distances,indices):
i = i[np.where(d < 3)]
pts = pts_list.T[i]
center = np.mean(pts,axis=0)
center_list.append(center)
center_list = np.array(center_list).astype(np.int32)
#Supprimer les doublons
center_list = np.unique(center_list,axis=0)
center_list = center_list.T
Les points ainsi obtenus sont présentés ci-dessous de la même manière. Les points qui se chevauchent dans une particule sont résumés. Ceci termine la détection des particules: thumbsup :: thumbsup:
Ensuite, nous dériverons la fonction de distribution radiale (RDF). RDF indique la probabilité qu'une autre particule existe par rapport à la distance d'une particule, et est souvent utilisé pour décrire la structure périodique d'un cristal.
Voir ci-dessous pour la dérivation de RDF et sa méthode de calcul. Calcul de la fonction de distribution radiale - Page de Koishi
Utilisez sklearn.neighbors.NearestNeighbors utilisé précédemment pour calculer les atomes de proximité.
#Calculez la position et la distance des atomes proches jusqu'à 40
nbrs = NearestNeighbors(n_neighbors=40, algorithm='ball_tree').fit(center_list.T)
distances, indices = nbrs.kneighbors(center_list.T)
#Calculez la distance par rapport aux autres particules existant à moins de 50 pixels et créez un histogramme
dist_list = []
for d,i in zip(distances,indices):
d = d[np.where(d < 50)][1:]
dist_list.extend(d)
dist_list = np.array(dist_list)
dist_hist = np.histogram(dist_list,range=(0,50),bins=50)
#Calcul RDF
rdf = dist_hist[0]/(4*np.pi*np.power(dist_hist[1][1:],2))
Tout va bien. Cette fois, j'ai calculé le RDF dans une plage allant jusqu'à 50 pixels. (Strictement différent car il n'est pas divisé par la densité à la fin, mais cette fois c'est OK si la valeur de crête est connue)
Le résultat est présenté ci-dessous.
Vous pouvez voir les sommets jusqu'à la 1ère, 2ème et 3ème proximité. Les cristaux colloïdaux sont cette fois densément tassés, mais en raison de certains défauts, les pics sont fendus ou larges.
Pour le moment, il s'avère que la distance à la première particule de proximité est d'environ 10 à 18 pixels.
Comptons maintenant le nombre de ces premières particules de proximité.
Lorsque le colloïde sphérique est densément compacté en deux dimensions, le nombre de coordination avec la première particule de proximité est de ** 6 **. La zone en dessous est celle où les colloïdes ne sont pas correctement disposés. En d'autres termes, le traçage du nombre de premières particules de proximité peut illustrer des défauts dans les cristaux colloïdaux.
Faisons-le: v :: v: J'utilise également sklearn.neighbors.NearestNeighbors. Le seuil pour le considérer comme le premier atome de proximité est de 16 px.
#Compter le premier atome de proximité
co_list = []
for d,i in zip(distances,indices):
d = d[np.where(d < 16)][1:]
co_list.append(d.shape[0])
De plus, la figure ci-dessous montre le nombre de premières particules de proximité dans un histogramme.
Le nombre de particules les plus proches est généralement réparti entre 3 et 7. Pour l'instant, traçons dans l'intervalle (3,6).
fig = plt.figure(dpi=150,facecolor='white')
plt.imshow(img)
plt.scatter(center_list[1],center_list[0],s=3,c=co_list,cmap='rainbow',alpha=0.7)
plt.colorbar()
plt.clim(3,6)
plt.xlim(0,1000)
plt.ylim(0,1000)
L'image d'origine est affichée à gauche et le tracé de chaque particule codée par couleur par coordination est affiché à droite. Partie désordonnée = L'emplacement du défaut peut être illustré: heart_eyes :: heart_eyes:
Enfin, voici un tracé sur toute l'image d'origine: point_down:
Enfin, visualisons les grains polycristallins de cristaux colloïdaux. Plus précisément, la cartographie bidimensionnelle des grains cristallins (voir la figure ci-dessous) telle qu'obtenue par EBSD (diffraction de rétrodiffusion par faisceau d'électrons) est également effectuée pour les cristaux colloïdaux.
(Source: https://www.ube-ind.co.jp/usal/documents/m1303_550.htm)
Une telle analyse est également réalisée dans un papier sur des cristaux colloïdaux.Par exemple, sur la figure 1 de l'article suivant, le vecteur formé par la particule d'intérêt ⇒ la première particule de proximité est classée selon l'angle formé par l'axe X, et le grain cristallin est analysé. Nous faisons de la visualisation.
Référence: [Ramananarivo, S., Ducrot, E. & Palacci, J. Recuit à activité contrôlée de monocouches colloïdales. Nat Commun 10, 3380 (2019).](Https://doi.org/10.1038/s41467-019- 11362-y)
Visualisons-le de la même manière!
theta_list = []
for d,i in zip(distances,indices):
#Extraire les particules de proximité en s'excluant
idx = i[np.where(d < 16)][1:]
cnts = center_list.T[idx]
#Obtenez la position de la particule avec la plus grande valeur X
top = cnts[np.argmin(cnts[:,1])]
#Obtenez la position de la particule d'intérêt
center = center_list.T[i[0]]
#Calculez l'angle avec l'axe X
axis = np.array([0,center[1]])
u = top - center
v = axis - center
c = np.inner(u, v) / (np.linalg.norm(u) * np.linalg.norm(v) + 1e-99)
a = np.rad2deg(np.arccos(np.clip(c, -1.0, 1.0))) - 90
theta_list.append(a)
L'histogramme des angles ainsi obtenu est illustré ci-dessous.
L'angle semble être dans la plage de ± 30 degrés. Tracons!
L'image d'origine est affichée à gauche et l'angle du premier atome de proximité est codé par couleur et tracé sur la droite. Enfin, faisons ce codage couleur pour l'image entière! Vous pouvez clairement voir le grain du cristal colloïdal: yum :: yum: Je pense que c'est plus bruyant que la cartographie des journaux auxquels j'ai fait référence, mais c'est assez bon pour un travail de défaite.
Faites appel à vos rivaux avec des personnages colorés!
Récemment, j'ai reçu une demande d'analyse d'image d'une personne qui fait des recherches sur les colloïdes, et je faisais diverses analyses tout en restant à la maison. Pour le moment, je viens de terminer un paragraphe, j'ai donc l'impression d'avoir résumé mon savoir-faire à partir d'images de cristaux colloïdaux qui roulaient sur le net.
En fait, les images obtenues lors des expériences sont souvent bruyantes et peuvent nécessiter un filtrage et un débruitage appropriés. À ce sujet, c'est essentiellement un jeu qui sort, ou une partie qui nécessite des essais et des erreurs. Je voudrais bientôt résumer les méthodes utiles. Ce sera un article compliqué.
Eh bien ~.