paint-brush
Tôi đã sử dụng AI sáng tạo để phát hiện các giống mèo: Đây là cách nó diễn ratừ tác giả@raymondcamden
804 lượt đọc
804 lượt đọc

Tôi đã sử dụng AI sáng tạo để phát hiện các giống mèo: Đây là cách nó diễn ra

từ tác giả Raymond Camden9m2023/12/19
Read on Terminal Reader

dài quá đọc không nổi

Đối với giao diện người dùng, tôi quyết định sử dụng tính năng nền tảng web gốc để truy cập máy ảnh của người dùng thông qua trường biểu mẫu HTML đơn giản. Bằng cách sử dụng capture="Camera" trên thẻ đầu vào, bạn có quyền truy cập trực tiếp vào camera của thiết bị. Có nhiều cách nâng cao hơn để thực hiện việc này, nhưng để nhanh chóng và đơn giản, nó hoạt động tốt. Thậm chí tốt hơn, trên máy tính để bàn, nó chỉ hoạt động như một công cụ chọn tệp.
featured image - Tôi đã sử dụng AI sáng tạo để phát hiện các giống mèo: Đây là cách nó diễn ra
Raymond Camden HackerNoon profile picture

Hãy thành thật mà nói, AI có công dụng gì khác ngoài việc làm việc với mèo? Nếu bạn đọc bài đăng trước đây của tôi về buổi ra mắt Gemini AI của Google, bạn có thể đã thấy lời nhắc thử nghiệm của tôi yêu cầu nó xác định loại con mèo xuất hiện trong ảnh.


Tôi quyết định biến ứng dụng này thành một ứng dụng web thích hợp làm ví dụ thực tế về API đang hoạt động. Đây là những gì tôi đã nghĩ ra.

Giao diện người dùng

Đối với giao diện người dùng, tôi quyết định sử dụng tính năng nền tảng web gốc để truy cập máy ảnh của người dùng thông qua trường biểu mẫu HTML đơn giản. Bằng cách sử dụng capture="camera" trên thẻ input , bạn có quyền truy cập trực tiếp vào camera của thiết bị.


Có nhiều cách nâng cao hơn để thực hiện việc này, nhưng để nhanh chóng và đơn giản, nó hoạt động tốt. Thậm chí tốt hơn, trên máy tính để bàn, nó chỉ hoạt động như một công cụ chọn tệp.


Suy nghĩ của tôi là - cung cấp một cách để có được hình ảnh (thông qua máy ảnh hoặc lựa chọn tệp), hiển thị hình ảnh và gửi nó đến phần cuối. Mặc dù JS cực kỳ đơn giản và đơn giản sẽ ổn, nhưng tôi vẫn tiếp tục và sử dụng Alpine.js để tương tác.


Đầu tiên, HTML cần cung cấp giao diện người dùng cho hình ảnh, một nơi hiển thị hình ảnh và một nơi khác để hiển thị kết quả.


 <!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>


Bây giờ, hãy chuyển sang JavaScript. Tôi sẽ bắt đầu bằng cách chia sẻ phiên bản đầu tiên vì nó đơn giản hơn việc giải thích tại sao nó bị lỗi. Ban đầu, tất cả những gì tôi cần làm là thông báo khi tệp được chọn hoặc ảnh được chụp và hiển thị nó trên DOM.


(Tôi đã sử dụng một chút CSS không được chia sẻ ở đây để kiểm tra kích thước hiển thị. Thông tin thêm về điều đó sau.) Tôi cũng cần gửi phiên bản base64 của tệp tới mã phía máy chủ. Đây là phiên bản ban đầu:


 //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; } } })) });


Phương thức gotPic được kích hoạt bất cứ khi nào trường input kích hoạt sự kiện onchange . Tôi lấy tệp/hình ảnh được sử dụng, đọc nó dưới dạng URL dữ liệu (base64), sau đó gán nó cho hình ảnh trong DOM và gửi nó đến máy chủ. Đẹp và đơn giản phải không?


Chà, mọi thứ đều hoạt động tốt trên máy tính để bàn, nhưng khi tôi chuyển sang máy ảnh của mình, Samsung S22 Ultra Magnus Extreme 200 Camera Lens Edition (không phải tên thật), tôi đã gặp phải vấn đề trong đó API của Google phàn nàn rằng tôi đã gửi quá nhiều dữ liệu. Sau đó, tôi nhớ rằng máy ảnh của tôi chụp những bức ảnh rất chi tiết và tôi cần thay đổi kích thước hình ảnh trước khi gửi đi.


Tôi đã thay đổi kích thước trong CSS, nhưng rõ ràng, điều đó không giống với việc thay đổi kích thước thực sự . Tôi đã tìm thấy bài viết tuyệt vời này trên trang web của ImageKit: Làm cách nào để thay đổi kích thước hình ảnh trong Javascript? Trong bài viết này, họ mô tả cách sử dụng phần tử canvas HTML để thực hiện thay đổi kích thước.


Có lẽ tôi đã không sử dụng Canvas trong gần một thập kỷ, nhưng tôi đã có thể sử dụng lại mã của họ vào giao diện người dùng của mình một cách hiệu quả:


 //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; } } })) });


Bạn sẽ nhận thấy mã sử dụng kích thước tối đa cho cả chiều rộng và chiều cao, đồng thời xử lý việc thay đổi kích thước một cách chính xác trong khi vẫn giữ nguyên tỷ lệ khung hình. Một lần nữa, tôi không thể coi thường bất kỳ điều gì trong số đó, cảm ơn Manu Chaudhary về bài đăng trên blog của anh ấy.


Kết quả cuối cùng của sự thay đổi này là bây giờ tôi đang gửi một hình ảnh nhỏ hơn nhiều đến dịch vụ phía sau và cuối cùng đã đến lúc xem xét điều đó.

Phần cuối

Về mặt hỗ trợ, tôi quyết định sử dụng lại Cloudflare Workers . Tôi hơi do dự vì trước đây nó có một số vấn đề với gói NPM và bản demo của tôi, nhưng lần này thì không có vấn đề gì. Nếu bạn còn nhớ bài đăng trước của tôi, AI Studio của Google cho phép bạn dễ dàng xuất mã mẫu từ lời nhắc của mình, vì vậy tất cả những gì tôi phải làm là kết hợp mã đó vào Cloudflare Worker.


Đây là toàn bộ mã:


 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}}); }, };


Phần lớn mã được soạn sẵn từ bản xuất AI Studio, ngoại trừ bây giờ, tôi lấy dữ liệu hình ảnh của mình từ thông tin được ĐĂNG cho nhân viên. Tôi muốn đặc biệt gọi ra lời nhắc:


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


Ban đầu, tôi đã cố gắng hết sức để có được kết quả bằng JSON và trả về một chuỗi trống nếu hình ảnh không phải là một con mèo. Nhưng sau đó tôi nhận thấy một điều thú vị - Song Tử đã xử lý rất tốt những bức ảnh không phải về mèo. Giống như... tốt đến kinh ngạc.


Tôi thực sự đánh giá cao việc ứng dụng không chỉ nói "Không phải con mèo" mà còn giải thích nó là gì. Rõ ràng, có chỗ cho cả hai phong cách cho một ứng dụng như thế này, nhưng tôi đã tiếp tục và giữ lại những câu trả lời dài dòng và hữu ích của Google.


Bây giờ, phần thú vị nhất là... kết quả.

Kết quả

Hãy bắt đầu với một vài hình ảnh về mèo.


Đầu tiên là Pig, con mèo yêu thích của tôi, không béo và trông không giống Jabba the Hut chút nào:

Hình ảnh chú mèo tam thể được nhận diện chính xác


Tiếp theo là hình ảnh của Luna. Trong trường hợp này, giống không chính xác - nhưng ít nhất là gần giống.


Một bức ảnh được coi là Maine Coon


Bây giờ, hãy ném cho Google một đường cong:


Một hình ảnh tưới nước có thể được xác định chính xác.


Điều này thực sự làm tôi ngạc nhiên. Mô tả chính xác 100% và thành thật mà nói, nếu tôi nhìn thấy bức tranh này mà không biết thì tôi đã nhận ra nó là tác phẩm điêu khắc về một con mèo chứ không phải là một bình tưới nước. Ý tôi là, tôi đoán điều đó khá hiển nhiên, nhưng thực lòng tôi không nghĩ bản thân mình sẽ nhận ra điều đó.


Bây giờ, hãy phát điên hoàn toàn:


Hình ảnh cây Giáng sinh được xác định chính xác.

Đúng, đúng vậy Google. Còn hình ảnh mèo do AI tạo ra thì sao?


Hình ảnh chú mèo làm DJ.


Tôi nghĩ nó xử lý việc đó khá tốt.


Kế tiếp...

Hình ảnh của nhân vật hành động Bigfoot


Đúng, đó là Bigfoot được rồi. Hãy nghĩ xem, tất cả những "nhà nghiên cứu" Bigfoot đó có thể nghỉ hưu và chỉ cần kết nối camera hành trình của họ với AI!


Hình ảnh của nhân vật hành động Skeletor

Tôi phải nói rằng - Tôi rất ấn tượng khi Google không chỉ công nhận nhượng quyền thương mại mà còn cả nhân vật thực tế, mà công bằng mà nói, Skeletor có một vẻ ngoài khá khác biệt.


Và cuối cùng, vì tôi (tất nhiên là không công bằng) đã so sánh con mèo của tôi với Jabba, hãy xem nó được xử lý như thế nào:


Jabba the Hutt


Ồ, tôi biết tôi đã nói là xong rồi, nhưng tất nhiên, tôi phải kiểm tra một con chó:


Con chó trên một chiếc ghế dài


Làm tốt lắm, Song Tử. Thật không may, tôi sẽ không lưu trữ trực tiếp bản demo này (URL 'trực tiếp' của Cloudflare trước đó trong mã sẽ không hoạt động), nhưng tôi có thể chia sẻ mã.