paint-brush
Kedi Irklarını Tespit Etmek İçin Üretken Yapay Zeka Kullandım: İşte Nasıl Olduile@raymondcamden
804 okumalar
804 okumalar

Kedi Irklarını Tespit Etmek İçin Üretken Yapay Zeka Kullandım: İşte Nasıl Oldu

ile Raymond Camden9m2023/12/19
Read on Terminal Reader

Çok uzun; Okumak

Ön uç için, kullanıcının kamerasına basit bir HTML form alanı aracılığıyla erişmek için yerel bir web platformu özelliğini kullanmaya karar verdim. Bir giriş etiketinde Capture = "camera" komutunu kullanarak doğrudan cihazın kamerasına erişebilirsiniz. Bunu yapmanın daha gelişmiş yolları var, ancak hızlı ve basit olması açısından iyi çalışıyor. Daha da iyisi, masaüstünde yalnızca bir dosya seçici görevi görür.
featured image - Kedi Irklarını Tespit Etmek İçin Üretken Yapay Zeka Kullandım: İşte Nasıl Oldu
Raymond Camden HackerNoon profile picture

Dürüst olalım, üretken yapay zekanın kedilerle çalışmaktan başka ne faydası var? Google'ın Gemini AI lansmanı hakkındaki önceki yazımı okursanız, resimde gösterilen kedinin türünü tanımlamasını isteyen test istemlerimi görmüş olabilirsiniz.


Uygulamadaki API'nin gerçek bir örneği olarak bunu uygun bir web uygulamasına dönüştürmeye karar verdim. İşte aklıma gelen şey bu.

Ön Uç

Ön uç için, kullanıcının kamerasına basit bir HTML form alanı aracılığıyla erişmek için yerel bir web platformu özelliğini kullanmaya karar verdim. Bir input etiketinde capture="camera" kullanarak doğrudan cihazın kamerasına erişebilirsiniz.


Bunu yapmanın daha gelişmiş yolları var, ancak hızlı ve basit olması açısından iyi çalışıyor. Daha da iyisi, masaüstünde yalnızca bir dosya seçici görevi görür.


Benim düşüncem, bir görüntü elde etmenin (kamera veya dosya seçimi yoluyla), görüntüyü görüntülemenin ve arka uca göndermenin bir yolunu sağlamaktı. İnanılmaz derecede basit ve vanilya JS iyi olurdu ama ben devam ettim ve etkileşim için Alpine.js'yi kullandım.


İlk olarak, HTML'nin görsel için kullanıcı arayüzünü, görselin görüntüleneceği bir yeri ve sonucun görüntüleneceği başka bir yeri sağlaması gerekir.


 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="app.css"> <title></title> </head> <body> <h2>🐈 Detector</h2> <div x-data="catDetector"> <input type="file" capture="camera" accept="image/*" @change="gotPic" :disabled="working"> <template x-if="imageSrc"> <p> <img :src="imageSrc"> </p> </template> <div x-html="status"></div> </div> <script defer src="https://unpkg.com/[email protected]/dist/cdn.min.js"></script> <script src="app.js"></script> </body> </html>


Şimdi JavaScript'e dönelim. Nasıl başarısız olduğunu açıklamaktan daha basit olduğu için ilk sürümü paylaşarak başlayacağım. Başlangıçta yapmam gereken tek şey, bir dosyanın seçildiğini veya bir fotoğrafın ne zaman çekildiğini fark etmek ve bunu DOM'a aktarmaktı.


(Görünür boyutu kontrol altında tutmak için burada paylaşılmayan bir miktar CSS kullandım. Birazdan bu konuda daha fazla bilgi vereceğim.) Ayrıca dosyanın base64 sürümünü sunucu tarafı koduna göndermem gerekiyordu. İşte ilk versiyon:


 //const IMG_FUNC = 'http://localhost:8787/'; const IMG_FUNC = 'https://catdetector.raymondcamden.workers.dev'; document.addEventListener('alpine:init', () => { Alpine.data('catDetector', () => ({ imageSrc:null, working:false, status:'', async init() { console.log('init'); }, async gotPic(e) { let file = e.target.files[0]; if(!file) return; let reader = new FileReader(); reader.readAsDataURL(file); reader.onload = async e => { this.imageSrc = e.target.result; this.working = true; this.status = '<i>Sending image data to Google Gemini...</i>'; let body = { imgdata:this.imageSrc } let resp = await fetch(IMG_FUNC, { method:'POST', body: JSON.stringify(body) }); let result = await resp.json(); this.working = false; this.status = result.text; } } })) });


gotPic yöntemi, input alanı bir onchange olayını tetiklediğinde tetiklenir. Kullanılan dosyayı/görüntüyü alıp veri URL'si (base64) olarak okuyorum ve ardından DOM'daki görüntüye atayıp sunucuya gönderiyorum. Güzel ve basit, değil mi?


Masaüstünde her şey yolunda gitti, ancak kameram olan Samsung S22 Ultra Magnus Extreme 200 Camera Lens Edition'a (gerçek adı değil) geçtiğimde, Google API'sının çok fazla veri gönderdiğimden şikayet ettiği sorunlarla karşılaştım. Daha sonra kameramın gerçekten detaylı fotoğraflar çektiğini hatırladım ve göndermeden önce resmi yeniden boyutlandırmam gerekiyordu.


Zaten CSS'de yeniden boyutlandırma yapıyordum, ancak açıkçası bu gerçekten yeniden boyutlandırmayla aynı şey değil. ImageKit'in sitesinde şu mükemmel makaleyi buldum: Javascript'te resimler nasıl yeniden boyutlandırılır? Bu makalede, yeniden boyutlandırmayı gerçekleştirmek için bir HTML canvas öğesinin nasıl kullanılacağı anlatılmaktadır.


Canvas'ı muhtemelen on yıla yakın bir süredir kullanmamıştım, ancak kodlarını kullanıcı arayüzümde yeterince iyi bir şekilde yeniden kullanabildim:


 //const IMG_FUNC = 'http://localhost:8787/'; const IMG_FUNC = 'https://catdetector.raymondcamden.workers.dev'; // Resize logic: https://imagekit.io/blog/how-to-resize-image-in-javascript/ const MAX_WIDTH = 400; const MAX_HEIGHT = 400; document.addEventListener('alpine:init', () => { Alpine.data('catDetector', () => ({ imageSrc:null, working:false, status:'', async init() { console.log('init'); }, async gotPic(e) { let file = e.target.files[0]; if(!file) return; let reader = new FileReader(); reader.readAsDataURL(file); reader.onload = async e => { let img = document.createElement('img'); img.onload = async e => { let width = img.width; let height = img.height; if(width > height) { if(width > MAX_WIDTH) { height = height * (MAX_WIDTH / width); width = MAX_WIDTH; } } else { if (height > MAX_HEIGHT) { width = width * (MAX_HEIGHT / height); height = MAX_HEIGHT; } } let canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; let ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, width, height); this.imageSrc = canvas.toDataURL(file.type); this.working = true; this.status = '<i>Sending image data to Google Gemini...</i>'; let body = { imgdata:this.imageSrc } let resp = await fetch(IMG_FUNC, { method:'POST', body: JSON.stringify(body) }); let result = await resp.json(); this.working = false; this.status = result.text; }; img.src = e.target.result; } } })) });


Kodun hem genişlik hem de yükseklik için maksimum boyut kullandığını ve aynı en boy oranını korurken yeniden boyutlandırmayı doğru şekilde gerçekleştirdiğini fark edeceksiniz. Tekrar ediyorum, Manu Chaudhary'ye blog yazısı için teşekkürler, bunların hiçbirini kabul edemem.


Bu değişikliğin net sonucu şu ki, artık arka uç hizmete çok daha küçük bir görüntü gönderiyorum ve artık buna bir göz atmanın zamanı geldi.

Arka Uç

Arka tarafım için Cloudflare Workers'ı tekrar kullanmaya karar verdim. Daha önce NPM paketleri ve demolarımla ilgili bazı sorunlar yaşadığı için biraz tereddütlüydüm ancak bu sefer herhangi bir sorun yaşamadım. Son gönderimden hatırlarsanız, Google'ın AI Studio'su, istemlerinizden kolayca örnek kod çıkarmanıza olanak tanıyor, dolayısıyla tek yapmam gereken bunu Cloudflare Worker'a dahil etmekti.


İşte kodun tamamı:


 const { GoogleGenerativeAI, HarmCategory, HarmBlockThreshold, } = require("@google/generative-ai"); const MODEL_NAME = "gemini-pro-vision"; export default { async fetch(request, env, ctx) { const API_KEY = env.GEMINI_KEY; console.log('begin serverless logic'); const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS", "Access-Control-Max-Age": "86400", }; let { imgdata } = await request.json(); imgdata = imgdata.replace(/data:.*?;base64,/, ''); const genAI = new GoogleGenerativeAI(API_KEY); const model = genAI.getGenerativeModel({ model: MODEL_NAME }); const generationConfig = { temperature: 0.4, topK: 32, topP: 1, maxOutputTokens: 4096, }; const safetySettings = [ { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, }, { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, }, { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, }, { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, }, ]; const parts = [ {text: "Look at this picture and if you see a cat, return the breed of the cat."}, { inlineData: { mimeType: "image/jpeg", data: imgdata } } ]; console.log('calling google'); const result = await model.generateContent({ contents: [{ role: "user", parts }], generationConfig, safetySettings, }); const response = result.response; let finalResult = { text: response.text() }; return new Response(JSON.stringify(finalResult), { headers: {...corsHeaders}}); }, };


Kodun çoğunluğu AI Studio dışa aktarımından elde edilen standart metindir, ancak şu an görüntü verilerimi çalışana POST edilen bilgilerden alıyorum. Özellikle istemi belirtmek istiyorum:


 Look at this picture and if you see a cat, return the breed of the cat.


Başlangıçta, JSON'da bir sonuç elde etmek ve resim bir kedi değilse boş bir dize döndürmesini sağlamak için çok uğraştım. Ama sonra harika bir şey fark ettim: Gemini, kedi olmayan resimleri ele alma konusunda gerçekten iyi bir iş çıkardı. Mesela... şaşırtıcı derecede iyi.


Aslında uygulamanın sadece "Kedi Değil" demekle kalmayıp bunun yerine ne olduğunu açıklamasını gerçekten takdir ettim. Açıkçası, böyle bir uygulama için her iki stile de yer var, ancak ben gittim ve Google'ın ayrıntılı ve yararlı yanıtlarını sakladım.


Şimdi işin eğlenceli kısmına geçelim... sonuçlar.

Sonuçlar

Birkaç kedi resmiyle başlayalım.


Bunlardan ilki, şişman olmayan ve Jabba the Hut'a hiç benzemeyen, en sevdiğim kedim Pig:

Doğru şekilde tanınan bir alaca kedinin resmi


Sırada Luna'nın bir resmi var. Bu durumda cins yanlıştır - ancak en azından yakındır.


Maine Coon olarak kabul edilen bir hayvanın resmi


Şimdi Google'a bir eğri çizelim:


Bir sulama kutusunun resmi doğru bir şekilde tanımlandı.


Bu beni gerçekten şaşırttı. Açıklama %100 doğrudur ve açıkçası bu resmi görüp bilmeseydim, bunun bir sulama kabı değil, bir kedi heykeli olduğunu anlardım. Yani, sanırım bu çok açık, ama açıkçası bunu benim de fark edebileceğimi sanmıyorum.


Şimdi tamamen çılgına dönelim:


Doğru şekilde tanımlanmış bir Noel ağacının resmi.

Evet, doğru Google. Yapay zeka tarafından oluşturulan kedi görüntülerine ne dersiniz?


DJ olarak bir kedinin resmi.


Bence bunu gayet iyi halletti.


Sonraki...

Koca Ayak aksiyon figürünün resmi


Evet, bu Koca Ayak tamam. Bir düşünün, tüm Koca Ayak "araştırmacıları" emekli olabilir ve takip kameralarını yapay zekaya bağlayabilir!


İskeletor aksiyon figürünün resmi

Şunu söylemeliyim ki, Google'ın sadece seriyi değil aynı zamanda gerçek karakteri de tanımasından etkilendim; ancak adil olmak gerekirse, İskeletor'un ona oldukça farklı bir görünümü var.


Ve son olarak, (haksız bir şekilde) kedimi Jabba ile karşılaştırdığım için, gelin bunun nasıl ele alındığına bakalım:


Hutt Jabba


Ah, işimin bittiğini söylediğimi biliyorum ama elbette bir köpeği test etmem gerekiyordu:


Köpek kanepede


Çok iyi iş çıkardın, İkizler. Maalesef bu demoyu canlı olarak barındırmayacağım (kodun başındaki Cloudflare 'canlı' URL'si çalışmaz), ancak kodu paylaşabilirim.