Résumé de l'utilisation d'opencv de python

En tant que l'un des langages les plus faciles à utiliser, Python possède un grand nombre de bibliothèques tierces.L'existence de ces bibliothèques tierces permet à de nombreuses personnes de se concentrer sur la logique métier et la logique mathématique tout en ignorant les opérations de code fastidieuses. -Party Library est l'un d'entre eux.

1. Installation et utilisation simple de bibliothèques tierces

Installer

Une simple installation de pip suffit.L'utilisation de la bibliothèque opencv implique souvent quelques opérations matricielles, donc numpy peut être considéré comme une famille avec elle.

pip install opencv-python

Après l'installation, vous pouvez simplement ouvrir l'image, ouvrir la vidéo, faisons une expérience simple :

lire des images

import cv2

# 读取图像,第一种是正常读取,第二种是读取灰度图像
img = cv2.imread(r"D:\img\among.png")
gray = cv2.imread(r"D:\img\among.png", 0)
# 显示图像
cv2.imshow("colorful", img)
cv2.imshow("gray", gray)
# 不再等待键盘输入事件,直接显示
cv2.waitKey(0)
# 关闭所有显示窗口
cv2.destroyAllWindows()

L'effet d'affichage est le suivant :
insérez la description de l'image ici
lisez la vidéo et lisez-la

import cv2

# 读取视频
video = cv2.VideoCapture('badapple_color.mp4')
# 获取视频对象的帧数
fps = video.get(cv2.CAP_PROP_FPS)
# 设定循环条件
while(video.isOpened()):
    _, frame = video.read()
    cv2.imshow("video", frame)
    # 设置退出条件是输入'q'
    if cv2.waitKey(int(fps)) in [ord('q'), 27]:
            break
cv2.destroyAllWindows()
video.release()

Remarque : la lecture ici n'a pas la barre de progression et l'audio comme un lecteur normal, car voici chaque image de la vidéo lue, puis elle est lue en boucle, et l'audio n'est pas lu, et la condition de sortie est définie entrer q.
insérez la description de l'image ici
Obtenez l'enregistrement de la caméra et enregistrez la vidéo

import cv2

video = cv2.VideoCapture(0)
while(True):
    # 获取一帧
    _, frame = video.read()
    # 将这帧转换为灰度图
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    cv2.imshow('frame', gray)
    if cv2.waitKey(1) == ord('q'):
        break

video.release()

Ensuite, il sortira en continu chaque image que la caméra reçoit, car ce qui est lu est en niveaux de gris, ici aussi en niveaux de gris, vous pouvez le laisser sortir normalement sans modification, c'est-à-dire commenter l'instruction de conversion de cvtColor , au fait, changez le imshow renvoie l'objet de sortie à l'image d'origine (l'image de lecture) et vous pouvez obtenir la carte des couleurs.
insérez la description de l'image ici
insérez la description de l'image ici
Faites une petite modification pour qu'il enregistre la vidéo :

import cv2

video = cv2.VideoCapture(0)

# 定义编码方式并创建VideoWriter对象
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
outfile = cv2.VideoWriter('res.mp4', fourcc, 25., (640, 480))

while(video.isOpened()):
    flag, frame = video.read()

    if flag:
        outfile.write(frame)  # 写入文件
        cv2.imshow('frame', frame)
        if cv2.waitKey(1) == ord('q'):
            break
    else:
        break

video.release()

comme suit:
insérez la description de l'image ici

2. Fondation de l'image

L'image dans l'ordinateur est composée de petits carrés colorés, ces petits carrés sont les unités de traitement de base appelées pixels. Sa taille dépend de la résolution de l'ordinateur, plus la résolution est élevée, plus les pixels sont petits. Une image binaire simple, dont les valeurs de pixel ne sont que 0 et 1, est utilisée pour identifier deux couleurs de noir et blanc; l'image en niveaux de gris supplémentaire ici consiste à affiner le noir et blanc pour rendre l'image beaucoup plus vive, c'est-à-dire pour représenter La valeur du noir et blanc est stockée de 0 à 255, 0 est du noir pur, 1 est du blanc pur et le dégradé noir et blanc au milieu est plus délicat ; de plus, il s'agit d'une image couleur et la composition des couleurs est essentiellement composé de trois couleurs primaires dans des proportions différentes, et l'image couleur a trois valeurs pour représenter les valeurs des trois couleurs primaires, c'est-à-dire le pixel d'une image couleur, son color est composé de trois valeurs, ce mode est aussi appelé espace colorimétrique RVB. Par exemple, [0, 0, 0] représente le noir pur, [255, 255, 255] représente le blanc pur et [255, 0, 0], [0, 255, 0] et [0, 0, 255] représentent rouge respectivement Les trois couleurs primaires du vert et du bleu, de sorte que l'espace colorimétrique RVB est également appelé trois canaux.Dans opencv, l'ordre des trois canaux est l'ordre inverse de BGR. Une image couleur est constituée d'une matrice composée de trois canaux, c'est pourquoi Numpy est souvent introduit pour la coopération lors du traitement d'images couleur. Le traitement d'images est aussi une question de mathématiques.


Exemple simple :

import cv2
import numpy as np

# 黑白图
b = np.zeros((100, 100), dtype=np.uint8)
w = np.zeors((100, 100), dtype=np.uint8)
w[:100, :100] = 255
print(b, w, sep="\n\n")

cv2.imshow("black", b)
cv2.imgshow("white", w)
cv2.waitKey()

# 三个三原色图片
r = np.zeros((300, 300, 3),dtype=np.uint8)
g = np.zeros((300, 300, 3),dtype=np.uint8)
b = np.zeros((300, 300, 3),dtype=np.uint8)
r[:,:, 2] = 255
g[:,:, 1] = 255
b[:,:, 0] = 255

cv2.imshow("red", r)
cv2.imshow("green", g)
cv2.imshow("blue", b)
cv2.waitKey()

# 包含三原色的图片
img = np.zeros((300, 300, 3), dtype=np.uint8)
img[:, 0:100, 2] = 255
img[:, 100:200, 1] = 255
img[:, 200:300, 0] = 255
cv2.imshow("RGB", img)

# 红橙黄绿蓝靛紫
img = np.zeros((300, 700, 3), dtype=np.uint8)
# 红
img[:,0:100,2] = 255
# 橙
img[:,100:200,2] = 255
img[:,100:200,1] = 97
# 黄
img[:,200:300,1] = 255
img[:,200:300,2] = 255
# 绿
img[:,300:400,1] = 255
# 蓝
img[:,400:500,0] = 255
# 靛
img[:,500:600,0] = 84
img[:,500:600,1] = 46
img[:,500:600,2] = 8
# 紫
img[:,600:700,0] = 240
img[:,600:700,1] = 32
img[:,600:700,2] = 160
# 输出
cv2.imshow("seven", img)
cv2.waitKey()

La sortie ne s'affiche pas, c'est tout. En fait, si vous souhaitez créer une table de comparaison de couleurs, vous pouvez également itérer un canal par un canal, puis itérer la séquence entière et la sortir, de sorte qu'il y ait une table de couleurs, mais vous devez toujours vous référer au vrai nom si vous voulez vous préparer à la valeur.

graphique aléatoire

L'ensemble de l'image en niveaux de gris aléatoire est l'état d'absence de signal TV dans le passé.

import cv2
import numpy as np

img = np.random.randint(0, 256, size=[300, 300], dtype=np.uint8)
cv2.imshow("老花", img)
cv2.waitKey()

img = np.random.randint(0, 256, size=[300, 300], dtype=np.uint8)
cv2.imshow("彩色老花", img)
cv2.waitKey()

insérez la description de l'image ici| insérez la description de l'image ici
Il a été décrit ci-dessus que la carte de couleurs RVB a trois canaux, et opencv fournit l'implémentation de la division des canaux

import cv2
import numpy as np

img = cv2.imread(r"D:\img\among.png")
# b, g, r = cv2.split(img) 等同
cv2.imshow("0", img[:,:,0])
cv2.imshow("1", img[:,:,1])
cv2.imshow("2", img[:,:,2])

# 同样的拆分功能
b, g, r = cv2.split(img)
# 合并成原图
img_mer = cv2.merge([b, g, r])
cv2.waitKey()

Effet:
insérez la description de l'image ici

trois valeurs d'attribut

  • shape, img.shape, indique la longueur, la largeur et la profondeur de la séquence img,
  • size , img.size, indique le nombre de pixels, ligne x colonne x canal
  • dtype, type de données d'image

3. Espace colorimétrique et conversion

L'espace colorimétrique, le mode d'expression de la couleur, l'espace colorimétrique commun est l'espace colorimétrique RVB, mais dans opencv, il s'agit du canal BGR inversé. En outre, il existe GRAY, qui est une image en niveaux de gris de huit bits, l'espace colorimétrique XYZ, l'espace colorimétrique YCrCb, l'espace colorimétrique HSV, l'espace colorimétrique HLS et l'espace colorimétrique Bayer. . . . . . Selon l'espace colorimétrique des différents besoins, il peut également être converti en cas de besoin.Voici pour en savoir plus sur leurs caractéristiques et leur conversion mutuelle.

Espace colorimétrique gris

Image en niveaux de gris 8 bits, correspondant à la plage de valeurs binaires 8 bits est 0-255, ce qui signifie simplement 256 niveaux de gris, 0 signifie noir pur, 255 signifie blanc pur et la valeur moyenne est le dégradé du noir pur au blanc pur , il est donc gris Dépenser. Dans opencv, l'espace colorimétrique RVB est transformé en un espace colorimétrique en niveaux de gris tel que GRIS, et sa formule de transformation est la suivante :

G ray = 0,299 ∗ R + 0,587 ∗ G + 0,114 ∗ B Gray = 0,299*R+0,587*G+0,114*BG r a y=0,299R+0,587g+0,114B

Il est relativement simple de convertir l'échelle de gris de Gray en espace colorimétrique RVB. La valeur des trois canaux RVB est directement la valeur de Gray, c'est-à-dire
$$R = Gray\G = Gray\B = Gray\$$

Concernant la conversion entre GREY et BGR, en fait, elle peut être réalisée lorsque opencv le lit.Je n'ai aucune idée de l'application pour le moment.Ici, c'est surtout pour jouer avec un exemple.

>>> import cv2
>>> import numpy as np
>>>
>>>
>>> mong = cv2.imread("among.png")
>>> gray = cv2.cvtColor(mong, cv2.COLOR_BGR2GRAY)
>>> cv2.imshow("source", mong)
>>> cv2.imshow("gray", gray)
>>> bgr_img = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
>>> cv2.imshow("change again", bgr_img)
>>> cv2.waitKey()
-1
>>>
>>>
>>> img = np.random.randint(0, 256, size=[2,3], dtype=np.uint8)
>>> res = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
>>> res_change = cv2.cvtColor(res, cv2.COLOR_BGR2GRAY)
>>> img
array([[ 48,  27, 228],
       [ 94, 144, 234]], dtype=uint8)
>>> res
array([[[ 48,  48,  48],
        [ 27,  27,  27],
        [228, 228, 228]],

       [[ 94,  94,  94],
        [144, 144, 144],
        [234, 234, 234]]], dtype=uint8)
>>> res_change
array([[ 48,  27, 228],
       [ 94, 144, 234]], dtype=uint8)
>>>

Joué, n'est pas revenu à la couleur d'origine.
insérez la description de l'image ici

Espace colorimétrique YCrCb

Pour le système visuel humain, parce que la perception des couleurs par les gens est inférieure à la perception de la luminosité de la lumière et que l'espace colorimétrique RVB se concentre sur la couleur, l'indicateur de luminosité est manquant, il existe donc un espace colorimétrique YCrCb. Dans cet espace colorimétrique, Y représente la luminosité de la source lumineuse, Cr représente la composante rouge et Cb représente la composante bleue. La formule de conversion pour convertir RVB en YCrCb est :
Y = 0,299 ∗ R + 0,587 ∗ G + 0,114 ∗ BC r = ( R − Y ) × 0,713 + delta C b = ( B − Y ) × 0,564 + delta Y = 0,299* R + 0.587*G + 0.114*B \\ Cr = (RY)\times0.713+delta\\Cb=(BY)\times0.564+deltaOui=0,299R+0,587g+0,114BC r=( RY )×0,713+d e lt aC b=( BY )×0,564+La valeur delta de d e lt a
a différentes valeurs correspondant à différentes images numériques :

valeur delta Chiffres de l'image
128 8 bits
32768 16 bits
0,5 image simple précision
À son tour, la formule de conversion de YCrCb en RVB est :

R = Y + 1,403 ⋅ ( C r - delta ) G = Y - 0,714 ⋅ ( cr - delta ) - 0,344 ⋅ ( C b - delta ) B = Y + 1,773 ⋅ ( C b - delta ) R=Y+1,403\ cdot(Cr-delta)\\G=Y-0.714\cdot(cr-delta)-0.344\cdot(Cb-delta)\\B=Y+1.773\cdot(Cb-delta)R=Oui+1.403( C rd e lt a )g=Oui0,714( crd e lt a )0,344( C bd e lt a )B=Oui+1.773( C bd e lt a )

Il est également appelé YUV, Y représente la luminosité, U et V représentent la chrominance, et il est meilleur que HSV dans la détection de la couleur de la peau.

Espace colorimétrique HSV

On dit qu'il s'agit d'un modèle de couleur pour la perception visuelle. Dans cet espace colorimétrique, il y a trois éléments : la teinte, la saturation et la luminosité. La teinte est la couleur de la lumière, la saturation est la profondeur de la couleur et la luminosité est la luminosité de lumière perçue par l'œil humain.

  • Hue H, les six couleurs de rouge, jaune, vert, bleu et rouge correspondent à un cercle de 360 ​​degrés (c'est pourquoi les gens qui créent ces concepts aiment rendre les gens déroutants) ;
  • Saturation S, un rapport, c'est-à-dire un nombre décimal, varie de 0 à 1, indiquant le rapport de la pureté maximale de la couleur à la couleur, la saturation est de 0 est le gris et le maximum est de 1 est la couleur elle-même ;
  • La luminosité V, la luminosité de la couleur, a également une plage de valeurs de [0, 1].

La formule de conversion RVB en HSV est la suivante :
V = max ( R , G , B ) luminosité : S = { V − min ( R , G , B ) , V ≠ 0 0 , autres teintes : H = { 60 ( G - B ) v - min ( R , G , B ) , V = R 120 + 60 ( B - G ) V - min ( R , G , B ) , V = G 240 + 60 ( R - G ) V - min ( R , G , B ) , V = B Teinte : H = { H + 360 , H < 0 H , autre V = max(R, G, B)\\luminosité : S= \begin{cases} V- min(R , G, B), &V\ne0\\ 0,&other \end{cases}\\hue : H= \begin{cases} \cfrac{60(GB)}{v-min(R, G, B) },&V=R\\ 120+\cfrac{60(BG)}{V-min(R, G, B)}, &V=G\\ 240+\cfrac{60(RG)}{V- min( R, G, B)}, &V=B \end{cas}\\Hue : H= \begin{cas} H+360,&H<0\\ H,&other\end{cas}V=max ( R , _G ,B )Luminosité : S={ Vmin ( R ,G ,B ) ,0 ,V=0AutreTeinte : H= vmin ( R ,G ,B )60 ( GB ),120+Vmin ( R ,G ,B )60 ( BG ),240+Vmin ( R ,G ,B )60 ( RG ),V=RV=gV=BTeinte : H={ H+360 ,H ,H<0Autre
Ce qui précède est la formule pour convertir RVB en HSV. C'est vraiment gênant. Connaître le principe est également pour la commodité du débogage. En fait, cela peut être fait par les fonctions internes d'opencv. Vous n'avez qu'à vous soucier de la signification de les trois éléments importants ci-dessus.

>>> rgb_b = np.ones((2, 3, 3), dtype=np.uint8)
>>> rgb_g = np.ones((2, 3, 3), dtype=np.uint8)
>>> rgb_r = np.ones((2, 3, 3), dtype=np.uint8)
>>> rgb_b[:, :, 0], rgb_g[:, :, 1], rgb_r[:, :, 2] = 255, 255, 255
>>> hsv_b = cv2.cvtColor(rgb_b, cv2.COLOR_BGR2HSV)
>>> hsv_g = cv2.cvtColor(rgb_g, cv2.COLOR_BGR2HSV)
>>> hsv_r = cv2.cvtColor(rgb_r, cv2.COLOR_BGR2HSV)
>>> rgb_b
array([[[255,   1,   1],
        [255,   1,   1],
        [255,   1,   1]],

       [[255,   1,   1],
        [255,   1,   1],
        [255,   1,   1]]], dtype=uint8)
>>> hsv_b
array([[[120, 254, 255],
        [120, 254, 255],
        [120, 254, 255]],

       [[120, 254, 255],
        [120, 254, 255],
        [120, 254, 255]]], dtype=uint8)
>>> rgb_g
array([[[  1, 255,   1],
        [  1, 255,   1],
        [  1, 255,   1]],

       [[  1, 255,   1],
        [  1, 255,   1],
        [  1, 255,   1]]], dtype=uint8)
>>> hsv_g
array([[[ 60, 254, 255],
        [ 60, 254, 255],
        [ 60, 254, 255]],

       [[ 60, 254, 255],
        [ 60, 254, 255],
        [ 60, 254, 255]]], dtype=uint8)
>>> rgb_r
array([[[  1,   1, 255],
        [  1,   1, 255],
        [  1,   1, 255]],

       [[  1,   1, 255],
        [  1,   1, 255],
        [  1,   1, 255]]], dtype=uint8)
>>> hsv_r
array([[[  0, 254, 255],
        [  0, 254, 255],
        [  0, 254, 255]],

       [[  0, 254, 255],
        [  0, 254, 255],
        [  0, 254, 255]]], dtype=uint8)
>>>

Espace colorimétrique HLS

Il est similaire à l'espace colorimétrique HSV, mais les trois éléments de l'espace colorimétrique HLS sont : la teinte H, la luminosité L et la saturation S. C'est ainsi qu'il est décrit dans le texte, alors quelle est la différence ? Des mots qui décrivent la luminosité ? Sans voix.

fonction de conversion de type

La conversion entre les différents espaces colorimétriques ci-dessus et l'espace colorimétrique RVB peut être réalisée dans la fonction de conversion de type fournie par opencv.

dst = cv2.cvtColor(src, code[, dstCn])

Pour différentes conversions de type, différentes valeurs de paramètre de code sont transmises et dstCn est le numéro de canal de l'image cible, qui est 0 par défaut, puis le numéro de canal sera automatiquement déterminé par l'image et le code d'origine.

valeur de code analyser
cv2.COLOR_BGR2RGB Convertir le type BGR en type RVB dans opencv
cv2.COLOR_RGB2BGR Convertir le type RVB en type BGR dans opencv
cv2.COLOR_BGR2GRAY BGR à GREY
cv2.COLOR_GRAY2BGR GREY à BGR
cv2.COLOR_BGR2XYZ BGR à XYZ
cv2.COLOR_XYZ2BGR XYZ à BGR
cv2.COLOR_BGR2YCrCb BGR à YCrCb
cv2.COLOR_YCrCb2BGR YCrCb à BGR
cv2.COLOR_BGR2HSV BGR à HSV
cv2.COLOR_HSV2BGR HSV à BGR
cv2.COLOR_BGR2HLS BGR à HLS
cv2.COLOR_BayerBG2BGR Anti-mosaïque, également le mode BG de Bayer

Dans les paramètres ci-dessus, il y a une conversion entre RVB et BGR. Dans opencv, l'ordre des canaux est généralement BGR, qui est inversé. Qu'en est-il de la conversion de RVB ? Selon les résultats de l'opération pratique, les valeurs des canaux B et R seront remplacées l'une par l'autre, c'est-à-dire que les deux canaux sont commutés, mais l'image de rendu opencv est toujours dans le même ordre de canal, donc si l'image s'affiche, la couleur change.

>>> import cv2
>>> import numpy as np
>>>
>>>
>>> img = np.random.randint(0, 256, size=(2, 3, 3), dtype=np.uint8)
>>> rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
>>> img
array([[[ 69, 184,  11],
        [193,   4, 194],
        [239, 139, 146]],

       [[188,  30,  44],
        [ 60, 145, 133],
        [ 46, 181, 139]]], dtype=uint8)
>>> rgb_img
array([[[ 11, 184,  69],
        [194,   4, 193],
        [146, 139, 239]],

       [[ 44,  30, 188],
        [133, 145,  60],
        [139, 181,  46]]], dtype=uint8)
>>> mong = cv2.imread("among.png")
>>> rgb_mong = cv2.cvtColor(mong, cv2.COLOR_BGR2RGB)
>>> cv2.imshow("source", mong)
>>> cv2.imshow("rgb_res", rgb_mong)
>>> cv2.waitKey()
-1

D'après les changements dans la matrice ci-dessus et les images ci-dessous, nous pouvons clairement voir les changements internes et les changements de macro. Même si les deux canaux sont remplacés, l'ordre des canaux de l'image par défaut d'opencv ne devrait pas changer, ou les règles internes du fonction imshow. Il s'agit toujours de l'ordre des canaux de bgr, ce qui rend la couleur de l'image différente.

insérez la description de l'image ici

Extraire une couleur spécifique

Pour un bloc de couleur spécifique dans l'image, lorsque nous en avons besoin, nous pouvons itérer l'image et produire une image qui ne contient que le bloc de couleur. Par exemple, la division des couleurs de Doraemon ci-dessus est très évidente, ce qui convient très bien à cet essai . De plus, la réalisation de cette idée dépend de la fonction inRange d'opencv.

dst = cv2.inRange(src, lowerb, upperb)

La fonction ci-dessus consiste à extraire la couleur de la zone [lowerb, upperb] dans l'image, mais il convient de noter que s'il s'agit d'une image en niveaux de gris, lowerb n'est qu'une valeur entière, mais s'il s'agit d'une image dans l'espace colorimétrique RVB , lowerb a besoin d'une matrice pour décrire la couleur, et upperb est le même que ci-dessus. Eh bien, mais la fonction spécifique au matériel d'exprimer la couleur avec trois canaux est vraiment un casse-tête pour moi. En HSV, il n'y a qu'une seule façon d'exprimer la couleur, ce qui est très attrayant pour les utilisateurs. D'accord, essayez ceci.

>>> import cv2
>>> import numpy as np
>>>
>>> mong = cv2.imread("among.png")
>>> mong_hsv = cv2.cvtColor(mong, cv2.COLOR_BGR2HSV)
>>> bmin, bmax = np.array((100, 43, 46)), np.array((125, 255, 255))
>>> mask = cv2.inRange(mong_hsv, bmin, bmax)
>>> blue = cv2.bitwise_and(mong, mong, mask=mask)
>>> ymin, ymax = np.array((26, 43, 46)), np.array((34, 255, 255))
>>> ymask = cv2.inRange(mong_hsv, ymin, ymax)
>>> rmin, rmax = np.array((0, 43, 46)), np.array((10, 255, 255))
>>> rmask = cv2.inRange(mong_hsv, rmin, rmax)
>>> yellow = cv2.bitwise_and(mong, mong, mask=ymask)
>>> red = cv2.bitwise_and(mong, mong, mask=rmask)
>>> cv2.imshow("source", mong)
>>> cv2.imshow("blue", blue)
>>> cv2.imshow("yellow", yellow)
>>> cv2.imshow("red", red)
>>> cv2.waitKey()
-1

insérez la description de l'image ici
Ce qui précède est une expérience basée sur le tableau de comparaison HSV recherché. Il se peut que les couleurs de l'image ne soient pas complètement conformes à la comparaison de couleurs régulière, donc ce que vous obtenez est également des morceaux de blocs de couleur cassés, ce qui est vraiment fatigant.
Cependant, l'extraction des filigranes m'a apporté des gains inattendus dans cette partie.

>>> import cv2
>>> import numpy as np
>>>
>>>
>>> watermark = img[850:, 580:]
>>> wm_abstract = cv2.inRange(watermark, (230, 230, 230), (255, 255, 255))
>>> cv2.imshow("source", img)
>>> cv2.imshow("watermark_part", watermark)
>>> cv2.imshow("watermark", wm_abstract)
>>> cv2.waitKey()
-1
>>> 

De cette façon, même si la partie du filigrane est extraite, pour ce genre de filigrane de couleur pure, l'inRange de RVB est plus utilisable.
insérez la description de l'image ici

Marquage et détection du teint de la peau

Conformément à la gamme de couleurs déterminée ci-dessus, marquez la gamme de couleurs de la couleur de la peau correspondant à la photo humaine, puis vous pouvez obtenir la zone correspondante, puis l'extraire. Le livre utilise HSV pour cela, mais il existe des informations selon lesquelles YCrCb est mieux pour cette scène. Je vois si vous pouvez essayer les deux.

Dans le livre, la teinte et la saturation de la couleur de la peau sont définies entre [5, 170] et [25, 166]. Je ne sais pas si c'est pour les exemples d'images dans le livre, mais je les utiliserai d'abord, bien que J'utilise d'autres images pour des expériences.

>>> import cv2
>>> import numpy as np
>>>
>>>
>>> img = cv2.imread("eason.png")
>>> hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
>>> h, s, v = cv2.split(hsv)
>>> hmask = cv2.inRange(h, 5, 170)
>>> smask = cv2.inRange(s, 25, 166)
>>> mask = hmask & smask
>>> roi = cv2.bitwise_and(img, img, mask=mask)
>>> cv2.imshow("source", img)
>>> cv2.imshow("skin", roi)
>>> cv2.waitKey()
-1

insérez la description de l'image ici
En ce qui concerne les résultats, la détection de la couleur de la peau est toujours possible, essayez ensuite la détection de YCrCb. De l'effet ci-dessus, en fait, la partie de bord n'est pas bien traitée, donc le filtre passe-haut, l'une des quatre techniques de flou d'opencv, est utilisé ci-dessous.

# 引入部分和上面的一致
>>> ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
>>> y, cr, cb = cv2.split(ycrcb)
>>> cr = cv2.GaussianBlur(cr, (5, 5), 0)
>>> _, skin = cv2.threshold(cr, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
>>> cv2.imshow("res", skin)
>>> cv2.imshow("source", img)
>>> cv2.waitKey()
-1

insérez la description de l'image ici
L'effet est assez bon, mais je ne peux que copier l'exemple et je ne peux pas obtenir d'image colorée.

canal alpha

Sur la base de l'espace colorimétrique RVB, le canal A est ajouté pour indiquer la transparence ou l'opacité, c'est l'espace colorimétrique RGBA. La plage de valeurs du canal A peut être [0, 1] ou [0, 255]. Dans le traitement d'image courant, il existe généralement trois canaux RVB, donc pour utiliser l'espace colorimétrique RGBA, cvtColor est requis pour la conversion. Voici juste un enregistrement, pas une extension.

4. Opération d'image

On dit, ah, on dit que l'ajout d'images et les opérations sur les bits sont utilisés pour la décomposition du plan binaire de l'image, le cryptage XOR de l'image, le tatouage numérique, l'encodage/décodage du visage, etc. Si vous voulez inverser, ce n'est pas clair pour le moment. Le traitement de cette partie est temporairement démontré par la programmation interactive de python.

Ajout

Il existe deux types principaux, l'un est l'ajout mené par l'opérateur simple +, et l'autre est la fonction d'ajout d'ajout fournie par opencv. Décrivez les règles :

  • Opérateur plus, lorsque l'image a et l'image b utilisent un opérateur plus simple pour effectuer une opération plus, une fois que leurs résultats dépassent la valeur maximale de niveaux de gris de 255, la valeur est modulo 256 et la valeur d'addition est this Si le reste n'est pas dépassé, il fonctionnera normalement.
    a + b { a + b , a + b ≤ 255 mod ( a + b , 256 ) , a + b > 255 a+b \begin{cas} a+b,&a+b\le255\\ mod(a+ b , 256),&a+b>255 \end{cas}un+b{ un+b ,mod ( un _ _+b ,256 ) ,un+b255un+b>255
  • cv2.add(a, b), l'image a et l'image b sont ajoutées à l'aide de cette fonction, qui est la même que l'addition dominée par l'opérateur plus ci-dessus, et il y a aussi deux résultats. Le premier est lorsque la somme des deux est supérieure à 255 , alors qu'elle soit réservée comme valeur maximale, 255 est la valeur de saturation, plafonnée ; si elle n'est pas supérieure à 255, elle sera additionnée normalement.
    cv 2. add ( une , b ) { a + b , a + b ≤ 255 255 , a + b > 255 cv2. add(a, b) \begin{cases} a+b, &a+b\le255\\ 255, &a+b>255 \end{cas}c v 2. un jj ( une ,b ){ un+b ,255 ,un+b255un+b>255

Prenons un exemple simple

import cv2
import numpy as np

# 灰度图模式读取
img = cv2.imread('among.png', 1)
# 加法处理
a = img + img
b = a + a
c = cv2.add(img, img)
c = cv2.add(c, c)

cv2.imshow("a", a)
cv2.imshow("b", b)
cv2.imshow("c", c)
cv2.imshow("d", d)
cv2.waitKey()

Les résultats de l'opération sont les suivants :
insérez la description de l'image ici
a et b sont des opérations d'addition d'image utilisant l'opérateur d'addition, et c et d sont des opérations d'addition utilisant la fonction d'addition de cv2. On voit clairement que la première a un sens de ligne dans le opérations d'addition de plus en plus poussées Il est de plus en plus évident que les colorées deviennent de plus en plus foncées, tandis que les secondes sont à l'inverse, devenant de plus en plus saturées et apparaissant de plus en plus blanches. C'est un simple traitement d'images, mais si c'est entre deux images ou même entre des valeurs et des images, ce n'est pas clair, car il n'y a pas de référence.

somme pondérée

La somme pondérée consiste à prendre en compte les éléments du poids de l'image lors du calcul de la somme des valeurs des pixels des deux images. Les deux images de somme pondérée doivent avoir la même taille et le même type, mais il n'y a aucune exigence pour le canal et le type spécifique. Cependant, les informations de blog que j'ai trouvées sont essentiellement scénarisées et il n'y a aucune déclaration appropriée pour une référence humaine, mdzz. Pour le moment, copions ici le concept du livre et utilisons la formule :
dst = saturer ( src 1 × α + src 2 × β + γ ) dst = saturer(src1\times\alpha + src2\times\beta+ \gamma)ds t _=s a t u r a t e ( src 1×un+src2 _×b+γ )
La réalisation de cette somme pondérée est la fonction addWeighted dans opencv. Correspondant à la formule ci-dessus, elle doit également passer en cinq paramètres : src1, alpha, src2, beta, gama. Le soi-disant poids est la différence entre le deux images à mon avis Proportion, qui est plus évidente dans les résultats de l'image finale, donc ce qui précède peut être compris comme: image de résultat = image 1 x coefficient 1 + image 2 x coefficient 2 + quantité de réglage de la luminosité.

Exemple simple de mélange d'images :

import cv2
import numpy as np

a = cv2.imread('blena.png')
b = cv2.imread('bboat.png')
c = cv2.addWeighted(a, 0.2, b, 0,8, 0)
d = cv2.addWeighted(a, 0.5, b, 0,5, 0)
e = cv2.addWeighted(a, 0.8, b, 0,2, 0)
cv2.imshow("c", c)
cv2.imshow("d", d)
cv2.imshow("e", e)
cv2.waitKey()

Parce que je n'ai vraiment aucune ressource, j'ai dû prendre des captures d'écran de deux images dans les livres Baidu et les enregistrer pour l'expérimentation, puis les afficher en fonction de la proportion croissante de l'image du visage. Les résultats sont les suivants : évident
insérez la description de l'image ici
. La proportion devient de plus en plus grande et le visage de l'image devient de plus en plus évident. Ceci est un exemple d'images mixtes. J'ai entendu dire que si les images sont de tailles différentes, vous pouvez utiliser le redimensionnement pour les ajuster. Essayer à nouveau. Après avoir changé l'image, le code est ajusté à :

import cv2
import numpy as np

a = cv2.imread("ayanamirei.jpg")
b = cv2.imread("asikaj.jpg")

# 比例调整大小,本来size并不一样,但调整后以外发现一致了,应该是算法问题
a = cv2.resize(a, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_LINEAR)
b = cv2.resize(b, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_LINEAR)

c = cv2.addWeighted(a, 0.2, b, 0.8, 0)
d = cv2.addWeighted(a, 0.5, b, 0.5, 0)
e = cv2.addWeighted(a, 0.8, b, 0.2, 0)
cv2.imshow("c", c)
cv2.imshow("d", d)
cv2.imshow("e", e)
cv2.waitKey()

Les ajustements ont bien fonctionné, puis j'ai pu refaire un traitement d'image mélangé. La palette de couleurs obtenue est la suivante :
insérez la description de l'image ici
Relativement parlant, le rapport de 0,4 et 0,3 devrait être meilleur.Lorsque le fond n'est pas si évident, il devrait être préférable de dégager le fond. Le choix du dernier paramètre de redimensionnement a une grande influence sur le dessin :

  • INTER_NEAREST : interpolation du plus proche voisin
  • INTER_LINEAR : interpolation linéaire (par défaut)
  • INTER_AREA : Interpolation de zone
  • INTER_CUBIC : interpolation spline cubique
  • INTER_LANCZOS4 : l'interpolation Lanczos
    cv2.INTER_AREA est recommandée pour effectuer un zoom arrière ; cv2.INTER_CUBIC et cv2.INTER_LINEAR sont recommandés pour effectuer un zoom avant

opération logique au niveau du bit

En ce qui concerne les opérations logiques, il existe des opérations telles que AND ou NOT, XOR, etc. L'opération logique au niveau du bit consiste ici à convertir un nombre en un nombre binaire et à effectuer des opérations logiques sur chaque numéro de bit correspondant. Il n'est pas nécessaire de développer les opérations logiques spécifiques et ne sera pas enregistré. Les fonctions d'opération logique au niveau du bit fournies par opencv sont :

  • Bitwise and, dst = cv2.bitwise_and(src1, src2[, mask])
  • Bitwise or, dst = cv2.bitwise_or(src1, src2[, mask])
  • Pas au niveau du bit, dst = cv2.bitwise_not(src[, mask])
  • Bitwise XOR, dst = masque cv2.bitwise_xor(src1, src2[, mask])
    , masque d'opération facultatif, valeur de tableau monocanal 8 bits

Utilisation des opérations AND au niveau du bit

Pour les opérations AND au niveau du bit, lors du traitement d'images en niveaux de gris, le pixel est AND au niveau du bit avec la valeur 0, et seul 0 est obtenu, et le AND au niveau du bit avec 255 est la valeur d'origine. Lorsqu'une image avec un grand nombre de valeurs 0 et 255 valeurs Au lieu de AND au niveau du bit, vous obtenez une image partiellement "noircie", comme si elle avait été découpée. Pour le dire crûment, une autre image est utilisée pour recouvrir partiellement voire totalement l'image cible.

import cv2
import numpy as np

img = cv2.imread("blena.png")
# 制作一个和原图同尺寸的数组
mask = np.zeros(img.shape, dtype=np.uint8)

# 固定区域设置纯白
mask[100:280, 100:250] = 255
a = cv2.bitwise_and(img, mask)

# 三图进行显示
cv2.imshow("source", img)
cv2.imshow("mask", mask)
cv2.imshow("res", a)
cv2.waitKey()

insérez la description de l'image ici

Eh bien, à l'exception de l'application des opérations ET au niveau du bit, les deux ou trois autres semblent n'avoir aucune application pour le moment. Cependant, il convient de noter que les deux matrices pour les opérations logiques au niveau du bit doivent être de la même taille, sinon des erreurs se produiront. Il y a donc beaucoup d'utilisations qui vont ajuster la taille de l'image.

Décomposition des plans de bits

L'image couleur peut être divisée en trois matrices selon les trois canaux RVB, qui est le désassemblage des canaux ; l'image obtenue en combinant les pixels sur le même bit de l'image est également appelée un plan de bits, et ce processus est appelé une décomposition un peu plane. Dans l'image en niveaux de gris, la valeur d'un pixel est comprise entre 0 et 255, soit une plage de 8 bits dans un octet. La valeur de chaque bit est extraite pour obtenir un plan de bits, plus l'image d'origine, il y aura un total de 9 images . Si l'image couleur était divisée en plans de bits, ce serait trop. Donc, l'exemple ici est juste une image en niveaux de gris, ce qui est plus facile à faire.

Pour une image en niveaux de gris, lorsqu'elle est développée en un nombre binaire et que le plan de bits est coupé, plus le poids du bit où se trouve le plan de bits est élevé, plus la corrélation entre le plan de bits et l'image d'origine est élevée. le poids du bit où se trouve le plan de bits Plus le correspondant est faible, plus la corrélation avec l'image d'origine est faible. Pour le dire franchement, le plan de bits découpé par les chiffres correspondant à la puissance 2 à la puissance 0 est moins visible de l'image d'origine, et le plan de bits découpé par les chiffres correspondant à la puissance 2 à la puissance 7 ressemble plus à l'image originale.

Pour les images couleur RVB, il est divisé en trois canaux, et la couleur correspondant aux trois canaux est également un nombre binaire de 8 bits. Divisez-les en trois canaux, puis synchronisez les chiffres correspondants pour découper un plan de bits basé sur les canaux, puis les combiner, qui est le plan binaire de l'image d'origine. Eh bien, il semble que la segmentation du plan de bits de la carte des couleurs ne soit pas trop difficile.

Étapes de segmentation du plan binaire :

  • Extrayez la largeur et la hauteur de l'image d'origine et construisez une matrice de la même taille ;
  • Construisez la matrice ci-dessus en un pixel dont la valeur est 2 n 2 ^ n2La matrice de n est utilisée pour l'extraction ;
  • Effectuez une opération ET au niveau du bit sur la matrice extraite et l'image d'origine pour obtenir le plan de bits
  • Afin d'éviter que les plans de bits correspondant aux chiffres plus petits ne soient affichés en noir pur, il doit être seuillé, de sorte que le résultat final ne soit que des valeurs en noir et blanc telles que 0 et 255, ou qu'il soit vrai ou faux .
import cv2
import numpy as np

img = cv2.imread("alian.jpg", 0)
img = cv2.resize(img, None, fx=0.4, fy=0.4, interpolation=cv2.INTER_LINEAR)
cv2.imshow("source", img)
w, h = img.shape
# 创建8层同规模的矩阵,每个矩阵用来放置对应数位的提取矩阵,在后面的循环中给对应矩阵赋值
arrays = np.zeros((w, h, 8), dtype=np.uint8)
for i in range(8):
    x[:, :, i] = 2**i

# 循环对原图进行按位与运算提取位平面,然后进行阈值处理,最后输出图像
for i in range(8):
    temp = cv2.bitwise_and(img, x[:, :, i])
    # 将temp中大于0的值转为True,除此以外的值转换为False
    mask = temp>0
    # 将temp中True换成255
    temp[mask] = 255
    cv2.imshow("res"+str(i+1), temp)
cv2.waitKey()

insérez la description de l'image ici
insérez la description de l'image ici
insérez la description de l'image ici
D'après ce qui précède, on peut clairement voir que l'extraction du plan de bits et l'affichage d'une image en niveaux de gris (l'avatar d'Alian est utilisé, malheureusement il est trop blanc et le corps est trop grand, donc la taille doit être modifiée), dans ce Ainsi, si vous souhaitez traiter l'image couleur, ajoutez l'image couleur dans le processus ci-dessus pour diviser les trois canaux et enfin synthétiser les trois canaux. comme suit:

import cv2
import numpy as np

img = cv2.imread("alian.jpg")
img = cv2.resize(img, None, fx=0.4, fy=0.4, interpolation=cv2.INTER_LINEAR)
cv2.imshow("source", img)
w, h = img.shape[:2]
b, g, r = cv2.split(img)
b_arr = np.zeros((w, h, 8), dtype=np.uint8)
g_arr = np.zeros((w, h, 8), dtype=np.uint8)
r_arr = np.zeros((w, h, 8), dtype=np.uint8)
for i in range(8):
    b_arr[:, :, i], g_arr[:, :, i], r_arr[:, :, i] = 2**i, 2**i, 2**i

for i in range(8):
    t1 = cv2.bitwise_and(b, b_arr[:, :, i])
    t2 = cv2.bitwise_and(g, g_arr[:, :, i])
    t3 = cv2.bitwise_and(r, r_arr[:, :, i])
    mask1 = t1 >0
    mask2 = t2 >0
    mask3 = t3 >0
    t1[mask1], t2[mask2], t3[mask3] = 255, 255, 255
    temp = cv2.merge([t1, t2, t3])
    cv2.imshow("res"+str(i+1), temp)
cv2.waitKey()

insérez la description de l'image ici
insérez la description de l'image ici
insérez la description de l'image ici

filigrane

Tous les principaux sites Web sont très préoccupés par leurs propres problèmes de droit d'auteur, et ils ne peuvent pas attendre pour y mettre diverses empreintes. Le filigrane sur l'image est une manifestation. Parfois, vous le téléchargez, et quand il est affiché sur le site Web, il y aura être les empreintes des autres, ce qui est très dégoûtant. . Mais pour les particuliers, cela fait aussi partie des droits de propriété intellectuelle à protéger, et c'est une question d'opinion publique.

Il a été introduit ci-dessus qu'un plan de bits est une collection de pixels d'une image basée sur les chiffres du même binaire. Plus le chiffre est grand, plus l'image du plan de bits correspondant correspond à l'image d'origine, et plus le chiffre est petit, plus la valeur est grande. la différence avec l'image d'origine. Par conséquent, le bit le plus bas du nombre binaire est la 0ème position de 2, également appelé bit le moins significatif (LSB, Least Significant Bit) . Lorsque les informations sont stockées dans ce bit et combinées dans l'image d'origine, ces informations sont masquées. L'information, le filigrane appartient à ce type d'informations cachées. Comment?

La première consiste à lire l'image à tatouer et une image avec un filigrane clair, extraire le bit le moins significatif correspondant au plan de bits de cette dernière, puis effectuer les réglages appropriés ou zoom avant ou arrière selon le taille de l'image, puis collez-la sur l'image qui doit être filigranée, cette étape peut utiliser la fonction de somme pondérée de cv2 ou la fonction de collage de PIL.

Carte en filigrane personnalisée

Tout d'abord, parce que je n'ai pas l'image originale du filigrane, je souhaite générer deux images originales du filigrane avec ma propre étiquette, une avec du texte blanc sur fond noir et deux avec du texte noir sur fond blanc. Il est très simple de générer des images d'arrière-plan en noir et blanc. Une valeur de pixel de 0 est un noir pur et une valeur de pixel de 255 est un blanc pur. Pour ajouter du texte, vous pouvez utiliser la fonction putText fournie avec cv2. Allez, commencez à construire.

import cv2
import numpy as np

# 制造黑色和白色背景
black_w = np.zeros((300, 450), dtype=np.uint8)
white_b = np.ones((300, 450), dtype=np.uint8)*255

# 调用putText函数添加手写体的标签,距离左上角150的位置,字体大小为3,粗细为3
cv2.putText(black_w, 'JackSAMA', (0, 150), cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 3, 255, 2)
cv2.putText(white_b, 'JackSAMA', (0, 150), cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 3, 0, 2)

cv2.imshow("black", black_w)
cv2.imshow("white", white_b)
cv2.waitKey()

insérez la description de l'image ici
L'image du filigrane est prête. En fait, elle peut être générée en fonction de la situation réelle. Tant que la fonction de filigrane est personnalisée, elle peut également être ajustée en fonction de la taille de l'image. Bien sûr, elle peut également être ajustée de manière synchrone s'il y a une image réelle. Les paramètres de fonction de putText sont principalement les suivants :

img = cv2.putText(img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]])
img, 操作图像对象
text,添加的文本,一般都是英文,中文使用会乱码,暂时也还没解决
fontFace,用过标签语言的应该都知道这是字体类型的意思
fontScale,字体大小
color,对于灰度图,简单的0-255表示即可,如果是rgb彩图就要适用(b, g, r)进行表示
thickness,线条粗细,默认是1
lineType,线条类型,默认是8连接类型
bottomLeftOrigin,默认为False,这样文本就是横着来;输入为True就是文本竖着来
fontFace analyser
cv2.FONT_HERSHEY_SIMPLEX Les polices normales sans empattement sont des polices anglaises couramment utilisées
cv2.FONT_HERSHEY_PLAIN petite police sans empattement
cv2.FONT_HERSHEY_DUPLEX taille normale sans empattement
cv2.FONT_HERSHEY_COMPLEX police serif normale
cv2.FONT_HERSHEY_TRIPLEX Polices serif de taille normale
cv2.FONT_HERSHEY_COMPLEX_SMALL version simplifiée avec empattement
cv2.FONT_HERSHEY_SCRIPT_SIMPLEX police de style d'écriture manuscrite
cv2.FONT_HERSHEY_SCRIPT_COMPLEX Version complexe de la police manuscrite
cv2.FONT_ITALIC marque italique
type de ligne analyser
cv2.FILLED type rempli
cv2.LINE_4 4 types de connexion
cv2.LINE_8 8 types de connexion
cv2.LINE_AA Anti-aliasing pour des lignes plus lisses

Ce qui précède sont quelques interprétations des paramètres de police et des types de ligne dans opencv. J'ai entendu dire qu'il peut être remplacé par une bibliothèque auto-conçue, mais je ne sais pas comment. Mais il semble que l'ajout d'un paramètre de ligne anti-aliasing semble beaucoup plus confortable.

Intégrer le filigrane
Ajoutez un filigrane à l'image de Doraemon.

>>> import cv2
>>> import numpy as np
>>>
>>> mong = cv2.imread("among.png")
>>> mong.shape
(347, 272, 3)
>>> watermark = cv2.imread("white_black_sign.png")
>>> watermark.shape
(300, 450, 3)
# 调整水印原图大小和对应待处理图片补充空白
>>> watermark = cv2.resize(watermark, None, fx=0.3, fy=0.3, interpolation=cv2.INTER_AREA)
>>> cv2.imshow("", watermark)
>>> cv2.waitKey()
-1
>>> watermark.shape
(90, 135, 3)
>>> temp = np.ones(mong.shape, dtype=np.uint8)*255
>>> temp[210:300, 137:272] = watermark
>>> cv2.imshow("", temp)
>>> cv2.waitKey()
-1
# 进行加权和拼接,实现图片添加水印
>>> res1 = cv2.addWeighted(mong, 0.9, temp, 0.1, 0)
>>> cv2.imshow("", res1)
>>> cv2.waitKey()
-1

insérez la description de l'image ici

supprimer filigrane

De nombreuses façons de supprimer le filigrane consistent à demander à l'artiste de créer une image de filigrane de couleur unie comme celle que j'ai générée ci-dessus, puis d'identifier la gamme de couleurs, puis de fusionner les deux images, c'est-à-dire de la coller, il y a donc de nombreuses utilisations Utilisation de la bibliothèque PIL

import cv2
import PIL import Image
import numpy as np

img = cv2.imread("./iamfine.png")
h, w, _ = img.shape[0:3]
#切割,根据实际水印位置而定,[y0:y1, x0:x1]为下面裁剪用法,裁剪完后可以用上面的方法输出查看一下
cropped = img[int(h*0.9):h, int(w*0.75):w]
# 对图片进行阈值化处理,把由后面两个参数划定的RGB色彩空间范围外的色彩输出为0或者255,由图片底色确定这个范围
thresh = cv2.inRange(cropped, np.array([230, 230, 230]), np.array([250, 250, 250]))
#创建结构和尺寸的数据元素
kernel = np.ones((3, 3), np.uint8)
# 扩展待修复区域
watermask = cv2.dilate(thresh, kernel, iterations=10)
specular = cv2.inpaint(cropped, watermask, 5, flags=cv2.INPAINT_TELEA)
#保存去除水印的残图
cv2.imwrite("new.png", specular)

# 用PIL的paste函数把残图粘贴在原图上得到新图
i = Image.open("./img/iamfine.png")
i2 = Image.open("./img/new.png")
i2.paste(i, (int(w*0.75), int(h*0.9), w, h))
i2.save("final.png")

Comparaison des deux images :
|
En fait, vous pouvez utiliser la méthode d'ajout d'un filigrane ci-dessus pour l'inverser.

Générer des images de personnages

Une image est composée de pixels, et les ordinateurs stockent les images en binaire, et les bits utilisés pour stocker les pixels sont la profondeur de l'image, qui est stockée dans le bit 1. L'image est soit en noir, soit en blanc, car il n'y a que 0 et 1 ; utilisez un octet (8 bits) pour stocker la valeur de 0 à 255. La couleur a trois couleurs primaires : rouge, vert et bleu. Ces trois couleurs peuvent être superposées pour représenter d'autres couleurs, et la couleur des pixels de cette image est déterminée par les trois couleurs de RVB, qui sont généralement appelées trois canaux, car ils utilisent respectivement trois caractères. Les sections représentent respectivement les valeurs individuelles des trois couleurs primaires, et lorsqu'elles sont empilées, c'est la couleur du pixel, qui est généralement (0-255, 0-255, 0-255 ) en représentation pixel.


L'image de caractères que nous voulons générer consiste en fait à établir un mappage des pixels aux caractères, car le jeu de caractères est également très volumineux, même le jeu ASCII le plus basique est de 128 caractères, mais nous n'avons pas besoin d'en utiliser autant, nous pouvons utilisez quelques caractères simples pour constituer un jeu de caractères, puis associez-le à la table de couleurs du pixel (car les règles de mappage sont personnalisées).


La vérité est la suivante, mais je n'ai pas compris les exemples des autres, mais une autre méthode m'a beaucoup gagné, c'est-à-dire convertir d'abord l'image en une image en niveaux de gris, c'est-à-dire une image en noir et blanc, à ce moment, elle être facile à convertir des pixels en caractères beaucoup de.
L'opération simple est la suivante :

import cv2
import numpy as np

str = "#+-."
img = cv2.imread("among.png", 0)
# 此时就只有height和width两个值,没有depth
h, w = img.shape[0:2]
for_change = np.ndarray([h, w])
font = cv2.FONT_HERSHEY_SIMPLEX
for i in range(0, h, 5):
    for j in range(0, w, 5):
        t = str[round(3-img[i, j]/255*3)]
        cv2.putText(for_change, t, (j, i), font, 0.1, color=(255, 255, 255))
        
cv2.imshow("", for_change)
cv2.waitKey(0)
cv2.imwrite("asciiPic.png", for_change)


Eh bien, au moins c'est réalisé, mais il y a un problème avec cela, c'est-à-dire que même la carte de base est remplacée ensemble, et puis c'est un peu accrocheur.

générer une vidéo de personnage

Étant donné qu'une vidéo est une image image par image, la génération d'une image de personnage représente la moitié du chemin pour générer une vidéo de personnage, et la logique ci-dessus peut être itérée pour générer une vidéo de personnage.

import cv2
import numpy as np


def pixel2char(pixel):
    char_list = "@#$%&erytuioplkszxcv=+---.     "
    index = int(pixel / 256 * len(char_list))
    return char_list[index]


def get_char_img(img, scale=4, font_size=5):
    # 调整图片大小
    h, w = img.shape
    re_im = cv2.resize(img, (w//scale, h//scale))
    # 创建一张图片用来填充字符
    char_img = np.ones((h//scale*font_size, w//scale*font_size), dtype=np.uint8)*255
    font = cv2.FONT_HERSHEY_SIMPLEX
    # 遍历图片像素
    for y in range(0, re_im.shape[0]):
        for x in range(0, re_im.shape[1]):
            char_pixel = pixel2char(re_im[y][x])
            cv2.putText(char_img, char_pixel, (x*font_size, y*font_size), font, 0.5, (0, 0, 0))
    return char_img


def generate(input_video, output_video):
    # 1、读取视频
    cap = cv2.VideoCapture(input_video)

    # 2、获取视频帧率
    fps = cap.get(cv2.CAP_PROP_FPS)

    # 读取第一帧,获取转换成字符后的图片的尺寸
    ret, frame = cap.read()
    char_img = get_char_img(cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY), 4)

    # 创建一个VideoWriter,用于保存视频
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    writer = cv2.VideoWriter(output_video, fourcc, fps, (char_img.shape[1], char_img.shape[0]))
    while ret:
        # 读取视频的当前帧,如果没有则跳出循环
        ret, frame = cap.read()
        if not ret:
            break
        # 将当前帧转换成字符图
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        char_img = get_char_img(gray, 4)

        # 转换成BGR模式,便于写入视频
        char_img = cv2.cvtColor(char_img, cv2.COLOR_GRAY2BGR)
        writer.write(char_img)
    writer.release()


if __name__ == '__main__':
    generate('in.mp4', 'out.mp4')

Eh bien, cette construction. . . . . . Je n'en peux plus, l'ordinateur se met à fumer avant que la génération ne soit finie ! ! ! Maître Lu fume ! ! ! Après déconnexion, la vidéo ne fait pas la moitié de la longueur de la vidéo d'origine, mais la taille est plusieurs fois celle de la vidéo d'origine ! ! ! Optimisons-le plus tard, si vous ne pouvez pas traire, vous ne pouvez pas traire.

L'image utilisée ci-dessus

insérez la description de l'image ici
insérez la description de l'image ici
insérez la description de l'image ici
insérez la description de l'image ici
insérez la description de l'image ici
insérez la description de l'image ici
Il y a aussi un morceau de Rei Ayanami, mais il est trop gros pour être transmis. Faisons-le d'abord, et enregistrons-le plus tard. Il y a aussi une vidéo : Eh bien, il semble si difficile d'obtenir cette ressource.

J'ai récemment lancé un site web personnel, et divers articles sont prêts à y être regroupés. Si cela vous intéresse, vous pouvez le consulter :

petite gare cassée

Je suppose que tu aimes

Origine blog.csdn.net/weixin_44948269/article/details/128150084
conseillé
Classement