Kuru mallar | Tarihteki en eksiksiz OpenCV canlı algılama eğitimi!

AI Technology Review Press: Bu makale ünlü bilgisayarla görme öğretim web sitesi "pyimagesearch" den geliyor ve yazar Adrian Rosebrock. Bu makalede, Adrian, "görüntülerde / videolarda gerçek yüzlerin ve sahte yüzlerin nasıl tanımlanacağı" konusunda derinlemesine bir analiz yapacak ve OpenCV tabanlı modeller kullanarak canlı algılama için belirli yöntemler tanıtacak. Leifeng.com AI Technology Review aşağıdaki gibi derlenmiştir.

Bu eğitim, canlılık tespiti için OpenCV'yi nasıl kullanacağınızı öğretecektir. Öğrenerek, yüz tanıma sisteminde sahte yüzleri algılayabilen ve yüz karşıtı aldatma gerçekleştirebilen bir canlılık detektörü oluşturabileceksiniz.

Geçtiğimiz yıl, bu makalenin yazarı, aşağıdakiler de dahil olmak üzere birçok yüz tanıma eğitimi yazdı:

  • OpenCV'ye dayalı yüz tanıma (okuma adresi: https://www.pyimagesearch.com/2018/09/24/opencv-face-recognition/)

  • Yüz tanıma için dlib, Python ve derin öğrenmeyi kullanın (adresi okuyun: https://www.pyimagesearch.com/2018/06/18/face-recognition-with-opencv-python-and-deep-learning/)

  • Raspberry Pi'ye dayalı yüz tanıma (okuma adresi: https://www.pyimagesearch.com/2018/06/25/raspberry-pi-face-recognition/)

Ancak, aldığım bazı e-postalarda ve bu blog yazılarındaki yüz tanıma hakkındaki yorumlarda sık sık sorulan bir soru şuydu:

Gerçek bir yüz ile sahte bir yüzü nasıl ayırt edebilirim?

Şüphelenmeyen bir kullanıcı kasıtlı olarak yüz tanıma sisteminizi kandırmaya çalışırsa ne olacağını hayal edin?

Yüz tanıma yapan bir kameraya bir fotoğraf veya video (bu makalenin üst kısmındaki resim gibi) göstermeye çalışabilirler. Bu fotoğraf ve videodaki kişiler, kullanıcıların kendileri değildir.

Bu durumda, yüz tanıma sistemi, önlerinde gösterilen resimleri doğru yüz olarak tanımak tamamen mümkündür ve bu da sonuçta yetkisiz kullanıcıların yüz tanıma sisteminden kaçınmasına neden olacaktır!

Bu "sahte" ve "gerçek / yasal" yüzleri nasıl ayırt edebiliriz? Anti-face spoofing algoritması yüz tanıma uygulamanıza nasıl uygulanabilir?

Cevap, canlı algılama için OpenCV kullanmaktır, bu makalede anlatılacak.

Peki OpenCV tabanlı canlı algılama işlevini kendi yüz tanıma sisteminize nasıl entegre edebilirsiniz? Cevap bu makalede bir sonraki bölümde verilecektir.

OpenCV'ye dayalı canlı algılama

Bu eğiticinin ilk bölümünde, "Canlılık tespiti nedir?" Ve "Yüz tanıma sistemini iyileştirmek için neden canlılık tespitine ihtiyacımız var?" Dahil olmak üzere canlılık tespitini tartışacağız.

Burada, ilk olarak canlı algılama için kullanılacak veri setini gözden geçiriyoruz:

  • Canlı algılama için bir veri kümesi nasıl oluşturulur

  • Gerçek ve sahte yüzlerimizin resim örnekleri

Canlı vücut dedektörü projesi için proje yapısını da gözden geçireceğiz.

Canlı bir dedektör oluşturmak için gerçek ve sahte yüzleri ayırt edebilen derin bir sinir ağını eğiteceğiz.

Bu nedenle şunlara ihtiyacımız var:

1. Bir görüntü veri kümesi oluşturun

2. Canlı algılama yapabilen evrişimli bir sinir ağı uygulayın (biz buna "LivenessNet" diyoruz)

3. Canlı algılama ağını eğitme

4. Eğitimli yaşam algılama modelimizi kullanabilen ve bunu gerçek zamanlı videoya uygulayabilen bir Python + OpenCV komut dosyası oluşturun

Şimdi ana konuya geçelim!

Canlı test nedir ve neden buna ihtiyacımız var?

Şekil 1: OpenCV'ye dayalı canlı algılama. Soldaki resim canlı vücudumun (gerçek kişi) bir videosu ve sağdaki resimde iPhone'umu tuttuğumu görebiliyorum (ekranda yüz tanıma sistemini aldatmak için kullanılan sahte bir yüz / yüz resmi var).

Yüz tanıma sistemleri giderek daha popüler hale geliyor. İPhone / akıllı telefondaki yüz tanıma sisteminden, Çin'de büyük ölçekli gözetim-yüz tanıma için kullanılan yüz tanıma sistemine kadar her yerde yaygınlaştı.

Ancak yüz tanıma sistemi, "aldatıcı" ve "gerçek dışı" yüzler tarafından kolayca kandırılır.

Yüz tanıma için kullanılan bir kameraya bir kişinin fotoğrafını (ister basılı olsun ister bir akıllı telefon ekranında görüntülenen vb.) Göstererek, yüz tanıma sistemini kandırmak kolaydır.

Yüz tanıma sistemini daha güvenli hale getirmek için, bu tür sahte / gerçek olmayan yüzleri tespit etmemiz gerekiyor ve "canlı algılama" bu algoritmaya atıfta bulunmak için kullanılan terimdir.

Aşağıdakiler dahil birçok canlı algılama yöntemi vardır:

  • Yüz alanında yerel ikili desenlerin (LBP, https://www.pyimagesearch.com/2015/12/07/local-binary-patterns-with-python-opencv/) hesaplanması ve destek vektörlerinin kullanımı dahil olmak üzere doku analizi Makine (SVM), insan yüzlerini gerçek veya sahte olarak sınıflandırır.

Bir yüz resminin Fourier alanını görüntülemek gibi frekans analizi (Fourier onu dönüştürür).

  • İki ardışık kare arasındaki piksel değerlerindeki değişiklikleri görüntüleme gibi değişken odak analizi.

  • Göz hareketi, dudak hareketi ve göz kırpma algılama dahil sezgisel algoritmalara dayanır (https://www.pyimagesearch.com/2017/04/24/eye-blink-detection-opencv-python-dlib/). Bu tür bir algoritma, kullanıcının başka bir kişinin resmini göstermediğinden emin olmak için göz hareketini ve göz kırpmayı izlemeye çalışır (çünkü resim yanıp sönmez veya dudakları hareket ettirmez).

  • Optik akış algoritması, yani üç boyutlu bir nesne ve iki boyutlu bir düzlem tarafından oluşturulan optik akışın farkını ve özelliklerini görüntülemek içindir.

  • Üç boyutlu yüz şekli, Apple iPhone tarafından kullanılan yüz tanıma sistemine benzer olup, yüz tanıma sisteminin basılı / fotoğraftaki / görüntüdeki gerçek yüz ile başka bir kişinin yüzünü ayırt etmesini sağlar.

  • Yukarıdaki yöntemlerin birleştirilmesi, yüz tanıma sistemi mühendislerinin kendi özel uygulamalarına uygun bir canlı algılama modeli seçmelerine olanak tanır.

Chakraborty ve Das tarafından 2014 yılında yayınlanan "Yüz Canlılığı Algılamaya Genel Bakış" adlı makalede (makale okuma adresi: https://arxiv.org/pdf/1405.2227.pdf), canlılık algılama algoritmasının kapsamlı bir incelemesi yapılmıştır.

Bu makalenin öğreticisinde, canlı algılamayı bir ikili sınıflandırma sorunu olarak ele alıyoruz.

Bir girdi görüntüsü verildiğinde, gerçek ve yanlış yüzleri tanıyabilen evrişimli bir sinir ağını eğiteceğiz.

Canlı algılama modelimizi eğitmeye başlamadan önce, kullanılan veri kümesine bir göz atalım.

Canlı tespit videomuz

Şekil 2: Toplanan gerçek yüzler ve sahte / aldatıcı yüzler örneği. Soldaki video yüzümün yasal bir videosudur. Sağda, dizüstü bilgisayarım tarafından solda kaydedilen video var.

Örneğimizi basitleştirmek için, bu makalede oluşturduğumuz canlılık detektörü, ekrandaki gerçek yüzler ile aldatıcı yüzler arasında ayrım yapmaya odaklanacak.

Algoritma, yazıcılar tarafından yazdırılan yüzler veya yüksek çözünürlüklü dahil olmak üzere diğer aldatıcı yüz türlerine kolayca genişletilebilir.

Canlı algılama veri seti oluşturmak için şunları yapacağım:

1. iPhone'umu kullanın ve onu portre / selfie moduna ayarlayın.

2. Ofiste dolaştığım 25 saniyelik bir video kaydedin.

3. Bu 25 saniyelik videoyu iPhone'um masaüstüme bakacak şekilde tekrar oynatın Bu yeniden oynatılan videoyu masaüstü perspektifinden kaydettim;

4. Bu, iki örnek video oluşturacaktır. Biri "gerçek" bir yüz, diğeri ise "sahte / aldatıcı" bir yüz olarak kullanıldı.

5. Sonunda, iki tür yüzün ilgili ROI'lerini çıkarmak için bu iki video setine aynı anda yüz algılama teknolojisi uyguladım.

Bu makalenin "indir" bölümünde size gerçek yüz videoları ve sahte yüz videoları sağladım.

Bir veri seti oluşturmaya başlamak için bu videoları doğrudan kullanabilirsiniz, ancak canlılık dedektörünüzün sağlamlığını ve doğruluğunu iyileştirmeye yardımcı olmak için daha fazla veri toplamanızı öneririm.

Testler yoluyla, bu modelin kendi yüzümü tespit etme yönünde önyargılı olacağını doğruladım - çünkü tüm modeller kendi videomla eğitildi, bu model hala anlamlı. Ayrıca, Kafkasyalı olduğum için, bu veri setinin diğer ten renklerinin yüzlerini tespit etmek için kullanıldığında aynı mükemmel performansa sahip olmasını beklemiyorum.

İdeal olarak, birden çok ırkın yüzlerini içeren verileri kullanarak bir model eğitebilirsiniz. Okuyucular canlı algılama modelini iyileştirme konusunda daha fazla öneri isterse, lütfen aşağıdaki "Sınırlamalar ve Daha Fazla Çalışma" bölümüne bakın.

Bir sonraki eğitimde, kaydettiğim veri setini nasıl kullanacağınızı öğrenecek ve gerçek bir canlı vücut dedektörü elde etmek için OpenCV ve derin öğrenme teknolojisini kullanacaksınız.

Proje yapısı

Okumaya devam etme sürecinde, okuyucular kodu, veri setini ve canlı algılama modelini elde etmek ve arşivi açmak için "İndir" bölümünde sağlanan bağlantıyı kullanabilir.

Projenin dizinine gittiğinizde, aşağıdaki yapıyı göreceksiniz:

$ ağaç --dirsfirst --filelimit 10

veri kümesi

sahte

gerçek

yüz algılayıcısı

deploy.prototxt

res10_300x300_ssd_iter_140000.caffemodel

pyimagesearch

__init__.py

livenessnet.py

videolar

fake.mp4

real.mov

collect_examples.py

train_liveness.py

liveness_demo.py

lepickle

canlılık. Model

plot.png

6 dizin, 12 dosya

Projemizde dört ana dizin bulunmaktadır:

  • "Veri Kümesi /": Veri kümesi dizinimiz iki tür görüntü içerir:

(1) Kamera yüz videomu oynayan cep telefonu ekranının çektiği sahte görüntüye bakıyor.

(2) Cep telefonumla selfie çektiğim videodaki gerçek görüntü.

  • "Face_detector /": Yüzün ROI'sini bulmak için kullandığımız önceden eğitilmiş Caffe yüz dedektörünü içerir.

  • "Pyimagesearch /": Bu modül, LivenessNet sınıfımızı içerir.

  • "Videolar /": LivenessNet sınıflandırıcısını eğitmek için iki giriş videosu sağladım.

Şimdi, üç Python komut dosyasını ayrıntılı olarak inceleyeceğiz. Bu makalenin sonunda, bu betikleri kendi verileriniz üzerinde çalıştırabilir ve video girişi yapabilirsiniz. Bu eğitimde göründükleri sırayla, üç komut dosyası şunlardır:

1. "collect_examples.py": Bu komut dosyası, giriş video dosyasından yüzün ROI'sini alır ve derin öğrenme yüz canlı algılama veri kümesi oluşturmamıza yardımcı olur.

2. "train_liveness.py": Dosya adından da anlaşılacağı gibi, bu komut dosyası LivenessNet sınıflandırıcımızı eğitecektir. Modeli eğitmek için Keras ve TensorFlow kullanacağız. Eğitim süreci aşağıdaki dosyaları üretecektir:

(1) le.pickle: Sınıf etiketi kodlayıcımız.

(2) liveness.model: Yüz canlılığı algılayıcısını eğitmek için kullandığımız bir dizi Keras modeli.

(3) plot.png: Eğitim geçmişi diyagramı, modelin doğruluk ve kayıp eğrisini gösterir ve modelimizi buna göre değerlendirebiliriz (yani, aşırı uydurma / uydurma).

3. "liveness_demo.py": Demo komut dosyamız, gerçek zamanlı yüz canlı algılaması için video kareleri çekmek üzere web kameranızı başlatır.

Eğitim (video) veri setimizden yüzün yatırım getirisini tespit edin ve çıkarın

Şekil 3: Canlılık algılama veri seti oluşturmak için, videodaki yüzün ROI alanının ilk olarak tespit edilmesi gerekir.

Şimdi başlatılmış veri setimizi ve proje mimarimizi inceleyebiliriz, giriş videosundan gerçek ve sahte yüz görüntülerini nasıl çıkaracağımızı görelim.

Bu komut dosyasının nihai amacı, iki dizini verilerle doldurmaktır:

1. "veri kümesi / sahte /": "fake.mp4" dosyasında yüz ROI alanını içerir.

2. "dataset / real /": "real.mov" dosyasında yüz ROI alanını içerir.

Bu çerçevelere dayanarak, bir sonraki görüntüde derin öğrenmeye dayalı canlı bir dedektör eğiteceğiz.

"Collect_examples.py" dosyasını açın ve aşağıdaki kodu ekleyin:

# gerekli paketleri içe aktarın

numpy'yi np olarak içe aktar

ithal argparse

cv2 içe aktar

işletim sistemini içe aktar

# bağımsız değişkeni yapılandırın ve bağımsız değişkenleri çözümleyin

ap = argparse.ArgumentParser

ap.add_argument ("- i", "--input", tür = str, gerekli = Doğru,

help = "video giriş yolu")

ap.add_argument ("- o", "--output", type = str, required = True,

help = "kırpılmış yüzlerin çıktı dizini yolu")

ap.add_argument ("- d", "--detector", tür = str, gerekli = Doğru,

help = "OpenCV'nin derin öğrenme yüz algılayıcısına giden yol")

ap.add_argument ("- c", "--confidence", tür = float, varsayılan = 0,5,

help = "zayıf algılamaları filtrelemek için minimum olasılık")

ap.add_argument ("- s", "--skip", tür = int, varsayılan = 16,

help = "Yüz algılama uygulamadan önce atlanacak kare sayısı")

args = vars (ap.parse_args)

2-5 satırları, ihtiyacımız olan yükleme paketini tanıtır. Yerleşik Python modülüne ek olarak, komut dosyası yalnızca OpenCV ve NumPy'ye ihtiyaç duyar.

Satır 8-19, komut satırı bağımsız değişkenlerimizi ayrıştırır (https://www.pyimagesearch.com/2018/03/12/python-argparse-command-line-arguments/):

  • "--Input": Girdi video dosyamızın yolu.

  • "--Output": Kırpılan her yüz görüntüsünü depolayan çıktı dizininin yolu.

  • "--Detektör": Yüz dedektörünün yolu. OpenCV'nin derin öğrenme yüz dedektörünü kullanacağız (https://www.pyimagesearch.com/2018/02/26/face-detection-with-opencv-and-deep-learning/). Okuyucuların rahatlığı için, Caffe modeli bu makalenin "indirme" bölümünde mevcuttur.

  • "--Güven": Zayıf yüz algılama sonuçlarını filtrelemenin minimum olasılığı. Varsayılan olarak değer% 50'dir.

  • "--Skip": Her bir görüntüyü algılayıp kaydetmemize gerek yok çünkü bitişik çerçeveler benzer. Bunun yerine, iki yüz algılama görevi arasında N kareyi atlayacağız. Varsayılan N değerini (16) değiştirmek için bu parametreyi kullanabilirsiniz.

Yüz algılayıcıyı yüklemeye ve video akışımızı başlatmaya devam edelim:

# serileştirilmiş yüz dedektörümüzü diskten yükleyin

baskı ("yüz algılayıcı yükleniyor ...")

protoPath = os.path.sep.join ()

modelPath = os.path.sep.join ()

net = cv2.dnn.readNetFromCaffe (protoPath, modelPath)

# video dosyası akışına bir işaretçi açın ve toplamı başlatın

Şimdiye kadar okunan ve kaydedilen kare sayısı

vs = cv2.VideoCapture (değiştirgeler)

oku = 0

kaydedildi = 0

23-26 satırları OpenCV'nin derin öğrenme yüz dedektörünü yükler (ilgili okuma: https://www.pyimagesearch.com/2018/02/26/face-detection-with-opencv-and-deep-learning/) .

Ardından 30. satırdaki video akışımızı açtık.

Hala döngüyü yürütürken, okunan çerçeve sayısı ve kaydedilen çerçeve sayısı için iki değişkeni (satır 31 ve 32) başlattık.

Ardından, bu kareleri işlemek için bir döngü oluşturacağız:

# video dosyası akışından kare üzerinden döngü

True iken:

# kareyi dosyadan al

(yakalandı, çerçeve) = vs. okunmuş

# Çerçeve yakalanmadıysa, sona ulaştık

akışın #

yakalanmadıysa:

kırmak

# o ana kadar okunan toplam çerçeve sayısını artırır

+ = 1 oku

# bu çerçeveyi işlememiz gerekip gerekmediğini kontrol edin

% args! = 0 okursanız:

devam et

35. satırdaki "while" döngüsünü başlatıyoruz.

Sonra, bir çerçeve aldık ve doğruladık (37-42. Satırlar).

Şu anda, bir çerçeve okuduğumuz için, "okuma" sayacının sayısını artıracağız (48. satır). Bu belirli çerçeveyi atlamamız gerekirse, bir sonraki döngüye hiçbir şey yapmadan devam edeceğiz (48 ve 49. satırlar).

Yüzleri tespit etmeye devam edelim:

# çerçeve boyutlarını alın ve çerçeveden bir damla oluşturun

(h, w) = çerçeve.shape

blob = cv2.dnn.blobFromImage (cv2.resize (çerçeve, (300, 300)), 1.0,

(300, 300), (104.0, 177.0, 123.0))

# blob'u ağ üzerinden geçirin ve algılamaları alın ve

# tahmin

net.setInput (blob)

algılamalar = net.forward

# en az bir yüz bulunduğundan emin olun

eğer len (tespitler) > 0:

# her görüntünün yalnızca BİR tane olduğunu varsayıyoruz

# yüz, öyleyse en büyük olasılığa sahip sınırlayıcı kutuyu bulun

i = np.argmax (algılamalar)

güven = tespitler

Yüz algılama gerçekleştirmek için görüntüden bir damla oluşturmamız gerekir (53 ve 54. satırlar). Caffe yüz dedektörüne uyum sağlamak için, bu "blob" un genişliği ve yüksekliği 300 * 300'dür. Sınırlayıcı kutuyu daha sonra ölçeklendirmemiz gerekiyor, bu yüzden 52. satırda çerçevenin boyutlarını alacağız.

58 ve 59. satırlar bu "blob" u derin öğrenme yüz detektöründen geçirir.

Komut dosyamız, videonun her karesinde (62-65. Satırlar) yalnızca bir yüz olduğunu varsayar, bu da yanlış pozitifleri önlemeye yardımcı olur. Birden fazla yüzü olan bir video üzerinde çalışıyorsanız, mantığı buna göre ayarlamanızı öneririm.

Bu nedenle, 65. satır en yüksek yüz algılama endeksini alır. 66. satır, testin güvenirliğini çıkarmak için dizini kullanır.

Ardından, zayıf yüz algılama sonuçlarını filtreleyelim ve yüzün yatırım getirisini diske yazalım:

# en büyük olasılıkla algılamanın da

# minimum olasılık testimiz anlamına gelir (böylece filtrelemeye yardımcı olur

# zayıf algılama)

güven varsa > argümanlar:

# için sınırlayıcı kutunun (x, y) koordinatlarını hesaplayın

# yüz ve yüz ROI'sini çıkarın

kutu = algılamalar * np.array ()

(startX, startY, endX, endY) = box.astype ("int")

yüz = çerçeve

# çerçeveyi diske yaz

p = os.path.sep.join ()

cv2.imwrite (p, yüz)

kaydedildi + = 1

print ("{} diske kaydedildi" .format (p))

# biraz temizlik yapın

vs sürüm

cv2.destroyAllWindows

Satır 71, yüz algılama ROI'mizin yanlış pozitifleri azaltmak için gereken minimum eşiği karşılamasını sağlar.

Bu temelde, yüzün ROI sınırlayıcı kutusu "kutu" koordinatlarını ve karşılık gelen yüz ROI değerini (74-76 satırları) çıkardık.

Yüz ROI'si için bir "yol + dosya adı" oluşturduk ve 79-81. Satırlarda diske yazdık. Burada "kurtulan" kişilerin sayısını artırabiliriz.

İşlemden sonra OpenCV'nin çalışma belleğini 86 ve 87. satırlarda temizleyeceğiz.

Canlılık algılama görüntü veri setimizi oluşturun

Şekil 4: OpenCV yüz canlı algılama veri setimiz. Canlı algılama modelinin tanıtım örneğini eğitmek için Keras ve OpenCV'yi kullanacağız.

Kaynak kodunu ve giriş video örneklerini almak için bu eğiticinin "İndir" bölümündeki bağlantıyı kullandığınızdan emin olun.

Bu temelde, lütfen bir terminal açın ve "sahtecilik / aldatıcı" kategorimiz için gerekli yüz görüntüsünü çıkarmak için aşağıdaki komutu çalıştırın:

$ python collect_examples.py --input videos / real.mov --output veri kümesi / gerçek \

--detector face_detector - 1 atla

yüz dedektörü yükleniyor ...

kaydedilmiş veri kümeleri / sahte / 0.png diske

kaydedilmiş veri kümeleri / sahte / 1.png diske

kaydedilmiş veri kümeleri / sahte / 2.png diske

kaydedilmiş veri kümeleri / sahte / 3.png diske

kaydedilmiş veri kümeleri / sahte / 4.png diske

kaydedilmiş veri kümeleri / sahte / 5.png diske

...

kaydedilmiş veri kümeleri / sahte / 145.png diske

kaydedilmiş veri kümeleri / sahte / 146.png diske

kaydedilmiş veri kümeleri / sahte / 147.png diske

kaydedilmiş veri kümeleri / sahte / 148.png diske

kaydedilmiş veri kümeleri / sahte / 149.png diske

Benzer şekilde, "gerçek" kategorinin yüz görüntüsünü elde etmek için aynı işlemi gerçekleştirebiliriz:

$ python collect_examples.py --input videos / fake.mov --output veri seti / sahte \

--detector face_detector --skip 4

yüz dedektörü yükleniyor ...

kaydedilmiş veri kümeleri / real / 0.png diske

kaydedilmiş veri kümeleri / real / 1.png diske

kaydedilmiş veri kümeleri / real / 2.png diske

kaydedilmiş veri kümeleri / real / 3.png diske

kaydedilmiş veri kümeleri / real / 4.png diske

...

kaydedilmiş veri kümeleri / real / 156.png diske

kaydedilmiş veri kümeleri / real / 157.png diske

kaydedilmiş veri kümeleri / real / 158.png diske

kaydedilmiş veri kümeleri / real / 159.png diske

kaydedilmiş veri kümeleri / real / 160.png diske

"Gerçek" yüz videosu, "sahte" yüz video dosyasından daha uzun olduğundan, her bir çıktı türü için yüz ROI sayısını dengelemek için daha uzun bir çerçeve atlama değeri kullanacağız.

Yukarıdaki komut dosyasını çalıştırdıktan sonra, aşağıda gösterildiği gibi görüntü verilerini elde etmiş olmalısınız:

  • Sahte insan yüzleri: 150 resim

  • Gerçek yüzler: 161 görüntü

  • Toplam: 311 resim

Derin öğrenme canlılığı dedektörümüz "LivenessNet" i uygulayın

Şekil 5: Görüntülerde ve videolarda canlı yüzleri algılamak için tasarlanmış evrişimli bir sinir ağı (CNN) olan LivenessNet'in derin öğrenme mimarisi.

Daha sonra, derin öğrenme teknolojisine dayalı canlılık dedektörü "LivenessNet" i uygulayacağız.

Özünde, "LivenessNet" basit bir evrişimli sinir ağıdır.

Aşağıdaki iki nedenden dolayı, kasıtlı olarak ağı olabildiğince sığ ve olabildiğince az parametre tutacağız:

1. Küçük veri setimize aşırı uyum olasılığını azaltmak için.

2. Canlı dedektörümüzün hızlı ve gerçek zamanlı çalışabilmesini sağlamak için (Raspberry Pi gibi sınırlı bilgi işlem kaynaklarına sahip cihazlarda bile).

Şimdi "LivenessNet" i uygulamaya başlayacağız, lütfen "livenessnet.py" dosyasını açın ve aşağıdaki kodu ekleyin:

# gerekli paketleri içe aktarın

keras.models'den Sıralı içe aktarma

keras.layers.normalization'dan import BatchNormalization

keras.layers.convolutional import Conv2D'den

keras.layers.convolutional import MaxPooling2D'den

keras.layers.core'dan içe aktarma Etkinleştirme

keras.layers.core'dan içe aktarma Düzleştir

keras.layers.core'dan import Dropout

keras.layers.core'dan import Dense

keras'tan arka ucu K olarak içe aktar

sınıf LivenessNet:

@hayalhanemersin

def build (genişlik, yükseklik, derinlik, sınıflar):

# modeli giriş şekli ile birlikte başlat

# "kanal kalıcıdır" ve kanalın boyutu

model = Sıralı

inputShape = (yükseklik, genişlik, derinlik)

chanDim = -1

# "önce kanalları" kullanıyorsak, giriş şeklini güncelleyin

# ve kanal boyutu

K.image_data_format == "channel_first" ise:

inputShape = (derinlik, yükseklik, genişlik)

chanDim = 1

Keras paketinden gerekli tüm yöntemleri içe aktaracağız (2-10. Satırlar). Her bir ağ katmanı ve işlevi hakkında daha fazla bilgi edinmek istiyorsanız, lütfen "Python ile Bilgisayar Görü için Derin Öğrenme" (URL: https://www.pyimagesearch.com/deep-learning-computer-vision-python-book/ ).

12. satırda "LivenessNet" sınıfını tanımlamaya başlıyoruz. Statik bir kurucu "yapı" (satır 14) içerir. "Derleme" yöntemi dört girdi parametresini kabul eder:

  • "Genişlik": resmin / videonun genişliği

  • "Yükseklik": görüntünün yüksekliği

  • "Derinlik": görüntünün kanal sayısı (RGB görüntüleri kullanacağımız için bu örnekte derinlik 3'tür).

  • "Sınıflar": Algılama kategorilerinin sayısı. İki tür çıktı yüzümüz var: "gerçek" yüzler ve "sahte" yüzler.

17. satırda "model" i başlatıyoruz.

18. satır, modelin "inputShape" i tanımlar ve 23-25. Satırlar kanalların sırasını belirtir.

Aşağıda, evrişimli sinir ağına bazı ağ katmanı bileşenleri ekleyeceğiz:

# ilk DÖNÜŞ = > RELU = > CONV = > RELU = > HAVUZ katman seti

model.add (Conv2D (16, (3, 3), padding = "aynı",

input_shape = inputShape))

model.add (Etkinleştirme ("relu"))

model.add (BatchNormalization (eksen = chanDim))

model.add (Conv2D (16, (3, 3), dolgu = "aynı"))

model.add (Etkinleştirme ("relu"))

model.add (BatchNormalization (eksen = chanDim))

model.add (MaxPooling2D (pool_size = (2, 2)))

model.add (Bırakma (0,25))

# saniye CONV = > RELU = > CONV = > RELU = > HAVUZ katman seti

model.add (Conv2D (32, (3, 3), dolgu = "aynı"))

model.add (Etkinleştirme ("relu"))

model.add (BatchNormalization (eksen = chanDim))

model.add (Conv2D (32, (3, 3), dolgu = "aynı"))

model.add (Etkinleştirme ("relu"))

model.add (BatchNormalization (eksen = chanDim))

model.add (MaxPooling2D (pool_size = (2, 2)))

model.add (Bırakma (0,25))

Evrişimli sinir ağımız, VGGNet'in bazı özelliklerini gösterir. Sadece birkaç öğrenilmiş evrişim çekirdeği ile çok sığdır. İdeal olarak, gerçek yüzleri sahte yüzlerden ayırmak için derin bir ağ kullanmamız gerekmez.

28-36. Satırlar ilk "CONV = > RELU = > CONV = > RELU = > POOL "(evrişimli katman = > RELU aktivasyon işlevi katmanı = > Evrişimli katman = > RELU aktivasyon işlevi katmanı = > Havuzlama katmanı) Toplu normalleştirme katmanı ve bırakma katmanı da ekleyen ağ katmanı toplama.

39-46. Satırlar başka bir "CONV = > RELU = > CONV = > RELU = > POOL "(evrişimli katman = > RELU aktivasyon işlevi katmanı = > Evrişimli katman = > RELU aktivasyon işlevi katmanı = > Havuzlama katmanı) ağ katmanı koleksiyonu.

Sonunda "FC = > RELU "(tam bağlantı = > RELU aktivasyon işlevi katmanı) katmanı:

# ilk (ve tek) FC seti = > RELU katmanları

model.add (Düzleştir)

model.add (Yoğun (64))

model.add (Etkinleştirme ("relu"))

model.add (BatchNormalization)

model.add (Dropout (0.5))

# softmax sınıflandırıcı

model.add (Yoğun (sınıflar))

model.add (Etkinleştirme ("softmax"))

# inşa edilen ağ mimarisini döndür

dönüş modeli

49-57 satırları, tamamen bağlı bir katmandan, bir ReLU aktivasyon işlevi katmanından ve bir softmax sınıflandırıcı başlığından oluşur.

Modeli, 60. satırdaki eğitim komut dosyasına geri döndürün.

Canlı algılayıcı için bir eğitim komut dosyası oluşturun

Şekil 6: LivenessNet eğitim süreci. Veri setimiz olarak hem "gerçek" yüzleri hem de "aldatıcı / sahte" yüz görüntülerini kullanarak, canlı bir algılama modeli eğitmek için OpenCV, Keras çerçevesi ve derin öğrenme teknolojisini kullanabiliriz.

Gerçek / aldatıcı yüz görüntüsü veri setimiz ve LivenessNet uygulamamız göz önüne alındığında, artık ağı eğitmeye hazırız:

Lütfen "train_liveness.py" dosyasını açın ve aşağıdaki kodu ekleyin:

# matplotlib arka ucunu ayarlayın, böylece rakamlar arka planda kaydedilebilir

ithal matplotlib

matplotlib.use ("Agg")

# gerekli paketleri içe aktarın

pyimagesearch.livenessnet'ten LivenessNet'i içe aktarın

sklearn.preprocessing adresinden LabelEncoder'ı içe aktar

sklearn.model_selection'dan import train_test_split

sklearn.metrics'den sınıflandırma içe aktarma

keras.preprocessing.image adresinden ImageDataGenerator içe aktarın

keras.optimizers'dan Adam'ı içe aktarın

keras.utils'ten np_utils'i içe aktar

imutils'den içe aktarma yollarından

matplotlib.pyplot dosyasını plt olarak içe aktar

numpy'yi np olarak içe aktar

ithal argparse

ithal turşu

cv2 içe aktar

işletim sistemini içe aktar

# bağımsız değişken çözümleyiciyi oluşturun ve bağımsız değişkenleri çözümleyin

ap = argparse.ArgumentParser

ap.add_argument ("- d", "--dataset", gerekli = Doğru,

help = "giriş veri kümesine giden yol")

ap.add_argument ("- m", "--model", tür = str, gerekli = Doğru,

help = "eğitilmiş modele giden yol")

ap.add_argument ("- l", "--le", tür = str, gerekli = Doğru,

help = "etiket kodlayıcı yolu")

ap.add_argument ("- p", "--plot", type = str, default = "plot.png",

help = "çıktı kaybı / doğruluk grafiğine giden yol")

args = vars (ap.parse_args)

Canlı yüz algılama komut dosyamız çok sayıda girdi sağlar (2-19. Satırlar). sırasıyla şunlardır:

  • "Matplotlib": Eğitim durumunun şematik bir diyagramını oluşturmak için kullanılır. 3. satırda, diyagramı diske kolayca yazabilmemiz için arka ucu "Agg" olarak belirtiyoruz.

"LivenessNet": Önceki bölümde tanımladığımız canlılığı algılayan evrişimli sinir ağı.

  • "Train_test_split": Eğitim ve test için bir veri bölümü oluşturan, scikit-learn'den tanıtılan bir işlev.

  • "Sınıflandırma_raporu": Scikit-learn'den getirilen ve modelimizin performansını açıklayan kısa bir istatistiksel rapor oluşturan bir işlev.

  • "ImageDataGenerator": Veri iyileştirme için kullanılır ve bize rastgele mutasyonla geliştirilmiş görüntü grupları sağlar.

  • "Adam": Bu model için çok uygun bir iyileştirici (isteğe bağlı seçenekler arasında Stokastik Gradyan İniş (SGD), RMSprop, vb. Bulunur).

  • "Yollar": "imutils" paketimden tanıtılan bu modül, diskteki tüm görüntü dosyalarının yollarını toplamamıza yardımcı olabilir.

  • "Pyplot": Güzel bir eğitim süreci diyagramı oluşturmak için kullanılır.

  • "Numpy": OpenCV için de gerekli olan Python için bir dijital işleme geliştirme kitaplığı.

  • "Argparse": komut satırı bağımsız değişkenlerini işlemek için kullanılır (ilgili okuma: https://www.pyimagesearch.com/2018/03/12/python-argparse-command-line-arguments/)

  • "Pickle": Etiket kodlayıcımızı serileştirmek ve diske yazmak için kullanılır.

  • "Cv2": OpenCV'yi bağlamak için kullanılır.

  • "Os": Bu modülün birçok işlevi vardır, ancak burada onu yalnızca işletim sistemi yol ayırıcıları oluşturmak için kullanıyoruz.

Giriş biraz fazla görünüyor, ancak bu girdilerin amacını anladıktan sonra, komut dosyasının geri kalanını kontrol etmek daha sezgisel olmalıdır.

Komut dosyası dört komut satırı parametresini kabul eder:

  • "--Veri Kümesi": Veri kümesinin yolunu girin. Bu veri kümesi, bu makalenin önceki bölümlerinde bulunan "collect_examples.py" komut dosyası kullanılarak oluşturuldu.

  • "--Model": Komut dosyamız bir çıktı modeli dosyası oluşturacak ve yolunu bu parametre aracılığıyla belirleyecektir.

  • "--Le": Serileştirilmiş etiket kodlayıcı dosyasını çıkarmak için gereken yol.

  • "--Plot": Eğitim komut dosyası, eğitim sürecinin şematik bir diyagramını oluşturacaktır. "Plot.png" varsayılan değerini geçersiz kılmak istiyorsanız, değeri komut satırında belirtmelisiniz.

Aşağıdaki kod bloğu bir dizi başlatma çalışması gerçekleştirecek ve verilerimizi oluşturacaktır:

# ilk öğrenme oranını, parti boyutunu ve

Eğitim yapılacak # dönem

INIT_LR = 1e-4

BS = 8

EPOCHS = 50

# veri kümesi dizinimizdeki görüntülerin listesini alın, ardından başlatın

# veri listesi (ör. resimler) ve sınıf resimleri

baskı ("resimler yükleniyor ...")

imagePaths = list (paths.list_images (değiştirgeler))

data =

etiketler =

imagePaths'te imagePath için:

# dosya adından sınıf etiketini çıkarın, resmi yükleyin ve

# en boy oranını göz ardı ederek sabit bir 32x32 piksel olacak şekilde yeniden boyutlandırın

label = imagePath.split (os.path.sep)

image = cv2.imread (imagePath)

image = cv2.resize (resim, (32, 32))

# sırasıyla veri ve etiket listelerini güncelleyin

data.append (resim)

tags.append (etiket)

# verileri bir NumPy dizisine dönüştürün, ardından ölçeklendirerek önceden işleyin

# aralığa göre tüm piksel yoğunlukları

data = np.array (data, dtype = "float") / 255.0

35-37. Satırlarda ayarlanan eğitim parametreleri, öğrenme oranını, parti boyutunu ve yineleme sayısını içerir.

Bu noktada, "iamgePaths" içindeki yolu elde ettik ve ayrıca "veri" ve sınıf "etiketleri" (42-44. Satırlar) depolamak için iki listeyi başlattık.

46-55. Satırlardaki döngü bir "veri" ve "etiket" listesi yaratır. "Veri" listesi yüklediğimiz ve 32 * 32 piksel olarak yeniden boyutlandırdığımız görüntülerden oluşur. Her görüntünün "etiketler" listesinde saklanan karşılık gelen bir etiketi vardır.

Her pikselin parlaklığı aralığa göre ölçeklendi ve listeyi 59. satırdaki NumPy dizisine dönüştürdük.

Sonra, etiketleri kodlayıp verileri böleceğiz:

# etiketleri (şu anda dizelerdir) tamsayı olarak kodlayın ve ardından

# onları tek seferde kodlayın

le = LabelEncoder

etiketler = le.fit_transform (etiketler)

etiketler = np_utils.to_categorical (etiketler, 2)

#% 75'i kullanarak verileri eğitim ve test bölümlerine ayırın

# eğitim verileri ve kalan% 25 test için

(trainX, testX, trainY, testY) = train_test_split (veriler, etiketler,

test_size = 0.25, random_state = 42)

63-65 satırları etiketi tek sıcak vektör olarak kodlar.

Verileri bölmek için scikit-learn kullanıyoruz - verilerin% 75'i eğitim için kullanılıyor ve kalan% 25'i test için ayrılıyor (satır 69 ve 70).

Daha sonra, veri artırma nesnemizi başlatacağız ve canlı yüz algılama modelimizi derleyip eğiteceğiz:

# Veri büyütme için eğitim görüntü oluşturucusunu oluşturun

aug = ImageDataGenerator (rotation_range = 20, zoom_range = 0,15,

width_shift_range = 0.2, height_shift_range = 0.2, kesme_aralığı = 0.15,

horizontal_flip = Doğru, fill_mode = "en yakın")

# optimize ediciyi ve modeli başlat

baskı ("model derleniyor ...")

opt = Adam (lr = INIT_LR, bozunma = INIT_LR / EPOCHS)

model = LivenessNet.build (genişlik = 32, yükseklik = 32, derinlik = 3,

sınıflar = len (le.classes_))

model.compile (loss = "binary_crossentropy", optimize edici = opt,

metrics =)

# ağı eğitin

print ("{} dönem için eğitim ağı ...". format (EPOCHS))

H = model.fit_generator (aug.flow (trainX, trainY, batch_size = BS),

validation_data = (testX, testY), steps_per_epoch = len (trainX) // BS,

epochs = EPOCHS)

73-75. satırlar, rasgele döndürme, ölçekleme, çevirme, kesme ve çevirme yoluyla yeni bir görüntü oluşturacak, verileri zenginleştirilmiş bir nesne oluşturur. Veri büyütme hakkında daha fazla bilgi edinmek için lütfen şu blog gönderisine bakın: https://www.pyimagesearch.com/2018/12/24/how-to-use-keras-fit-and-fit_generator-a- uygulamalı eğitim /

79-83. Satırlarda "LivenessNet" modelini oluşturduk ve derledik.

Daha sonra, modeli 87-89. Satırlarda eğitmeye başlıyoruz. Sığ ağlar ve küçük veri kümeleri kullandığımız için, bu süreç nispeten hızlı olacaktır.

Model eğitimi tamamlandığında, eğitim sonuçlarını değerlendirebilir ve eğitim sürecinin şematik bir diyagramını oluşturabiliriz:

# ağı değerlendirin

print ("ağ değerlendiriliyor ...")

tahminler = model.predict (testX, batch_size = BS)

print (sınıflandırma_raporu (testY.argmax (eksen = 1),

tahminler.argmax (axis = 1), target_names = le.classes_))

# ağı diske kaydedin

print ("ağ '{}' ...". format (değiştirgeler) olarak serileştiriliyor)

model.save (değiştirgeler)

# etiket kodlayıcıyı diske kaydedin

f = açık (bağımsız değişkenler, "wb")

f.write (pickle.dumps (le))

f.kapat

# eğitim kaybını ve doğruluğunu çizin

plt.style.use ("ggplot")

plt.figure

plt.plot (np.arange (0, EPOCHS), H.history, label = "tren_ kaybı")

plt.plot (np.arange (0, EPOCHS), H.history, label = "değer_ kaybı")

plt.plot (np.arange (0, EPOCHS), H.history, label = "train_acc")

plt.plot (np.arange (0, EPOCHS), H.history, label = "val_acc")

plt.title ("Veri Kümesinde Eğitim Kaybı ve Doğruluğu")

plt.xlabel ("Dönem #")

plt.ylabel ("Kayıp / Doğruluk")

plt.legend (loc = "alt sol")

plt.savefig (değiştirgeler)

93. satır, test setinde elde edilen tahmin sonuçlarını gösterir. Buna bağlı olarak, sınıflandırma sonucu raporu "sınıflandırma_raporu" oluşturulur ve terminalde görüntülenir (satır 94 ve 95).

"LivenessNet" modeli ve etiket kodlayıcı serileştirilir ve 99-104 satırlarında diske yazılır.

Kalan satır 107-117, gözden geçirme için eğitim sürecinin şematik bir diyagramını oluşturur.

Canlılık dedektörümüzü eğitmek

Şimdi, canlı dedektörü eğitmeye hazırız.

Lütfen bu eğiticinin "İndir" bölümündeki bağlantılardan kaynak kodunu ve veri kümesini indirdiğinizden emin olun. Ardından, lütfen aşağıdaki komutunuzu uygulayın:

$ python train.py --dataset veri kümesi --model liveness.model --le le.pickle

resimler yükleniyor ...

model derleniyor ...

50 devir eğitim ağı ...

1/50 Yaş

29/29 - 2s 58ms/step - loss: 1.0113 - acc: 0.5862 - val_loss: 0.4749 - val_acc: 0.7436

Epoch 2/50

29/29 - 1s 21ms/step - loss: 0.9418 - acc: 0.6127 - val_loss: 0.4436 - val_acc: 0.7949

Epoch 3/50

29/29 - 1s 21ms/step - loss: 0.8926 - acc: 0.6472 - val_loss: 0.3837 - val_acc: 0.8077

...

Epoch 48/50

29/29 - 1s 21ms/step - loss: 0.2796 - acc: 0.9094 - val_loss: 0.0299 - val_acc: 1.0000

Epoch 49/50

29/29 - 1s 21ms/step - loss: 0.3733 - acc: 0.8792 - val_loss: 0.0346 - val_acc: 0.9872

Epoch 50/50

29/29 - 1s 21ms/step - loss: 0.2660 - acc: 0.9008 - val_loss: 0.0322 - val_acc: 0.9872

evaluating network...

precision recall f1-score support

fake 0.971.000.9935

real 1.000.980.9943

micro avg 0.990.990.9978

macro avg 0.990.990.9978

weighted avg 0.990.990.9978

serializing network to 'liveness.model'...

6 OpenCVKeras

99%

OpenCV

7 OpenCV

1. /

2.

3.

liveness_demo.py

# import the necessary packages

from imutils.video import VideoStream

from keras.preprocessing.image import img_to_array

from keras.models import load_model

numpy'yi np olarak içe aktar

import argparse

import imutils

ithal turşu

ithalat zamanı

import cv2

işletim sistemini içe aktar

# construct the argument parse and parse the arguments

ap = argparse.ArgumentParser

ap.add_argument("-m", "--model", type=str, required=True,

help="path to trained model")

ap.add_argument("-l", "--le", type=str, required=True,

help="path to label encoder")

ap.add_argument("-d", "--detector", type=str, required=True,

help="path to OpenCV's deep learning face detector")

ap.add_argument("-c", "--confidence", type=float, default=0.5,

help="minimum probability to filter weak detections")

args = vars(ap.parse_args)

2-11

  • VideoStream

  • img_to_array

  • load_model Keras

  • imutils

  • cv2 OpenCV

14-23

  • --model Keras

  • --le

  • --detector ROI OpenCV

  • --confidence

LivenessNet +

# load our serialized face detector from disk

print(" loading face detector...")

protoPath = os.path.sep.join()

modelPath = os.path.sep.join()

net = cv2.dnn.readNetFromCaffe(protoPath, modelPath)

# load the liveness detector model and label encoder from disk

print(" loading liveness detector...")

model = load_model(args)

le = pickle.loads(open(args, "rb").read)

# initialize the video stream and allow the camera sensor to warmup

print(" starting video stream...")

vs = VideoStream(src=0).start

time.sleep(2.0)

27-30 OpenCV

LivenessNet 34-35

39 40 VideoStream 2

/

# loop over the frames from the video stream

True iken:

# grab the frame from the threaded video stream and resize it

# to have a maximum width of 600 pixels

frame = vs.read

frame = imutils.resize(frame, width=600)

# grab the frame dimensions and convert it to a blob

(h, w) = frame.shape

blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0,

(300, 300), (104.0, 177.0, 123.0))

# pass the blob through the network and obtain the detections and

# predictions

net.setInput(blob)

detections = net.forward

43 while46 47

50

OpenCV blobFromImagehttps://www.pyimagesearch.com/2017/11/06/deep-learning-opencvs-blobfromimage-works/blob 51 52 56 57

OpenCV

# loop over the detections

for i in range(0, detections.shape):

# extract the confidence (i.e., probability) associated with the

# prediction

confidence = detections

# filter out weak detections

if confidence > args:

# compute the (x, y)-coordinates of the bounding box for

# the face and extract the face ROI

box = detections * np.array()

(startX, startY, endX, endY) = box.astype("int")

# ensure the detected bounding box does fall outside the

# dimensions of the frame

startX = max(0, startX)

startY = max(0, startY)

endX = min(w, endX)

endY = min(h, endY)

# extract the face ROI and then preproces it in the exact

# same manner as our training data

face = frame

face = cv2.resize(face, (32, 32))

face = face.astype("float") / 255.0

face = img_to_array(face)

face = np.expand_dims(face, axis=0)

# pass the face ROI through the trained liveness detector

# model to determine if the face is "real" or "fake"

preds = model.predict(face)

j = np.argmax(preds)

label = le.classes_

# draw the label and bounding box on the frame

label = "{}: {:.4f}".format(label, preds)

cv2.putText(frame, label, (startX, startY - 10),

cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)

cv2.rectangle(frame, (startX, startY), (endX, endY),

(0, 0, 255), 2)

60

  • 63-66

  • box 69-77

  • ROI 81-85

  • / 89-91

  • 91 if label == "real": run_face_reconition 91

  • labelrectangle 94-98

# show the output frame and wait for a key press

cv2.imshow("Frame", frame)

key = cv2.waitKey(1) and 0xFF

# if the `q` key was pressed, break from the loop

if key == ord("q"):

kırmak

# do a bit of cleanup

cv2.destroyAllWindows

vs.stop

101-102 qquit 105-110

$ python liveness_demo.py --model liveness.model --le le.pickle \

--detector face_detector

Using TensorFlow backend.

loading face detector...

loading liveness detector...

starting video stream...

311 161 150

/

/

/

)

sonuç olarak

OpenCV

OpenCV Python

vs

1.

2. /

3.

LivenessNet Keras

1.

2.

99%

Python + OpenCV

OpenCV

https://www.getdrip.com/forms/321809846/submissions

via https://www.pyimagesearch.com/2019/03/11/liveness-detection-with-opencv

Tıklamak Orijinali okuyun

Xi Mengyao güreşi, He Sui haberi verdi, Çin'deki Victoria's Secret Show'un canlı yayınında "büyük şeyler" neler oldu?
önceki
İki yılda 10 siparişNASA fakir, SpaceX yemeği, kim kimin kalçasını tutuyor?
Sonraki
Aylık 4000 maaşla da uygun fiyatlı olan bu arabalar, yaşam kalitenizi N seviyeleri kadar artırabilir.
Bakış Açısı | Google Brain'de çalıştığım 18 ay boyunca pekiştirmeli öğrenmeyi nasıl araştırdım?
Hanteng X5'in ön satışı iki ayda 10.000'i aştı ve Hanteng'in yıllık üretim kapasitesi önümüzdeki yıl 450.000'e çıkarılacak.
"Dancing Awakening", eleştiriler tarafından çok kaba, Lu Han'ın "Sıcak Kanlı Sokak Dansı Topluluğu" bunun üstesinden gelebilir mi?
Güveç gerçekten kültürsüz mü? Hot Pot Museum küratörü aynı fikirde değil: Chongqing gurmeleri onurlarını korumak için dışarı çıkmalı!
100.000 SUV her zaman bu kötü stillerdir, bir kişilik noktası olarak görmüyor musunuz?
Kaynaklar | GAN'ın aşamalı eğitim yöntemi PI-REC: Elle çizilmiş taslaklar hızlı bir şekilde eksiksiz görüntülere dönüştürülür
"Dancing Awakening", eleştiriler tarafından çok zordu, Lu Han'ın "Sıcak Kanlı Sokak Dansı Topluluğu" bunun üstesinden gelebilir mi?
31 yaşındaki bir anne, 4 yaşındaki oğlunu ağ kablosuyla boğdu! Hala kırmızı bir gelinlik giyiyor
Mazda CX-4 ve CX-5 nasıl seçilir? Bu makaleyi okuduktan sonra cevabınız var
Ulusal kamuflaj serisinin bir üyesi daha! Nike Air Max 97'nin ABD'yi nasıl yorumladığını görün!
Hangisini sevdin?
To Top