«Դեմքի ճանաչման օգտագործումը հավելվածը կառավարելու համար» հոդվածի շարունակությունը
Բոն, սա այս հոդվածի շարունակությունն է, որտեղ ես նկարագրեցի, թե ինչպես եմ ստեղծել հավելված, որը հետևում է իմ հայացքին: Հավելվածը բավականին լավ էր աշխատում, բայց ոչ: Օրենսգիրքը նայելուց և այն բազմիցս վերակառուցելու մասին մտածելուց հետո ես ուզում էի ստեղծել լուծում, որն ինքնին կսովորեր կամ մոտ ինչ-որ բան:
Հիմնական ML
Երբ ես ավելի շատ քննարկեցի մարտահրավերը, մտքովս անցավ, որ սա կարող է հիանալի նախագիծ լինել Core ML-ի համար: Ես կարող էի ստեղծել արժեքների մոդել տարբեր խառնուրդների ձևերի համար և օգտագործել այն որոշելու համար, թե որ կողմն էի փնտրում: Դա լավ հարմար էր մոդելավորման համար, քանի որ ես ունեի մի շարք հստակ արժեքներ և արդյունքներ, և նույնիսկ ավելի լավ, դա իրական աշխարհի խնդիր էր, որի շուրջ ես կարող էի կառուցել մեքենայական ուսուցման մոդել:
Կոդ
Իմ ճանապարհորդության ընթացքում առաջին փոփոխությունը, որ կատարեցի, իմ հավելվածի համար հեռակառավարման վահանակի ներդրումն էր: Ես վստահ չէի, որ դրա կարիքը կունենամ, բայց դա կարող է օգնել ինձ դիտորդի խնդրի հարցում: Դա անելու համար ես օգտագործեցի MultipeerConnectivity
շրջանակը՝ այն իրականացնելով այս թղթում նկարագրված նույն սխեմայով։ Հանուն արդարության ձեզ համար, ընթերցող, այստեղ կա մի դյուրանցում ներբեռնելու բազմաստիճան կոդը: Խնդրում ենք պատճենել և տեղադրել այս կոդը իր սեփական Swift ֆայլում: Ես զանգահարեցի իմը MultiPeer
:
Ձեր ներբեռնած կոդը կազմաձևված է որպես գովազդատու. դուք պետք է այն ներառեք հիմնական հավելվածում: Դա անելուց հետո ես ստեղծեցի երկրորդ հավելվածը, որը կոչվում է «հեռակառավարում», որը նաև պահանջում էր, որ կոդը չմեկնաբանի բրաուզերի տողերը և մեկնաբանի գովազդատուի տողերը init()
մեթոդով հեռավոր կողմի համար: Այստեղ հեռակառավարիչը զննարկիչն է. հիմնական հավելվածը գովազդատուն է:
override init() { ... serviceBrowser.delegate = self serviceBrowser.startBrowsingForPeers() }
Ես կառուցեցի երկրորդ հավելվածի էկրանը, որպեսզի պարունակի երկու կոճակ՝ դադարեցման կոճակ՝ իրերը դադարեցնելու համար և գնալ կոճակ՝ դրանք ազատելու համար: Հեռակառավարման վահանակի նպատակն է վերանայել հայացքի արժեքները սարքի էկրանին` չփոխելով նշված արժեքները, երբ ես նայում եմ դրան, դասական դիտորդի խնդիր:
Դուք կգտնեք իմ հեռակառավարման ContentView
-ը՝ այս հիմնական հղումով: Օգտագործեք այս երկու աղբյուր ֆայլերը՝ ստեղծելու ձեր սեփական հեռահար հավելվածը և մի մոռացեք կատարել անհրաժեշտ փոփոխությունները տեղեկատվական հատկություններում:
Ինձ անհրաժեշտ էր ավելացնել ֆունկցիոնալությունը մյուս հավելվածին, այնպես որ ես ավելացրի օրինակ և հղումներ նշված օբյեկտին:
var connectSession = MultipeerSession.shared
Այնուհետև ես թարմացրի նստաշրջանի կոդը, որպեսզի փոխեմ դադարի փոփոխականը ճիշտ կամ կեղծ՝ կախված հղման միջոցով ուղարկված տվյալներից:
Նշում. փոփոխությունն արդեն առկա է ձեր ներբեռնած բազմակի կոդում:
Դուք պետք է մեկնաբանեք այս կոդը «հեռավոր» տարբերակում:
let foo = String(decoding: data, as: UTF8.self) DispatchQueue.main.async { if foo == "free" { looked.paused = false } else { looked.paused = true } }
Դիտորդների մարտահրավեր
Երկրորդը՝ անկասկած, ավելի կարևոր փոփոխությունը, որը ես արել եմ հավելվածում, խոսքի դասի ներառումն էր: Սա կարևոր էր, քանի որ ես պետք է շարունակեի էկրանից հեռու նայել՝ տարբեր հայացքները տեսնելու համար: Լավ, ես ստեղծել էի հեռակառավարման վահանակ՝ այն սառեցնելու համար, բայց եթե այն կարդա ուղղությունները, ևս մեկ լար կավելացներ իմ աղեղին: Ահա խոսքի կոդի աղբյուրը. այն կարող եք ներբեռնել այստեղ:
Ենթադրելով, որ դուք հայտարարել եք դասի օրինակ որպես talk
, այն գործելու համար ձեզ անհրաժեշտ է ընդամենը մի տող, ինչպիսին է.
talk.speaker(words: ["Minority Report"])
Ստեղծեք ML և Core ML
Լավ, գնացեք հիմնական ճաշատեսակ: Նախորդ տարբերակի հիմնական մարտահրավերը իմ հայացքի մատնանշած բոլոր ութ վայրերը ճանաչելն էր: Ես նախկինում կոշտ կոդավորված էի հավելվածում թվացող ողջամիտ արժեքները, ուստի կոդը հետևյալն էր.
if eyesLookUp > 0.2 && eyesLookSide < 0.3 && eyesLookDown < 0.1 { sphereNode.simdPosition = SIMD3(x: 0, y: height + 1, z: -6) sphereNode.geometry?.firstMaterial?.diffuse.contents = UIColor.red.withAlphaComponent(1) }
Բայց դա չաշխատեց այնպես, ինչպես ես ակնկալում էի: Ես կարող էի ստիպել, որ անկյունները լավ աշխատեն, բայց պայքարում էի կողմերի հետ և հակառակը: Ես ստեղծեցի մի պարզ աղյուսակ՝ տարբեր հայացքների արժեքները ուրվագծելու համար:
Դա կատարյալ համընկնում էր մոդելավորման համար, քանի որ յուրաքանչյուր ճանապարհ ուներ վերջնական արժեքներ: Այնուհետև ես սա արտահանեցի որպես «CSV» ֆայլ և գործարկեցի Ստեղծել ML գործիքը Xcode-ի տակ:
Նշում. ես պետք է խմբագրեի «CSV» ֆայլը, նախքան այն ներմուծելը, քանի որ թվերն իմ ֆայլում ստորակետների շարք ավելացրին:
Դա անելու համար ես օգտագործեցի այս գերազանց տեսանյութը, որպեսզի ինքս ինձ առաջնորդեմ մոդելային ֆայլ ստեղծելու համար անհրաժեշտ առաջադրանքները: Եթե ժամանակ չունեք դա դիտելու, ահա արագ ակնարկ:
Ինձ անհրաժեշտ էր ընտրել մեկ թիրախային փոփոխական՝ ուղղությունը: Իմ աղյուսակի մյուս տասը փոփոխականները/սյունակները անհրաժեշտ էին արժեքը որոշելու համար: Երբ ես ստեղծեցի մոդելը, ես օգտագործեցի այս երկրորդ տեսանյութը, որպեսզի հիշեցնեմ, թե ինչպես այն ներառել իմ նախագծում: Ինձ համար վերջնական կոդը այսպիսի տեսք ուներ.
Նշում. Ես սահմանել եմ eyeLook
փոփոխականները՝ օգտագործելով ARFaceAnchor
արժեքները:
let eyeLookIn_L = faceAnchor?.blendShapes[.eyeLookInLeft]?.doubleValue let eyeLookIn_R = faceAnchor?.blendShapes[.eyeLookInRight]?.doubleValue let eyeLookOut_R = faceAnchor?.blendShapes[.eyeLookOutRight]?.doubleValue let eyeLookOut_L = faceAnchor?.blendShapes[.eyeLookOutLeft]?.doubleValue let eyeLookDown_L = faceAnchor?.blendShapes[.eyeLookDownLeft]?.doubleValue let eyeLookUp_L = faceAnchor?.blendShapes[.eyeLookUpLeft]?.doubleValue let eyeLookDown_R = faceAnchor?.blendShapes[.eyeLookDownRight]?.doubleValue let eyeLookUp_R = faceAnchor?.blendShapes[.eyeLookUpRight]?.doubleValue
Այժմ, օգտագործելով այս պարզ մոդելը, ես կարող էի տարբերել բոլոր ուղղությունները, բացառությամբ առանց ուղղության հայացքի, մի անտեսում, որը ես ի սկզբանե ֆիքսել եմ կոդերի այս տողով.
if eyesLookLeft < 0.1 && eyesLookRight < 0.1 && eyesLookDown < 0.1 && eyesLookUp < 0.1 { sphereNode.simdPosition = SIMD3(x: 0, y: height, z: -6) sphereNode.geometry?.firstMaterial?.diffuse.contents = UIColor.green.withAlphaComponent(0.8) looked.gazeX = "" return }
Մոդելի կանխատեսումը գործադրելու համար ես միավորեցի այս անջատիչի հայտարարությունը «եթե» կետի հետ՝ օգնելու ինձ պահել իմ գլուխը տեսախցիկի նկատմամբ:
Ես նորից փորձարկեցի այն անիմացիոն GIF-ով, որն այստեղ ցույց է տալիս արդյունքը.
Ես գոհ էի նոր կառուցվածքից; այն ուներ ավելի նրբագեղություն և, ի տարբերություն կոշտ կոդավորված տարբերակի, հեշտ էր կսմթել: Կարգավորումների թեմայի վերաբերյալ ես փորձեցի մոդելի յոթ տարբեր տարբերակներ [փոխելով շեմային արժեքները], նախքան հայտնվեցի մեկը, թեև պետք է խոստովանեմ, որ պետք էր շատ անշարժ մնալ բոլոր ութ դիրքերը կազմելու համար: Ես փորձեցի նաև մի քանի այլ ալգորիթմներ. նրանք ոչ մի տարբերություն չեն ունեցել:
Հաջորդ շարժումները
Մտքիս վերջում ես ուզում էի գրանցել իրական արժեքները, պահել դրանք CSV-ում և օգտագործել դրանք մոդելը վերակառուցելու համար: Ես պետք է ավելի լավ հասկանայի, թե ինչպես է գլխիս կողմնորոշումը փոխում հայացքի արժեքները: Դա հասկանալը թույլ կտա ինձ այն ներդնել մոդելի մեջ, ինչը տեսականորեն կարող է նշանակել, որ ես կարող եմ մի փոքր ավելի շարժել գլուխս:
Ես ստեղծեցի մեթոդ՝ զանգվածում տեսած բոլոր արժեքները պահելու համար: Ահա դրա կոդը.
func logEyes(look:String) { let eyeLookIn_L = faceAnchor?.blendShapes[.eyeLookInLeft]?.doubleValue let eyeLookIn_R = faceAnchor?.blendShapes[.eyeLookInRight]?.doubleValue let eyeLookOut_R = faceAnchor?.blendShapes[.eyeLookOutRight]?.doubleValue let eyeLookOut_L = faceAnchor?.blendShapes[.eyeLookOutLeft]?.doubleValue let eyeLookDown_L = faceAnchor?.blendShapes[.eyeLookDownLeft]?.doubleValue let eyeLookUp_L = faceAnchor?.blendShapes[.eyeLookUpLeft]?.doubleValue let eyeLookDown_R = faceAnchor?.blendShapes[.eyeLookDownRight]?.doubleValue let eyeLookUp_R = faceAnchor?.blendShapes[.eyeLookUpRight]?.doubleValue let angleX = rad2deg(Double(self.cubeNode.eulerAngles.x)) let angleY = rad2deg(Double(self.cubeNode.eulerAngles.y)) let rec = Regard(direct: look, eyeLookIn_L: eyeLookIn_L, eyeLookOut_R: eyeLookOut_R, eyeLookIn_R: eyeLookIn_R, eyeLookOut_L: eyeLookOut_L, eyeLookDown_L: eyeLookDown_L, eyeLookDown_R: eyeLookDown_R, eyeLookUp_L: eyeLookUp_L, eyeLookUp_R: eyeLookUp_R, angleX: angleX, angleY: angleY) log.append(rec) }
Այնուհետև ես հավաքեցի կոդը, որն անհրաժեշտ էր ինձ տարբեր հայացքների համար մի քանի նվազագույն և առավելագույն արժեքներ տալու համար: Դա կարծես սա է.
if eyesTrue { let foo = log.filter({$0.direct == "eyesLeft"}) let foo2 = foo.filter({$0.eyeLookIn_L > 0}) let minValue = foo2.min { $0.eyeLookIn_L < $1.eyeLookIn_L }?.eyeLookIn_L let maxValue = foo2.max { $0.eyeLookIn_L < $1.eyeLookIn_L }?.eyeLookIn_L let sortedValues = foo2.sorted(by: { $0.eyeLookIn_L > $1.eyeLookIn_L} ) for foos in foo2 { print("\(foos.eyeLookIn_L!)") } print("lookLeft \(minValue) \(maxValue)") }
Ես փորձեցի գործարկել նշված կոդը իմ հեռակառավարման վահանակով, բայց որոշեցի, որ ավելի հեշտ կլինի օգտագործել այլ ժեստ՝ այն գործարկելու համար: Ահա դա անելու կոդը.
let eyesTrue = (faceAnchor?.blendShapes[.browOuterUpLeft]!.doubleValue)! > 0.1
Ես չափեցի արժեքները, գործարկեցի հավելվածը և հավաքեցի նմուշներ: Ահա այն արժեքները, որոնք ես վերադարձա.
lookUp min 0.010353747755289078 max 0.5442964434623718 eyeLookUp_L lookLeftUp min 0.10131984949111938 max 0.42720866203308105) eyeLookUp_L lookLeft min 0.054532475769519806 max 0.6156175136566162 eyeLookOut_R lookLeft min 0.4086191952228546) max 0.7494118809700012 eyeLookIn_L
Ես փոխեցի մոդելի արժեքները ձախ և աջ հայացքների համար և ավելացրի eyescenter
արժեքներ մոդելին: Ավելի վաղ նշված խնդիրը շտկելուց հետո նման տեսք ուներ.
Եվ դա բավականին լավ աշխատեց: Կարծում եմ, որ iPhone-ի ետևում կաղապար էր պետք՝ աչքերս ճիշտ տեղում կենտրոնացնելու համար: Դրանից ավելին անցնելու համար ինձ պետք էր տեսախցիկի վրա տեղադրված սաղավարտ, ինչպես այն, ինչ նրանք օգտագործում են Ավատար 2-ի հատուկ էֆեկտների համար: Եթե ցանկանում եք փորձել այն, ահա աղբյուրային կոդը: