Cum de a crea o aplicație de detectare a feței cu React Native

Ești un dezvoltator de aplicații hibride care dorește să includă detectarea feței în aplicația ta, dar nu ai idee de unde să începi? Pentru început, puteți citi o Introducere în detectarea feței pe Android, care vă arată cum să implementați în mod nativ detectarea feței pe Android. Dar dacă sunteți ca mine și nu doriți să scrieți cod Java pentru a crea un modul React Nativ care să facă acest lucru pentru dvs., atunci sunteți în locul potrivit.

În acest tutorial, ne uităm la API-ul de detectare a feței, care face parte din Microsoft Cognitive Services. Acest API permite dezvoltatorilor să implementeze cu ușurință funcționalitatea detectării feței în aplicații. În acest tutorial, o să presupun că aceasta nu este prima dvs. aplicație React Nativă. Dacă sunteți nou la React Native, vă recomandăm să citiți mai întâi tutorialul Facebook de la începutul paginii pe site-ul Web React Nativ. Acest tutorial vă arată cum să vă configurați mediul și să vă creați primul proiect Nativ React.

Cerințe preliminare

Chiar dacă ne concentrăm pe platforma Android în acest tutorial, cu puțină muncă, puteți adăuga suport pentru alte platforme. Asigurați-vă că ați instalat Android Studio. Puteți descărca Android Studio de pe portalul Google pentru dezvoltatori.

1. Ce este API-ul pentru detectarea feței?

Înainte de a începe să scriem aplicația noastră, aș dori să fac o clipă pentru a vorbi despre API pe care îl vom folosi pentru detectarea feței. API-ul de detectare a feței al Microsoft oferă funcționalități de detectare a feței și recunoaștere a feței prin intermediul unui API bazat pe cloud. Acest lucru ne permite să trimitem o solicitare HTTP care conține fie o imagine, fie o adresă URL a unei imagini existente pe web și să primească date despre orice chip detectat în imagine.

Trimiterea cererilor către API

Puteți solicita API pentru detectarea feței Microsoft prin trimiterea unui mesaj POST solicitați https://api.projectoxford.ai/face/v1.0/detect. Cererea trebuie să conțină următoarele informații de antet:

  • Tipul de conținut: Acest câmp de antet conține tipul de date al corpului de solicitare. Dacă trimiteți URL-ul unei imagini pe web, atunci ar trebui să fie valoarea acestui câmp de antet application / json. Dacă trimiteți o imagine, setați câmpul cu antet la application / octet-stream.
  • -APIM-POC Abonament-cheie: Acest câmp de antet conține cheia API utilizată pentru autentificarea cererilor dvs. Vă voi arăta cum să obțineți o cheie API mai târziu în acest tutorial.

În mod implicit, API-ul returnează numai date despre casetele care sunt utilizate pentru a închide fețele detectate în imagine. În restul acestui tutorial, mă voi referi la aceste cutii ca cutii pentru cutii. Această opțiune poate fi dezactivată prin setarea returnFaceRectangle interogare de interogare la fals. Valoarea implicită este Adevărat, ceea ce înseamnă că nu trebuie să îl specificați dacă nu doriți să dezactivați această opțiune.

Puteți furniza câțiva alți parametri opționali de interogare pentru a obține informații suplimentare despre fețele detectate:

  • returnFaceId: Dacă este setat la Adevărat, această opțiune atribuie un identificator unic fiecărei fețe detectate.
  • returnFaceLandmarks: Prin activarea acestei opțiuni, API returnează o serie de repere ale fețelor detectate, inclusiv ochii, nasul și buzele. Această opțiune este dezactivată în mod prestabilit.
  • returnFaceAttributes: Dacă această opțiune este activată, API caută și returnează atribute unice pentru fiecare dintre fețele detectate. Trebuie să furnizați o listă separată prin virgulă a atributelor care vă interesează, cum ar fi vârsta, sexul, zâmbetul, părul facial, poziția capului și ochelarii.

Mai jos este un exemplu de răspuns pe care îl obțineți de la API, datând următoarea adresă URL a solicitării:

https://api.projectoxford.ai/face/v1.0/detect?faceId=true&faceLandmarks=true&faceAttributes=age,gender,smile,facialHair,headPose,glasses
"faceId": "c5c24a82-6845-4031-9d5d-978df9175426", "faceRectangle": "lățime": 78, "înălțime": 78, "stânga": 394, ": " pupilLeft ": " x ": 412.7," y ": 78.4," pupilRight ": " x ": 437.7, "y": 92.4, "mouthLeft": "x": 417.8, "y": 114.4, "mouthRight": "x": 451.3, ":" 397.9, "y": 78.5, "eyebrowLeftInner": "x": 425.4, "y": 70.5 "eyeLeftOuter" : "x": 412.2, "y": 76.2, "eyeLeftBottom": "x": 413.0, "y": 80.1, "eyeLeftInner" , "eyebrowRightInner": "x": 4.8, "y": 69.7, "eyebrowRightOuter": "x": 5.5, "y": 68.5, eyeRightInner: ": 75.0," eyeRightTop ": " x ": 446.4," y ": 71.7," eyeRightBottom ": " x ": 447.0, y: 75.3 451.7, "y": 73.4, "noseRootLeft": "x": 428.0, "y": 77.1 "noseRootRight" "x": 428,3, " y ": " x ": 424.3," y ": 96.4," noseRightAlarOutTip ": " x " : 446.6, "y": 92.5, "upperLipTop": "x": 437.6, "y": 105.9, "upperLipBottom": "x": 437.6, "y": 108.2 "underLipTop" "x": 436.8, "y": 111.4, "underLipBottom": "x": 437.3, "y": 114.5 "faceAttributes" "," zâmbet ": 0.88," facialHair ": " mustață ": 0.8," barbă ": 0.1," sideburns ": 0.02" ochelari de soare " 2.1, "răsturnare": 3, "pas": 0]

Acest răspuns eșantion este destul de auto-explicativ, așa că nu mă voi arunca mai adânc în ceea ce reprezintă fiecare atribut. Datele pot fi folosite pentru a afișa chipurile detectate, atributele lor diferite și modul în care le puteți arăta utilizatorului. Acest lucru se realizează prin interpretarea coordonatelor x și y sau a poziționării de sus și de stânga.

Obținerea unei chei API

Pentru a utiliza API-ul de detectare a feței al Microsoft, fiecare solicitare trebuie autentificată cu o cheie API. Iată pașii pe care trebuie să îi faceți pentru a obține o astfel de cheie.

Creați un cont Microsoft Live dacă nu aveți deja unul. Conectați-vă cu contul dvs. Microsoft Live și înscrieți-vă pentru un cont Microsoft Azure. Dacă încă nu aveți un cont Microsoft Azure, puteți să vă înscrieți pentru o încercare gratuită, oferindu-vă acces la serviciile Microsoft timp de 30 de zile.

Pentru API-ul de detectare a feței, acest lucru vă permite să trimiteți gratuit până la douăzeci de apeluri API pe minut. Dacă aveți deja un cont Azure, atunci vă puteți abona la planul Pay-As-You-Go, astfel încât să plătiți numai pentru ceea ce utilizați.

Odată ce contul dvs. Microsoft Azure este configurat, sunteți redirecționat către portalul Microsoft Azure. În portal, navigați la bara de căutare și introduceți servicii cognitive în câmpul de căutare. Faceți clic pe rezultatul care apare Conturile serviciilor cognitive (previzualizare). Ar trebui să vedeți o interfață similară cu următoarea:

Apasă pe Adăuga și completați formularul cu care vă prezentați:

  • Nume de cont: Introduceți numele pe care doriți să-l dați resursei.
  • Tip API: Selectați API pentru detectarea feței.
  • Nivelul de prețuri: În scopul testării, selectați nivelul gratuit (până la 20 de apeluri API pe minut). Dacă doriți să utilizați serviciul în producție, selectați o altă opțiune care să se potrivească necesităților aplicației dvs..
  • Abonament: Selectați testul gratuit dacă utilizați un nou cont Microsoft Azure. În caz contrar, selectați opțiunea Pay-As-You-Go.
  • Grupul de resurse: Selectați unul existent dacă aveți deja unul. În caz contrar, creați un nou grup de resurse selectând noua opțiune și introduceți un nume pentru grupul de resurse.
  • Locație: Selectați Vestul SUA.

În pasul următor, trebuie să fiți de acord cu termenii și condițiile Microsoft pentru a continua. Apasă pe Crea butonul și așteptați ca resursa să termine implementarea.

Odată ce implementarea sa terminat, faceți clic pe Toate resursele în bara laterală stângă pentru a vedea resursele pe care le aveți în prezent. Cel pe care tocmai l-ai creat ar trebui să fie listat acolo. Dacă nu, încercați să actualizați pagina.

Faceți clic pe resursa pe care ați creat-o și faceți clic pe pictograma pentru a vizualiza cheile API asociate resursei. În mod implicit, două chei sunt generate și puteți utiliza oricare dintre acestea.


2. Construirea aplicației

Înainte de a începe să construim aplicația, permiteți-mi mai întâi să vă dau o scurtă trecere în revistă a aplicației. După cum am menționat mai devreme, vom construi o aplicație de detectare a feței. Aplicația va avea două butoane, una pentru selectarea unei imagini și una pentru detectarea fețelor. Butonul pentru alegerea unei imagini va cere utilizatorului să selecteze o sursă, camera aparatului sau galeria.

Dacă este selectată camera, va fi lansată aplicația implicită pentru cameră. Dacă este selectată galeria, aplicația va permite utilizatorului să selecteze o fotografie din galerie. După selectarea unei fotografii, butonul pentru detectarea fețelor devine vizibil. Dacă apăsați acest buton, veți trimite o cerere către API-ul de detecție a feței al Microsoft, care returnează datele pentru fețele detectate. Folosind răspunsul API, cutiile mici sunt desenate în jurul fețelor detectate, inclusiv etichetele pentru sexul și vârsta persoanei.

Acesta este aspectul aplicației:

Pasul 1: Instalarea dependențelor

Suntem gata să construim aplicația. Să începem prin instalarea dependențelor. Deschideți o nouă fereastră terminal în directorul de lucru și executați următoarea comandă:

reacționează la init FaceDetector nativ

Acest lucru creează un nou proiect React Native pentru noi, care, la momentul redactării, este la versiunea 0.25. După finalizarea procesului de instalare, navigați la dosarul proiectului.

Apoi, vom instala trei biblioteci pe care le vom folosi pentru a dezvolta aplicația:

npm instalare lodash react-nativ-fetch-blob reaction-nativ-selector de imagini -save
  • Doar folosim lodash pentru ei Hartă metodă. Folosim această metodă pentru a converti rezultatele obținute din API într-o componentă pe care o vom face.
  • react-native-image-picker: Această bibliotecă este utilizată pentru adăugarea abilității de a alege o imagine utilizând camera sau o imagine din galerie.
  • react-native-fetch-blob: Această bibliotecă este utilizată pentru trimiterea cererilor de rețea cu conținut de blob. Aplicația API pentru detectarea feței are nevoie în mod special de blobul fotografiei, dar aduce API nu o susține din cutie, motiv pentru care folosim această bibliotecă pentru a ne ocupa de ea.

Pasul 2: Configurarea proiectului

Deoarece nu toate modulele Reactive Native suportă încă React Native Package Manager, trebuie să configuram manual proiectul astfel încât modulele să funcționeze fără probleme. Mai exact, trebuie să configuram proiectul pentru reacționează-nativ-image-picker pentru a funcționa corect.

În directorul de lucru, deschideți Android / settings.gradle fișier și adăugați următorul fragment imediat după include: 'app':

include: proiectul "reactive-native-image-picker" (proiectul "reactive-native-image-picker").

Deschide Android / app / build.gradle fișier și găsiți dependențe secțiune. Ar trebui să arate ceva de genul:

dependență compilați fileTree (dir: "libs", include: ["* .jar"]) compilați com.android.support:appcompat-v7:23.0.1 compilați com.facebook.react: react-native: + " // From node_modules

Adăugați următorul fragment în lista dependențelor:

compilați proiectul (': reacter-native-image-picker')

Deschis Android / app / src / main / AndroidManifest.xml și adăugați următorul fragment sub permisiunile de sistem implicite, care sunt necesare de React Native.

   

Pentru referință, permisiunile implicite de sistem sunt:

 

Deschis Android / app / src / main / java / com / facedetector / MainActivity.java și adăugați următoarea declarație de import în partea de sus a paginii Activitate principala clasă.

import com.imagepicker.ImagePickerPackage;

Am menționat utilizarea lui rnpm mai devreme. Dacă nu aveți încă instalat pe computer, acum ar fi un moment bun pentru a face acest lucru:

npm instalează rnpm -g

Odată instalat, executați rnpm link comanda pentru a conecta automat modulele pe care le-ați instalat settings.gradle, build.gradleAndroidManifest.xml, și MainActivity.java.

rnpm link

Ea are grijă de tot ceea ce am făcut manual reacționează-nativ-image-picker. Am trecut prin procesul manual de adăugare a unei dependențe, astfel încât să știți ce rnpm se află sub capotă.

Pasul 3: Componentă punct de intrare pentru aplicații

Suntem gata să scriem un cod. În primul rând, deschis index.android.js și înlocuiți conținutul acestui fișier cu următoarele:

import Reacționează de la "reacționează"; importați AppRegistry, Component, StyleSheet, Text, View de la "react-native"; detector de import de la "./components/Detector"; const image_picker_options = title: 'Selectează fotografia', takePhotoButtonTitle: 'Take Photo ...', chooseFromLibraryButtonTitle: 'Alegeți din Bibliotecă ...', cameraType: 'back', mediaType: 'photo', maxWidth: cale falsă: "imagini"; const api_key = 'API KEY DETECTION FACE'; clasa FaceDetector extinde Component render () return (    );  const stiles = StyleSheet.create (container: flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '# F5FCFF',); AppRegistry.registerComponent ("FaceDetector", () => FaceDetector);

Ceea ce avem mai sus este codul boilerplate pentru un fișier cu punct de intrare Reactive Native. Mai întâi, importăm componentele de care avem nevoie.

import Reacționează de la "reacționează"; importați AppRegistry, Component, StyleSheet, Text, View de la "react-native"; detector de import de la "./components/Detector";

Apoi declarăm opțiunile pe care le face Detector componenta va avea nevoie. Aceasta include opțiunile pentru selectorul de imagini și cheia API pe care ați primit-o mai devreme de la Microsoft Azure. Nu uitați să introduceți cheia API și să o atribuiți api_key.

const image_picker_options = title: 'Selectează fotografia', takePhotoButtonTitle: 'Ia fotografie ...', chooseFromLibraryButtonTitle: 'Alegeți din Bibliotecă ...', cameraType: 'back', // camera din față sau din spate? mediaType: 'photo', // tipul fișierului pe care doriți să îl selectați maxWidth: 480, // lățimea țintă în care se redimensionează calitatea fotografiei: 1, // 0 la 1 pentru specificarea calității fotografiei noData: false, // dacă este setat la true dezactivează baza64 a fișierului; // Cheia API pe care ați primit-o de la Microsoft Azure const api_key = 'API KEY Detection Face API'; 

Apoi, vom crea componenta punct de intrare și, în interiorul containerului principal, utilizați Detector componentă. Nu uitați să treceți în proprietățile necesare:

clasa FaceDetector extinde Component render () return (    ); 

De asemenea, definim stilurile:

const stiles = StyleSheet.create (container: flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '# F5FCFF',);

În final, vom înregistra componenta:

AppRegistry.registerComponent ("FaceDetector", () => FaceDetector);

Pasul 4: Componenta butonului

Creați un fișier nou în componente și denumiți-o Button.js. Această componentă ne va permite să creăm cu ușurință butoane care efectuează o acțiune specifică. Mai târziu, veți vedea cum este folosit acest lucru în Detector componentă. Pentru moment, știi că trebuie să intri onpressbutton_styles, button_text_styles, și text ca proprietăți pentru a particulariza aspectul și funcționalitatea fiecărui buton.

import Reacționează de la "reacționează"; importați AppRegistry, Component, Text, View, TouchableHighlight de la "reactive-native"; pentru exportul clasei extinse Butonul extinde Component render () return (    This.props.text    );  AppRegistry.registerComponent (butonul "Button", () =>);

Pasul 5: Componenta detectorului

Încă înăuntru componente director, creați un fișier nou, denumiți-l Detector.js, și adăugați codul următor. Această componentă este acolo unde se întâmplă magia. Ia-ți un moment pentru a parcurge implementarea.

import Reacționează de la "reacționează"; importați AppRegistry, Component, StyleSheet, Text, View, Image de la "reactive-native"; importați module Native, ImagePickerManager din "NativeModules"; butonul de import din "./Button"; import RNFetchBlob din "reactive-native-fetch-blob"; importă de la "lodash"; exportul detectorului de clasă implicită extinde Component constructor (recuzită) super (recuzită); this.state = foto_style: position: 'relativ', lățimea: 480, înălțimea: 480, has_photo: false, foto: null, face_data: null;  render () return (   this._renderFaceBoxes .call (this)  

Să o despărțim, așa că știi exact ce se întâmplă. Începem prin importarea bibliotecilor de care avem nevoie. Aceasta include Reacţiona și componentele sale implicite, selectorul de imagini, componenta butonului, reacționează-nativ-fetch-pată de cerneală bibliotecă și lodash.

import Reacționează de la "reacționează"; importați AppRegistry, Component, StyleSheet, Text, View, Image de la "reactive-native"; importați module Native, ImagePickerManager din "NativeModules"; butonul de import din "./Button"; import RNFetchBlob din "reactive-native-fetch-blob"; importă de la "lodash";

În cadrul declarației de clasă, avem constructorul care este executat înainte ca componenta să fie montată. Aici setăm stilul implicit pentru fotografia selectată, valoarea booleană utilizată ca bază pentru a afișa sau nu butonul pentru detectarea fețelor, fotografia însăși și face_data, care este folosită ca sursă de date pentru construirea cutiilor cu fața.

exportul detectorului de clasă implicită extinde Component constructor (recuzită) super (recuzită); this.state = foto_style: position: 'relativ', lățimea: 480, înălțimea: 480, has_photo: false, foto: null, face_data: null;  ...

Înainte este face() , care face fotografia selectată și cele două butoane, selectați imaginea și detectați fețele. Observați că sunăm mai jos alte trei metode, _renderFaceBoxes (), _pickImage (), și _renderDetectedFacesButton (). Vom trece prin aceste metode în curând, dar deocamdată știm că sunt folosite pentru a simplifica implementarea face() metodă.

De asemenea, rețineți că folosim apel și lega în loc să apeleze direct la metode. Acest lucru se datorează faptului că metodele din clasele ES6 nu sunt legate automat de clasă. Acest menas trebuie să fie folosit fie lega sau apel să legeți metodele acest, care se referă la clasa însăși. Dacă nu știți diferența dintre lega și apel, verificați această întrebare privind depășirea stivei cu privire la diferența dintre apel, aplica, și lega.

render () return (   this._renderFaceBoxes.call (this)  

_pickImage () metoda este apelată când este apăsat butonul pentru selectarea imaginilor. Acest lucru se stabilește face_data la nul astfel încât eventualele cutii de fețe să fie îndepărtate. Apoi deschide fereastra de dialog pentru a alege de unde să obțină o fotografie din cameră, din camera sau din galerie.

Dialogul folosește obiectul din care a trecut index.android.js pentru a personaliza setările. După ce a fost selectată o fotografie, este returnat un răspuns care conține reprezentarea URI locală și baza64 a fotografiei, dimensiunile acesteia (lățimea și înălțimea) și alte informații importante despre fișier. Utilizăm aceste date pentru a actualiza starea, care actualizează interfața utilizator a aplicației.

Rețineți că mai devreme am specificat a lățimea maximă de 480 pentru opțiunile de selectare a imaginilor. Aceasta înseamnă că imaginea selectată este redimensionată la acea lățime, iar înălțimea este reglată automat pentru a menține raportul de aspect. De aceea actualizăm lățimea și înălțimea în photo_style pentru a redimensiona Imagine astfel încât fotografia să se potrivească frumos.

poziţie este setat sa rudă astfel încât cutiile cu fațadă absolut poziționate să fie constrânse în interiorul lui Imagine component. fotografie este folosit ca sursă pentru Imagine componentă și photo_data este reprezentarea de bază a fotografiei. Trebuie să o punem în stare, astfel încât să o putem folosi mai târziu atunci când solicităm API-ul.

_pickImage () this.setState (față_data: null); ImagePickerManager.showImagePicker (this.props.imagePickerOptions, (răspuns)) => if (answer.error) alert ('Eroare la obținerea imaginii. ; // sursa fotografiei pentru a afișa aceasta. setState (photo_style: position: 'relativ', lățime: answer.width, height: answer.height, has_photo: true, photo: source, photo_data: reply. date );  ); 

_renderDetectFacesButton () metoda este responsabilă pentru redarea butonului pentru detectarea fețelor. Afișează numai butonul dacă has_photo în stat este setat sa Adevărat.

_renderDetectFacesButton () if (this.state.has_photo) retur ( 

Când este apăsat butonul Detectează fețele, _detectFaces () metoda este executată. Această metodă face a POST cereți API-ului de detectare a feței, trecând în reprezentarea de bază a fotografiei selectate împreună cu câteva opțiuni ca parametri de interogare.

Rețineți că treceți în reprezentarea de bază a fotografiei, dar blobul fișierului este ceea ce este trimis la server, deoarece folosim reacționează-nativ-fetch-pată de cerneală bibliotecă. Odată ce primim un răspuns înapoi, actualizăm starea cu face_data pentru a face casetele cu fețe.

_dectFace () RNFetchBlob.fetch ('POST', 'https://api.projectoxford.ai/face/v1.0/detect?returnFaceId=true&returnFaceAttributes=age,gender', 'Accept': 'application / json' , "Content-Type": "aplicație / octet-stream", "Ocp-Apim-Abonament-cheie": this.props.apiKey, this.state.photo_data) .then ((res) => return res. (json) = if (json.length) this.setState (face_data: json); altceva // o matrice goală este returnată dacă API nu a detectat orice alertă a fețelor ("Ne pare rău, eu nu pot vedea fețe acolo"); retur json;) .catch (funcția (eroare) console.log (error); alert (' Încercați din nou. "+ JSON.stringify (eroare));); 

Rețineți că, în codul de mai sus, se ocupă de cazuri în care API nu este capabil să detecteze chipurile din fotografie alergând utilizatorul. Acest lucru se poate întâmpla din două motive:

  • Fotografia nu conține fețe.
  • Fețele din fotografie nu sunt recunoscute de algoritmul de detectare a feței, deoarece sunt fie prea mari, fie prea mici, unghiuri mari ale feței (poziția capului), iluminare insuficientă sau prea multă sau ceva blochează o porțiune a feței.

_renderFaceBoxes () metoda returnează cutiile cu fața în funcție de face_data care este în prezent în stat. Folosim sistemul lui lodash Hartă() funcția de a trece prin datele de pe față. Fiecare cutie este absolut poziționată astfel încât totul să pornească la marginea superioară din stânga a casetei Imagine componentă. top și stânga și poziția lăţime și înălţime din fiecare casetă se bazează pe valorile stocate în faceRectangle obiect.

_renderFaceBoxes () if (this.state.face_data) permiteți vizualizări = _map (this.state.face_data, (x) => let box = position: 'absolute', top: x.faceRectangle.top, stânga: x.faceRectangle.left; permiteți stil = width: x.faceRectangle.width, height: x.faceRectangle.height, borderWidth: 2, borderColor: '#fff' fff ', return (   x.faceAttributes.gender, x.faceAttributes.age y / o  ); ); întoarcere  vizualizări 

Înainte de a înregistra componenta, adăugăm stilurile.

const = StyleSheet.create (container: flex: 1, alignItems: 'center', alignSelf: 'center', backgroundColor: '#ccc', 529ecc ', butonul_text: culoare:' #FFF ', fontSize: 20);

În sfârșit, înregistrăm componenta.

AppRegistry.registerComponent ("Detector", () => Detector);

3. Construiți și executați

Construiți și rulați aplicația pentru a vedea dacă totul funcționează corect. Nu uitați să introduceți cheia API obținută de la portalul Microsoft Azure. Cu o cheie API validă, aplicația nu va putea detecta chipuri.

Concluzie

Asta e. În acest tutorial, ați învățat cum să creați o aplicație de detectare a feței folosind API-ul de detectare a feței al Microsoft. În timp ce ați făcut acest lucru, ați învățat cum să adăugați un serviciu la Microsoft Azure și să faceți o cerere către API-ul de detectare a feței.

Dacă doriți să aflați mai multe despre API-ul de detectare a feței, consultați documentația oficială și referința API pentru servicii cognitive.

Cod