はじめに
こんちゃ。Scratchでサーバーからプロジェクトに画像を送信できるようになったのでここに詳細を書きます
具体的な活用例です
本編行きます
使ったもの
ライブラリ
サービス
YouTube Data API (v3)
Google Apps Script (以下GAS)
仕組み
- Youtubeからサムネイル画像のURLを取ってくる
- それをいい感じのサイズに加工 (ここでは120x90)
- それを15x15の画像に分割
- 画像を送れるように変換
- 送信する
詳しく
1. Youtubeからサムネイル画像のURLを取ってくる
ここでGAS登場。
GASのYoutube Data APIを使ってサムネイルのURLを取得します。
簡単なコード
function doGet(e){
try{
const result = YouTube.Videos.list("snippet", {id: e.parameter.id});
const url = result.items[0].snippet.thumbnails.high.url;
Logger.log(url)
return ContentService.createTextOutput(JSON.stringify({
"url": url
})).setMimeType(ContentService.MimeType.JSON);
}catch(e){
return ContentService.createTextOutput(JSON.stringify({
"url": "",
"msg": e.message
})).setMimeType(ContentService.MimeType.JSON);
}
}
これをデプロイして使用します
2. それをいい感じのサイズに加工
Jimpを使います
const Jimp = require('jimp');
const process_img = async (img_buf) => {
let img = (await Jimp.read(img_buf))
.resize(120, 90);
return img.getBufferAsync(Jimp.MIME_PNG);
};
ローカルに展開したくないのでバッファを使ってます
3. それを15x15の画像に分割
ChatGPTに書いてもらいました。優秀。
// write by ChatGPT
async function splitImageIntoSubImages(
imageBuffer,
width,
height,
subWidth,
subHeight
) {
try {
// Jimpを使用して画像を読み込む
const image = await Jimp.read(imageBuffer);
// サブ画像を格納する配列
let subImages = [];
// 10x10のサブ画像を生成して配列に格納する
for (let y = 0; y < height / subHeight; y++) {
for (let x = 0; x < width / subWidth; x++) {
const subImage = image
.clone()
.crop(
x * subWidth,
y * subHeight,
subWidth,
subHeight
);
subImages.push(
await subImage.getBufferAsync(Jimp.MIME_PNG)
);
}
}
return subImages;
} catch (error) {
throw error;
}
}
4. 画像を送れるように変換
それぞれのピクセルのデータを以下のように変換します
[
[R, G, B],
[R, G, B],
[R, G, B],
...
]
ついでにそれぞれの数値を3桁になるようにゼロ埋めして全部つなげます
これもChatGPT。賢い。(少し手は加えた)
// by ChatGPT
async function pngBufferToArray(pngBuffer) {
try {
const image = await Jimp.read(pngBuffer);
const width = image.bitmap.width;
const height = image.bitmap.height;
let pixelArray = [];
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const { r, g, b } = Jimp.intToRGBA(
image.getPixelColor(x, y)
);
pixelArray.push([r, g, b]);
}
}
pixelArray = pixelArray.map((row) => {
return row.map((col) => {
return col.toString().padStart(3, '0');
});
});
pixelArray = pixelArray.map((row) => row.join(''));
return pixelArray.join('');
} catch (error) {
console.error('Error processing image:', error);
return null;
}
}
これでとりあえず送るデータは完成。ということで送りましょう
5. 送信する
クラウド変数を使って送ります。
クラウド変数の反映には0.1秒間隔があることに注意してください
15x15の画像なら8つクラウド変数を用意すれば一度に送れます。
こうやって
function setCloud(str, base_name, cloud) {
let cloudData = split_n(255, str);
let i = 0;
cloudData.forEach((data) => {
let name = base_name + i.toString();
cloud.set(name, data);
i++;
});
}
一気にセットできるやつを作成してそれを使います
(split_nはここから)
こんな感じで送信します
for (let i = 0; i < imgDat.length; i++) {
setCloud(imgDat[i], base_name, cloud);
console.log(`packet${i} send complete`);
await sleep(500); // 間隔開ける!!
cloud.set(`${base_name}8`, i + 1);
await sleep(500);
}
以上。これをつなぎ合わせただけでは動きません。
各自補完してください
あとがき
めっちゃ短い。まぁ1時間クオリティだしなぁ
15x15に分けて送信するのはパケットとかの発想から持ってきた。
15x15なのは120 90どちらも15で割り切れるから&クラウド変数の使用数が10個を超えないから。
ほんじゃまた。
なんかBuffer非推奨とか言われてる...もう作っちゃったしいいや
またいつか改善するわ
[追記]
Glitchにデプロイしました。Glitch便利ですね