采用紅外熱成像輔助的人臉識別對活體甄別的算法

采用紅外熱成像輔助的人臉識別對活體甄別的算法

浙江某學校的一群小學生,在一次課外實驗中驚發(fā)現(xiàn),只要用一張打印照片就能代替真人刷臉横缔、騙過小區(qū)里的豐巢智能柜镜遣,取出父母們的貨件己肮,該視頻一出就引起了圍觀。


破解人臉

視頻中,一群小學生正圍著一個豐巢快遞柜谎僻,其中一名小男孩用手里的打印照片對著快遞柜一掃娄柳,就完成了人臉識別。聽上去高精尖的人臉識別艘绍,居然被一群小學生給破解了


破解人臉

其實,我們目前的人臉識別系統(tǒng),大多數(shù)都是采用:圖像采集-->人臉檢測-->人臉識別,這幾個步驟,送到識別系統(tǒng)的最終是裁剪好的人臉圖片,對于活體的檢測,并沒有太多算法. 百度的人臉識別中有卡通臉的識別,主要是對臉部的圖片的層次進行分析,照片和圖片的層次和實際人員的圖像會有些不同,但是這些檢測算法對于,稍作處理的圖片或動態(tài)的圖像就會很遜色.能不能找到一種更加精確的方法來識別活體和假體人像的方法呢?

大家知道,絕對零度的物體都會對外輻射發(fā)光,在我們常溫范圍(0-80攝氏度),這個范圍輻射的光正處在紅外波段,采用紅外人成像技術就可以把圖像中的活體人像采集出來,通過可見光的采集的圖像(攝像頭)和紅外線采集的圖像(紅外熱成像傳感器)進行比對,用于判斷我們采集的圖像是否為活體.

紅外熱成像

1. 紅外熱成像

我們需要對采集的人臉特征做判斷,一張紅外熱成像的圖片,既有大致的形狀特征,也有基于顏色的溫標(一般越藍溫度越低,越紅溫度越高).
這個就是我使用AMG8833的紅外熱釋傳感器做的一套紅外熱釋成像的小玩具.這個傳感器是8x8的像素,雖然分辨率低,但是對大致的形狀和特征還是可以識別的
對于溫度的識別還是不錯的,關鍵是這個組件成本很低,某寶上我用了不到300人民幣就可以買到.顯示部分正好那基于esp32的M5小盒子做的.

這張照片,對著一杯茶水,看著屏幕上一大團紅色,沒錯,我喜歡喝熱茶.

代碼也比較簡單

#include <M5Stack.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_AMG88xx.h>
//low range of the sensor (this will be blue on the screen)
byte MINTEMP = 20;

//high range of the sensor (this will be red on the screen)
byte MAXTEMP = 32;

//the colors we will be using
const uint16_t camColors[] = {0x480F,
                              0x400F, 0x400F, 0x400F, 0x4010, 0x3810, 0x3810, 0x3810, 0x3810, 0x3010, 0x3010,
                              0x3010, 0x2810, 0x2810, 0x2810, 0x2810, 0x2010, 0x2010, 0x2010, 0x1810, 0x1810,
                              0x1811, 0x1811, 0x1011, 0x1011, 0x1011, 0x0811, 0x0811, 0x0811, 0x0011, 0x0011,
                              0x0011, 0x0011, 0x0011, 0x0031, 0x0031, 0x0051, 0x0072, 0x0072, 0x0092, 0x00B2,
                              0x00B2, 0x00D2, 0x00F2, 0x00F2, 0x0112, 0x0132, 0x0152, 0x0152, 0x0172, 0x0192,
                              0x0192, 0x01B2, 0x01D2, 0x01F3, 0x01F3, 0x0213, 0x0233, 0x0253, 0x0253, 0x0273,
                              0x0293, 0x02B3, 0x02D3, 0x02D3, 0x02F3, 0x0313, 0x0333, 0x0333, 0x0353, 0x0373,
                              0x0394, 0x03B4, 0x03D4, 0x03D4, 0x03F4, 0x0414, 0x0434, 0x0454, 0x0474, 0x0474,
                              0x0494, 0x04B4, 0x04D4, 0x04F4, 0x0514, 0x0534, 0x0534, 0x0554, 0x0554, 0x0574,
                              0x0574, 0x0573, 0x0573, 0x0573, 0x0572, 0x0572, 0x0572, 0x0571, 0x0591, 0x0591,
                              0x0590, 0x0590, 0x058F, 0x058F, 0x058F, 0x058E, 0x05AE, 0x05AE, 0x05AD, 0x05AD,
                              0x05AD, 0x05AC, 0x05AC, 0x05AB, 0x05CB, 0x05CB, 0x05CA, 0x05CA, 0x05CA, 0x05C9,
                              0x05C9, 0x05C8, 0x05E8, 0x05E8, 0x05E7, 0x05E7, 0x05E6, 0x05E6, 0x05E6, 0x05E5,
                              0x05E5, 0x0604, 0x0604, 0x0604, 0x0603, 0x0603, 0x0602, 0x0602, 0x0601, 0x0621,
                              0x0621, 0x0620, 0x0620, 0x0620, 0x0620, 0x0E20, 0x0E20, 0x0E40, 0x1640, 0x1640,
                              0x1E40, 0x1E40, 0x2640, 0x2640, 0x2E40, 0x2E60, 0x3660, 0x3660, 0x3E60, 0x3E60,
                              0x3E60, 0x4660, 0x4660, 0x4E60, 0x4E80, 0x5680, 0x5680, 0x5E80, 0x5E80, 0x6680,
                              0x6680, 0x6E80, 0x6EA0, 0x76A0, 0x76A0, 0x7EA0, 0x7EA0, 0x86A0, 0x86A0, 0x8EA0,
                              0x8EC0, 0x96C0, 0x96C0, 0x9EC0, 0x9EC0, 0xA6C0, 0xAEC0, 0xAEC0, 0xB6E0, 0xB6E0,
                              0xBEE0, 0xBEE0, 0xC6E0, 0xC6E0, 0xCEE0, 0xCEE0, 0xD6E0, 0xD700, 0xDF00, 0xDEE0,
                              0xDEC0, 0xDEA0, 0xDE80, 0xDE80, 0xE660, 0xE640, 0xE620, 0xE600, 0xE5E0, 0xE5C0,
                              0xE5A0, 0xE580, 0xE560, 0xE540, 0xE520, 0xE500, 0xE4E0, 0xE4C0, 0xE4A0, 0xE480,
                              0xE460, 0xEC40, 0xEC20, 0xEC00, 0xEBE0, 0xEBC0, 0xEBA0, 0xEB80, 0xEB60, 0xEB40,
                              0xEB20, 0xEB00, 0xEAE0, 0xEAC0, 0xEAA0, 0xEA80, 0xEA60, 0xEA40, 0xF220, 0xF200,
                              0xF1E0, 0xF1C0, 0xF1A0, 0xF180, 0xF160, 0xF140, 0xF100, 0xF0E0, 0xF0C0, 0xF0A0,
                              0xF080, 0xF060, 0xF040, 0xF020, 0xF800,
                             };

Adafruit_AMG88xx amg;
#define AMG_COLS 8
#define AMG_ROWS 8
float pixels[AMG_COLS * AMG_ROWS];
#define INTERPOLATED_COLS 24
#define INTERPOLATED_ROWS 24
int max_v = 0;
int min_v = 80;
float get_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y);
void set_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y, float f);
void get_adjacents_1d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y);
void get_adjacents_2d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y);
float cubicInterpolate(float p[], float x);
float bicubicInterpolate(float p[], float x, float y);
void interpolate_image(float *src, uint8_t src_rows, uint8_t src_cols, float *dest, uint8_t dest_rows, uint8_t dest_cols);
void setup()
{
  M5.begin();
  M5.setWakeupButton(BUTTON_B_PIN);
  M5.Lcd.begin();
  M5.Lcd.setRotation(0);
  M5.Lcd.fillScreen(TFT_BLACK);
  Serial.begin(115200);
  int icolor = 255;
  for (int irow = 16; irow <= 223;  irow++)
  {
    M5.Lcd.drawRect(0, 0, 35, irow, camColors[icolor]);
    icolor--;
  }
  infodisplay();
  if (!amg.begin())
  {
    while (1)
    {
      delay(1);
    }
  }
}

void loop() {
  if (M5.BtnA.pressedFor(1000)) {
    MINTEMP = min_v;
    min_v = 80;
    infodisplay();
  }

  if (M5.BtnA.wasPressed()) {
    if (MINTEMP <= 0)
    {
      MINTEMP = MAXTEMP - 1;
      infodisplay();
    }
    else
    {
      MINTEMP--;
      infodisplay();
    }
  }
  if (M5.BtnB.pressedFor(1000)) {
    M5.powerOFF();
  }

  if (M5.BtnC.pressedFor(1000)) {
    MAXTEMP = max_v;
    max_v = 0;
    infodisplay();
  }

  if (M5.BtnC.wasPressed()) {
    if (MAXTEMP >= 80)
    {
      MAXTEMP = MINTEMP + 1;
      infodisplay();
    }
    else
    {
      MAXTEMP++;
      infodisplay();
    }
  }
  M5.update();
  //read all the pixels
  amg.readPixels(pixels);

  for (int i = 1; i <= AMG88xx_PIXEL_ARRAY_SIZE; i++)
  {
  }
  float dest_2d[INTERPOLATED_ROWS * INTERPOLATED_COLS];

  interpolate_image(pixels, AMG_ROWS, AMG_COLS, dest_2d, INTERPOLATED_ROWS, INTERPOLATED_COLS);

  uint16_t boxsize = min(M5.Lcd.width() / INTERPOLATED_COLS, M5.Lcd.height() / INTERPOLATED_COLS);

  drawpixels(dest_2d, INTERPOLATED_ROWS, INTERPOLATED_COLS, boxsize, boxsize, false);
  max_v = INT_MIN;

  //  int max_i = 0;
  int spot_v = pixels[28];
  for ( int itemp = 0; itemp < sizeof(pixels) / sizeof(pixels[0]); itemp++ )
  {
    if ( pixels[itemp] > max_v )
    {
      max_v = pixels[itemp];
      //max_i = itemp;
    }

    if ( pixels[itemp] < min_v )
    {
      min_v = pixels[itemp];
      //max_i = itemp;
    }
  }
  M5.Lcd.setTextSize(2);
  M5.Lcd.fillRect(284, 18, 36, 16, TFT_BLACK);
  M5.Lcd.fillRect(284, 130, 36, 16, TFT_BLACK);
  M5.Lcd.setCursor(284, 18);
  M5.Lcd.setTextColor(TFT_WHITE);
  if (max_v > 80 | max_v < 0) {
    M5.Lcd.setTextColor(TFT_RED);
    M5.Lcd.printf("Err", 1);
  }
  else
  { M5.Lcd.print(max_v, 1);
    M5.Lcd.printf("C" , 1);
    M5.Lcd.setCursor(284, 130);
    M5.Lcd.print(spot_v, 1);
    M5.Lcd.printf("C" , 1);
    M5.Lcd.drawCircle(160, 120, 6, TFT_WHITE);
    M5.Lcd.drawLine(160, 110, 160, 130, TFT_WHITE);
    M5.Lcd.drawLine(150, 120, 170, 120, TFT_WHITE);
  }
}
/***infodisplay()*****/
void infodisplay(void) {
  M5.Lcd.setTextColor(TFT_WHITE);
  //     M5.Lcd.setCursor(288, 230);
  //   M5.Lcd.printf("Power" , 1);

  M5.Lcd.fillRect(0, 0, 36, 16, TFT_BLACK);
  M5.Lcd.setTextSize(2);
  M5.Lcd.setCursor(0, 1);
  M5.Lcd.print(MAXTEMP , 1);
  M5.Lcd.printf("C" , 1);
  M5.Lcd.setCursor(0, 225);
  M5.Lcd.fillRect(0, 225, 36, 16, TFT_BLACK);
  M5.Lcd.print(MINTEMP , 1);
  M5.Lcd.printf("C" , 1);
  M5.Lcd.setCursor(284, 0);
  M5.Lcd.printf("Max", 1);
  M5.Lcd.setCursor(284, 100);
  //  M5.Lcd.printf("Spot", 1);
  M5.Lcd.drawCircle(300, 120, 6, TFT_WHITE);
  M5.Lcd.drawLine(300, 110, 300, 130, TFT_WHITE);
  M5.Lcd.drawLine(290, 120, 310, 120, TFT_WHITE);
}

void drawpixels(float *p, uint8_t rows, uint8_t cols, uint8_t boxWidth, uint8_t boxHeight, boolean showVal) {
  int colorTemp;
  Serial.println('{');
  for (int y = 0; y < rows; y++) {
    for (int x = 0; x < cols; x++) {
      float val = get_point(p, rows, cols, x, y);
      if (val >= MAXTEMP) colorTemp = MAXTEMP;
      else if (val <= MINTEMP) colorTemp = MINTEMP;
      else colorTemp = val;

      uint8_t colorIndex = map(colorTemp, MINTEMP, MAXTEMP, 0, 255);
      colorIndex = constrain(colorIndex, 0, 255);
      //draw the pixels!
      uint16_t color;
      color = val * 2;
      Serial.print(camColors[colorIndex],DEC);
      Serial.print(',');      
      M5.Lcd.fillRect(40 + boxWidth * x, boxHeight * y, boxWidth, boxHeight, camColors[colorIndex]);
    }
  }
  Serial.println('}');
}

2.活體識別算法

我們這次需要判斷可見光的圖片的特征和紅外光的圖片特征,如果簡單的判斷溫度,不能很好的處理一些作假和偽造的行為.我們采用深度學習的算法,采集可見光的人像,同時采集紅外熱成像的圖片
,將圖片分類后訓練模型,通過訓練模型,以后再接受2張圖片:一張可見光的,一張紅外的,模型可以很好地預測出結果,有效判斷采集的圖像中人像是否為活體.

這個就是我的初步的思路.

應為主要做特征判斷,所以可見光圖像(人臉)可以使用128X128或者64X64,紅外光受限硬件只能是8x8的圖片.

深度學習模型這邊,因為是圖片,采用CNN(Convolutional Neural Networks)是個不錯的選擇, 圖片也不到cnn的層數(shù)也可以不用太多.
但是這個模型比較特殊,一般的模型都是一個INPUT和一個OUTPUT,這個模型需要2個INPUT.

這個需要我們搭建一個比較特殊的模型,有2個INPUT的CNN深度學習模型.

模型

代碼先簡單描述一下:

# 定義2個輸入
inputA = Input(shape=(8,8,3))
inputB = Input(shape=(128,128,3))
 
# 第一個輸入
x = Dense((8,8,3), activation="relu")(inputA)
x = Dense(4, activation="relu")(x)
x = Model(inputs=inputA, outputs=x)
 
# 第二個輸入
y = Dense(((64,64,3), activation="relu")(inputB)
y = Dense(32, activation="relu")(y)
y = Dense(4, activation="relu")(y)
y = Model(inputs=inputB, outputs=y)
 
# 合并2個輸入
combined = concatenate([x.output, y.output])
 

z = Dense(2, activation="relu")(combined)
z = Dense(1, activation="linear")(z)
 
# 整合到一個模型
model = Model(inputs=[x.input, y.input], outputs=z)

3.運行

模型訓練完成之后,系統(tǒng)同時采集可見光人像和紅外熱成像的圖片,把圖片送到模型中判斷,模型通過訓練可以有效的結合2各渠道的圖片推理出采集的圖像是否為活體.
理論上這個算法比單純依靠可見光的活算法,或者僅僅依靠紅外人成像溫度判斷要可靠地多.
當然這個還要靠實踐檢驗.

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末赤拒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子诱鞠,更是在濱河造成了極大的恐慌挎挖,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件航夺,死亡現(xiàn)場離奇詭異蕉朵,居然都是意外死亡,警方通過查閱死者的電腦和手機敷存,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門墓造,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锚烦,你說我怎么就攤上這事觅闽。” “怎么了涮俄?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵蛉拙,是天一觀的道長。 經常有香客問我彻亲,道長孕锄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任苞尝,我火速辦了婚禮畸肆,結果婚禮上,老公的妹妹穿的比我還像新娘宙址。我一直安慰自己轴脐,他們只是感情好,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布抡砂。 她就那樣靜靜地躺著大咱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪注益。 梳的紋絲不亂的頭發(fā)上碴巾,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天,我揣著相機與錄音丑搔,去河邊找鬼厦瓢。 笑死提揍,一個胖子當著我的面吹牛,可吹牛的內容都是我干的旷痕。 我是一名探鬼主播碳锈,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼欺抗!你這毒婦竟也來了售碳?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤绞呈,失蹤者是張志新(化名)和其女友劉穎贸人,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體佃声,經...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡艺智,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了圾亏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片十拣。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖志鹃,靈堂內的尸體忽然破棺而出夭问,到底是詐尸還是另有隱情,我是刑警寧澤曹铃,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布缰趋,位于F島的核電站,受9級特大地震影響陕见,放射性物質發(fā)生泄漏秘血。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一评甜、第九天 我趴在偏房一處隱蔽的房頂上張望灰粮。 院中可真熱鬧,春花似錦忍坷、人聲如沸谋竖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锤悄,卻和暖如春韧骗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背零聚。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工袍暴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留些侍,地道東北人盗似。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓嗡善,卻偏偏與公主長得像,于是被迫代替她去往敵國和親转捕。 傳聞我的和親對象是個殘疾皇子淋样,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內容