react-native-camera插件支持限制掃描區(qū)域

實現(xiàn)原理

Android二維碼掃描限制掃描區(qū)域的實現(xiàn)方式是通過修改相機預覽圖片截取灰度圖的范圍來實現(xiàn)限制掃描區(qū)域。相機預覽尺寸因手機而不同,相機會選擇一個相等的或相近的尺寸用來做預覽尺寸,根據這個尺寸提取出預覽圖片的YUV數(shù)據堡掏。YUV數(shù)據區(qū)別于RGB數(shù)據,YUV數(shù)據將像素的灰度值(明亮度)與色值(色彩與飽和度)區(qū)分開,分別來存岂却。我們的插件中使用的數(shù)據格式是YUV420格式。它的存儲方式是先將每個像素的Y值(灰度值)存儲起來裙椭,這樣就是一個width * height大小的數(shù)組躏哩。另外再存儲UV值,每四個像素共用一個U值和V值揉燃,所以U = V = Y / 4扫尺。最終獲得的數(shù)組大小是width * height * 3 / 2。這樣存儲的好處是炊汤,即使只拿到了Y值也是可以渲染圖片的正驻,只不過渲染的是黑白圖片。因為二維碼是二維平面圖片抢腐,只提取黑白顏色是能夠識別二維碼信息的姑曙。所以利用PlanarYUVLuminanceSource類將截取區(qū)域的X,Y氓栈,width渣磷,height值設置進去,在getMatrix(獲取灰度圖矩陣)時就會只獲取這一區(qū)域的YUV數(shù)據授瘦。我們把X醋界,Y,width提完,height值設置為二維碼掃描框的大小和位置形纺,這樣在識別二維碼時就只能識別掃描框中的二維碼了。

YUV數(shù)據格式參考

iOS二維碼掃描限制掃描區(qū)域是通過rectOfInterest方法來實現(xiàn)的徒欣,它的實現(xiàn)方式要比Android的方法更簡單逐样,因為iOS的原生代碼支持這樣的設置,我們只需要將參數(shù)傳進去就可以了。但是一個需要特別注意的地方是坐標系發(fā)生了變換脂新。常規(guī)的坐標系是左上角為原點挪捕,向右為X軸,向下為Y軸争便。而在iOS的rectOfInterest方法傳入的參數(shù)是以右上角為原點级零,向左為X軸,向下為Y軸滞乙。這樣X奏纪,Y,width斩启,height四個值就要發(fā)生變換才可以序调。

X -> Y / SCREEN_HEIGHT
Y -> (SCREEN_WIDTH - X - 掃描框寬度)/ SCREEN_WIDTH
width -> 掃描框高度 / SCREEN_HEIGHT
height -> 掃描框寬度 / SCREEN_WIDTH

iOS 二維碼有效區(qū)域rectOfInterest詳解

示例:self.output.rectOfInterest = CGRectMake(100 / (SCREEN_HEIGHT), (SCREEN_WIDTH - 100 - 200) / SCREEN_WIDTH, 100 / SCREEN_HEIGHT, 200 / SCREEN_WIDTH);//200 * 100的原點在(100,100)位置的掃描框

需要注意的是,設置掃描框區(qū)域范圍時會受到屏幕寬高比設置的影響兔簇。一般我們的手機屏幕寬高比都是16:9的发绢,插件中默認設置的是4:3,如果我們不相應的設置屏幕寬高比男韧,則設置完掃描區(qū)域后會發(fā)現(xiàn)在Y軸方向上能夠實現(xiàn)對掃描區(qū)域的控制朴摊,但是在X軸上則會比設置的值要寬一些,這應該是屏幕的預覽效果在4:3的寬高比設置下被拉伸所引起的誤差此虑。所以需要設置合適的屏幕寬高比。
Android的設置方式是在js端設置屬性ratio:'16:9'口锭。全面屏的手機的比值一般為'2:1'朦前。
iOS的設置方式是在js端設置屬性defaultVideoQuality:RNCamera.Constants.VideoQuality["720p"]。
更多的設置方式參考react-native-camera官方文檔

使用示例

<RNCamera 
    style={styles.preview}
    ratio={'16:9'}
    defaultVideoQuality={RNCamera.Constants.VideoQuality["720p"]}
    scanAreaLimit={true}
    scanAreaX={115}
    scanAreaY={328}
    scanAreaWidth={522}
    scanAreaHeight={521}
    flashMode={this.state.torchState == 'off' ? RNCamera.Constants.FlashMode.off : RNCamera.Constants.FlashMode.torch}
    onBarCodeRead={this._onBarCodeRead.bind(this)}
    permissionDialogTitle={'請求相機權限'}
    permissionDialogMessage={'應用沒有獲取到相機權限鹃操,請先到設置中為應用開啟相機權限'}
</RNCamera>

可以通過在View中用絕對布局畫一個框來看一下這個掃描區(qū)域的范圍韭寸。

<View style={{width: 522 * winWidth / 750, height: 521 * winHeight / 1334, position:'absolute', left: 115 * winWidth / 750, top: 328 * winHeight / 1334, borderWidth: 1, borderColor: 'red', borderStyle: 'solid', backgroundColor: 'transparent'}}>
</View>

插件代碼修改

js端

RNCamera.js增加五個屬性

屬性名 類型 默認值 描述
scanAreaLimit boolean false 用來控制是否開啟掃描區(qū)域限制
scanAreaX int 0 掃描區(qū)域原點X值
scanAreaY int 0 掃描區(qū)域原點Y值
scanAreaWidth int 0 掃描區(qū)域寬度
scanAreaHeight int 0 掃描區(qū)域高度

Android端

官方#1667號修改被還原

CameraViewManager.java

@ReactProp(name = "scanAreaLimit")
public void setScanAreaLimit(RNCameraView view, boolean scanAreaLimit) {
  view.setScanAreaLimit(scanAreaLimit);
}

@ReactProp(name = "scanAreaX")
public void setScanAreaX(RNCameraView view, int scanAreaX) {
  view.setScanAreaX(scanAreaX);
}

@ReactProp(name = "scanAreaY")
public void setScanAreaY(RNCameraView view, int scanAreaY) {
  view.setScanAreaY(scanAreaY);
}

@ReactProp(name = "scanAreaWidth")
public void setScanAreaWidth(RNCameraView view, int scanAreaWidth) {
  view.setScanAreaWidth(scanAreaWidth);
}

@ReactProp(name = "scanAreaHeight")
public void setScanAreaHeight(RNCameraView view, int scanAreaHeight) {
  view.setScanAreaHeight(scanAreaHeight);
}

RNCameraView.java

//ScanAreaLimitParams
private boolean mScanAreaLimit = false;
private int mScanAreaX = 0;
private int mScanAreaY = 0;
private int mScanAreaWidth = 0;
private int mScanAreaHeight = 0;

public void setScanAreaLimit(boolean scanAreaLimit) {
  this.mScanAreaLimit = scanAreaLimit;
}

public void setScanAreaX(int scanAreaX) {
  this.mScanAreaX = scanAreaX;
}

public void setScanAreaY(int scanAreaY) {
  this.mScanAreaY = scanAreaY;
}

public void setScanAreaWidth(int scanAreaWidth) {
  this.mScanAreaWidth = scanAreaWidth;
}

public void setScanAreaHeight(int mScanAreaHeight) {
  this.mScanAreaHeight = mScanAreaHeight;
}

/*...*/
new BarCodeScannerAsyncTask(delegate, mMultiFormatReader, correctData, correctWidth, correctHeight, mScanAreaLimit, mScanAreaX, mScanAreaY, mScanAreaWidth, mScanAreaHeight).execute();
/*...*/

BarCodeScannerAsyncTask.java(核心修改)

/*...*/
private final int COMMON_WIDTH = 750;
private final int COMMON_HEIGHT = 1334;
/*...*/

/*...*/
private BinaryBitmap generateBitmapFromImageData(byte[] imageData, int width, int height, boolean scanAreaLimit, int scanAreaX, int scanAreaY, int scanAreaWidth, int scaAreaHeight) {
  if(scanAreaLimit == false) {
    PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(
            imageData, // byte[] yuvData
            width, // int dataWidth
            height, // int dataHeight
            0, // int left
            0, // int top
            width, // int width
            height, // int height
            false // boolean reverseHorizontal
    );
    return new BinaryBitmap(new HybridBinarizer(source));
  } else {
    PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(
            imageData, // byte[] yuvData
            width, // int dataWidth
            height, // int dataHeight
            scanAreaX * width / COMMON_WIDTH, // int left
            scanAreaY * height / COMMON_HEIGHT, // int top
            scanAreaWidth * width / COMMON_WIDTH, // int width
            scaAreaHeight * height / COMMON_HEIGHT, // int height
            false // boolean reverseHorizontal
    );
    return new BinaryBitmap(new HybridBinarizer(source));
  }
}
/*...*/

iOS端

RNCameraManager.m

RCT_CUSTOM_VIEW_PROPERTY(scanAreaLimit, BOOL, RNCamera)
{
    [view setScanAreaLimit:[RCTConvert BOOL:json]];
}

RCT_CUSTOM_VIEW_PROPERTY(scanAreaX, NSInteger, RNCamera)
{
    [view setScanAreaX:[RCTConvert NSInteger:json]];
}

RCT_CUSTOM_VIEW_PROPERTY(scanAreaY, NSInteger, RNCamera)
{
    [view setScanAreaY:[RCTConvert NSInteger:json]];
}

RCT_CUSTOM_VIEW_PROPERTY(scanAreaWidth, NSInteger, RNCamera)
{
    [view setScanAreaWidth:[RCTConvert NSInteger:json]];
}

RCT_CUSTOM_VIEW_PROPERTY(scanAreaHeight, NSInteger, RNCamera)
{
    [view setScanAreaHeight:[RCTConvert NSInteger:json]];
}

RNCamera.h

#define SCREEN_WIDTH [UIApplication sharedApplication].delegate.window.frame.size.width
#define SCREEN_HEIGHT [UIApplication sharedApplication].delegate.window.frame.size.height
#define COMMON_WIDTH 750
#define COMMON_HEIGHT 1334

@property(assign, nonatomic) BOOL scanAreaLimit;
@property(assign, nonatomic) NSInteger scanAreaX;
@property(assign, nonatomic) NSInteger scanAreaY;
@property(assign, nonatomic) NSInteger scanAreaWidth;
@property(assign, nonatomic) NSInteger scanAreaHeight;

RNCamera.m(核心修改)

//在startSession方法中加入下段代碼
/*...*/
if (self.scanAreaLimit) {
    self.scanAreaX = self.scanAreaX * SCREEN_WIDTH / COMMON_WIDTH;
    self.scanAreaY = self.scanAreaY * SCREEN_HEIGHT / COMMON_HEIGHT;
    self.scanAreaWidth = self.scanAreaWidth * SCREEN_WIDTH / COMMON_WIDTH;
    self.scanAreaHeight = self.scanAreaHeight * SCREEN_HEIGHT / COMMON_HEIGHT;
    self.metadataOutput.rectOfInterest = CGRectMake((self.scanAreaY)/(SCREEN_HEIGHT), (SCREEN_WIDTH - self.scanAreaX - self.scanAreaWidth)/SCREEN_WIDTH, self.scanAreaHeight/SCREEN_HEIGHT, self.scanAreaWidth/SCREEN_WIDTH);
}
/*...*/
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市荆隘,隨后出現(xiàn)的幾起案子恩伺,更是在濱河造成了極大的恐慌,老刑警劉巖椰拒,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晶渠,死亡現(xiàn)場離奇詭異,居然都是意外死亡燃观,警方通過查閱死者的電腦和手機褒脯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缆毁,“玉大人番川,你說我怎么就攤上這事。” “怎么了颁督?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵践啄,是天一觀的道長。 經常有香客問我沉御,道長屿讽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任嚷节,我火速辦了婚禮聂儒,結果婚禮上,老公的妹妹穿的比我還像新娘硫痰。我一直安慰自己衩婚,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布效斑。 她就那樣靜靜地躺著非春,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缓屠。 梳的紋絲不亂的頭發(fā)上奇昙,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音敌完,去河邊找鬼储耐。 笑死,一個胖子當著我的面吹牛滨溉,可吹牛的內容都是我干的什湘。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼晦攒,長吁一口氣:“原來是場噩夢啊……” “哼闽撤!你這毒婦竟也來了?” 一聲冷哼從身側響起脯颜,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤哟旗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后栋操,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闸餐,經...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年讼庇,在試婚紗的時候發(fā)現(xiàn)自己被綠了绎巨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡蠕啄,死狀恐怖场勤,靈堂內的尸體忽然破棺而出戈锻,到底是詐尸還是另有隱情,我是刑警寧澤和媳,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布格遭,位于F島的核電站,受9級特大地震影響留瞳,放射性物質發(fā)生泄漏拒迅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一她倘、第九天 我趴在偏房一處隱蔽的房頂上張望璧微。 院中可真熱鬧,春花似錦硬梁、人聲如沸前硫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屹电。三九已至,卻和暖如春跃巡,著一層夾襖步出監(jiān)牢的瞬間危号,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工素邪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留外莲,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓兔朦,卻偏偏與公主長得像苍狰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子烘绽,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容

  • 二維碼掃描最近兩年簡直是風靡移動互聯(lián)網時代,尤其在國內發(fā)展神速俐填。圍繞條碼掃碼功能安接,首先說說通過本文你可以知道啥。一...
    55book閱讀 4,140評論 0 1
  • 文章圖片上傳不正常英融,如需文檔盏檐,可聯(lián)系微信:1017429387 目錄 1 安裝... 4 1.1 配置探針... ...
    Mrhappy_a7eb閱讀 6,285評論 0 5
  • 選擇qi:是表達式 標簽選擇器 類選擇器 屬性選擇器 繼承屬性: color,font驶悟,text-align胡野,li...
    wzhiq896閱讀 1,731評論 0 2
  • 對網頁有些了解的朋友應該都知道并不是所有網頁的頁面內容都在頁面源碼中,大多數(shù)網頁源碼中只有頁面結構而無數(shù)據痕鳍,顯示的...
    GHope閱讀 1,287評論 0 17
  • 2018年第38周(09/17--09/23) 這一周一切都在正常軌道進行··· 一硫豆、健康 1.早起 本周早起時間...
    tracy_bacb閱讀 83評論 0 0