「労働者が自分の仕事をうまくやりたいなら、まず自分の道具を研ぎ澄まさなければなりません。」 - 孔子、「論語。陸霊公」
表紙 > プログラミング > 生の画像バイトを使って遊ぶ

生の画像バイトを使って遊ぶ

2024 年 11 月 1 日に公開
ブラウズ:494

こんにちは、今日は画像生バイトで遊んでみましょう。私がやろうとしているのは、画像の生のピクセル バイトを操作して、何かおかしなものを作ることです。これは楽しいことです。この記事には、いくつかの理論と実践的な実装が含まれています。それでは、行きましょう…

画像は多数のピクセルの集まりによって形成されることがわかっているため、ピクセルは RGB (赤、緑、青) または RGBA (赤、緑、青、 Alpha) それぞれ 1 バイトを使用します。

PNG や JPG などの拡張子で表示される画像は画像の圧縮形式です。PNG は DEFLATE などのアルゴリズムを使用してピクセルを失わずに圧縮する可逆圧縮で、JPG は非可逆圧縮です。一部のピクセルが失われるため、画質が多少低下します。圧縮せずに画像を表示したい場合は、画像を BMP (ビットマップ画像ファイル) に変換するか、その他のファイルに変換する必要があります。形式に変換すると、非圧縮画像が得られます。しかし、これは必要ありません。生のバイトを抽出して処理し、再度 PNG または JPG に変換します。

まず、画像をアップロードするようにクライアントを設定しましょう。これに簡単な反応アプリケーションを設定します

import axios from "axios";
import { useState } from "react";
import "./App.css";

function App() {
  const [img, setImg] = useState(null);
  const [pending, setPending] = useState(false);

  const handleImg = (e) => {
    setImg(e.target.files[0]);
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!img) return;

    const formData = new FormData();
    formData.append("img", img);

    try {
      setPending(true);
      const response = await axios.post("http://localhost:4000/img", formData);
      console.log(response.data);

    } catch (error) {
      console.log("Error uploading image:", error);
    } finally {
      setPending(false);
    }
  };

  return (
    

); } export default App;

これは画像をアップロードするための単純なコードであり、主要部分はサーバー側にあります。

次に、画像のバイトを手動で計算し、サーバー側のコードで確認してみましょう。

下の画像を選択しました。

Playing with raw image bytes

画像は前回の記事のサムネイルです。これは PNG ファイルです。プロパティ セクションに移動すると、画像の幅と高さを確認できます。この場合、幅と高さは 722 x 407 で、293854 ピクセルに相当します。また、これは合計バイト数ではなく、単なる合計ピクセル数です。ご存知のとおり、各ピクセルは 3 バイトまたは 4 バイト、RGB または RGBA です。したがって、上の画像が RGB の場合、合計バイト数は 722 x 407 x 3 = 881562 になります。または、画像にアルファ チャネルがある場合、合計バイト数は 722 x 407 x 4 = 1175416 になります。

サーバー側について少し説明しましょう。私はノード js を使用しています。

複数形式のデータを解析するための multer というライブラリがあります。

app.post("/img", upload.single("img"), async (req, res) => {
  const arr = req.file.buffer
  console.log(arr.length)    //output: 30929
  res.send("success")
});

画像のバイトをバッファ配列に保存します。バッファ配列の長さをとれば、答えは 30929 です。配列にはこれだけのバイトがありますが、合計バイト数は 1175416 になるはずですよね?ここで何が起こるかというと、multer は圧縮などは行わず、ユーザーから画像を取得してそのままバッファに保存するだけです。そのため、PNG ファイルをアップロードしました。表示されているバッファは、 PNG 画像サイズ。

次に、圧縮画像バイトのバイトを変更しましょう。

app.post("/img", upload.single("img"), async (req, res) => {
  const arr = req.file.buffer;
  console.log("multer "   arr.length);
  fs.writeFile("output.png", arr, (err) => {
    console.log(err);
  });
  res.send("successfull");
});

fs を使用して、既存のイメージを使用して新しいイメージを作成しました。したがって、最初のバイトの arr[0] = 231 を変更すると、画像は開きません。

Playing with raw image bytes

最初の特定のバイトはメタデータ用に予約されているため、それらのメタデータを変更すると画像が破損する可能性があります。

それでは、500 バイト目にジャンプしましょう。 arr[500] = 123、イメージを書き込みます。しかし、現在、画像は壊れています。圧縮アルゴリズムでエンコードされたデータが変更される可能性があるため、圧縮された画像のバイトを直接操作するべきではありません。

画像から生のバイトが必要です。その後、バイトを個別に操作できます。そのために、sharp ライブラリを使用できます。

npm シャープのインストール

シャープをインストールします。次に、これらのロジックを処理するための別のファイルを作成します。

シャープ.js

export async function convert(buffer) {
  try {
    const data = await sharp(buffer).metadata();
    console.log(data)
  }catch(err){
    console.log(err)
  }
}

これは非同期関数です。アップロードした png からメタデータを取得しましょう。

{
  format: 'png',
  size: 30929,
  width: 722,
  height: 407,
  space: 'srgb',
  channels: 4,
  depth: 'uchar',
  density: 72,
  isProgressive: false,
  hasProfile: false,
  hasAlpha: true
}

これは画像のメタデータです。最後のデータは hasAlpha: true であるため、アルファ チャネルがあり、各ピクセルは 4 バイトであることがわかります。

次に、画像から生のバイトを取得しましょう。

const rawBytes = await sharp(buffer)
      .raw()
      .toBuffer({ resolveWithObject: true });

console.log(rawBytes.data.length)  //1175416

これで、配列の長さが計算結果と同じであることがわかります。したがって、この画像には 1175416 バイトが含まれます。 これで、任意のバイトを自由に変更できます。 これで、メタデータはバッファーに保存されなくなり、バッファーには画像の生のバイトのみが含まれます。

1 ピクセルだけを赤に変更しましょう。

  rawBytes.data[0] = 225;    //red
  rawBytes.data[1] = 10;     //green
  rawBytes.data[2] = 10;     //blue
  rawBytes.data[3] = Math.floor(0.8 * 255);   //alpha

Playing with raw image bytes

1 つのピクセルが赤に変更されるため、ピクセルの変化を確認するには画像を拡大する必要があります。

次に、画像を分割して色を変更してみましょう。上半分が黄色、下半分が緑色です

const div = rawBytes.data.length / 2;
    for (let i = 0; i 



反復ごとに 1 ピクセルを変更するため、ループを 4 回インクリメントします。出力は次のようになります。

Playing with raw image bytes

アルファ チャネルが 0.8 に設定されているため、この画像では透明度が確認できます

画像の書き込みについて言うのを忘れていました。新しい画像を書き込むのに fs は必要ありません。シャープ自体を使用できます。

await sharp(rawBytes.data, {
      raw: {
        width: data.width,
        height: data.height,
        channels: data.channels,
      },
    })
      .png()
      .toFile("demo.png");

同じメタデータを使用して新しい画像を生成しています。

サーバー側の完全なコードは次のとおりです。

//index.js
import express from "express";
import dotenv from "dotenv";
import multer from "multer";
import cors from "cors";
import { convert } from "./sharp.js";

const app = express();
dotenv.config();
app.use(cors({ origin: "http://localhost:5173" }));
const storage = multer.memoryStorage();
const upload = multer();

app.post("/img", upload.single("img"), async (req, res) => {
  const arr = req.file.buffer;
  await convert(arr);
  res.send("successful");
});

app.listen(process.env.PORT, () => {
  console.log("server started");
});
//sharp.js
import sharp from "sharp";

export async function convert(buffer) {
  try {
    const data = await sharp(buffer).metadata();
    console.log(data);
    //raw data
    const rawBytes = await sharp(buffer)
      .raw()
      .toBuffer({ resolveWithObject: true });
    console.log(rawBytes.data.length);
    const div = rawBytes.data.length / 2;
    for (let i = 0; i 



これで終わりです。これらのピクセルで遊んだだけです。そして最後に、この記事のサムネイルはループ内のこの 1 行で作成されます。

rawBytes.data[i] = Math.floor(Math.random()*256)

各バイトをランダムに変更しただけです ?

完全なコードについては、私のリポジトリをチェックしてください:pixel-byte-manipulation

間違いがあればコメントしてください

ありがとう!!!

リリースステートメント この記事は次の場所に転載されています: https://dev.to/sanx/playing-with-raw-image-bytes-2k8a?1 侵害がある場合は、[email protected] に連絡して削除してください。
最新のチュートリアル もっと>
  • 数式が含まれている場合でも、Openpyxl でセルの生の値を取得する方法
    数式が含まれている場合でも、Openpyxl でセルの生の値を取得する方法
    Openpyxl で実際のセル値を取得する方法openpyxl を使用して Excel のセル値にアクセスすると、表示される値と、セルに数式が含まれている場合。これは、openpyxl が通常、数式を解釈して計算結果を取得するためです。数式を含む実際のセル値を取得するには、ワークブックのロード時に ...
    プログラミング 2024 年 11 月 8 日に公開
  • Go で UTF-8 文字列をバイト配列に効率的に変換するにはどうすればよいですか?
    Go で UTF-8 文字列をバイト配列に効率的に変換するにはどうすればよいですか?
    UTF-8 文字列をバイト配列に変換するJSON のアンマーシャリングにはバイト スライス入力が必要ですが、文字列は Go では UTF-8 として保存されます。この記事では、UTF-8 文字列からバイト配列への効率的な変換について説明します。直接変換Go では、文字列をバイト スライスに変換し、文...
    プログラミング 2024 年 11 月 8 日に公開
  • dpdm を使用して Redux の循環依存関係のバグを修正した方法
    dpdm を使用して Redux の循環依存関係のバグを修正した方法
    混乱の輪を断ち切る: Redux 循環依存関係の旅 最近、Redux コードベースでバグに遭遇し、頭を悩ませました。テスト スイートが意味のないエラーをスローしたときに突然混乱が起こるのを感じたことがあるなら、その気持ちがわかるでしょう。何が起こったのか、そして最終的に問題を発見(...
    プログラミング 2024 年 11 月 8 日に公開
  • 単一の MySQLi ステートメントで複数のクエリを準備できますか?
    単一の MySQLi ステートメントで複数のクエリを準備できますか?
    単一の MySQLi ステートメントでの複数のクエリの準備単一の MySQLi ステートメントで複数のクエリを準備することはできません。各 mysqli_prepare() 呼び出しで準備できるクエリは 1 つだけです。複数のクエリを実行するための代替アプローチ複数のクエリを一度に実行する必要がある...
    プログラミング 2024 年 11 月 8 日に公開
  • Golang でマップを安全に使用する: 宣言と初期化の違い
    Golang でマップを安全に使用する: 宣言と初期化の違い
    導入 今週、私は golang の API ラッパー パッケージの 1 つに取り組んでいました。これは、URL エンコードされた値を含む投稿リクエストの送信、Cookie の設定、その他すべての楽しいことを扱いました。ただし、本文を構築している間、url.Value 型を使用して本...
    プログラミング 2024 年 11 月 8 日に公開
  • 次の目標
    次の目標
    私は 9.1/10 という素晴らしいスコアで論文を完成させたところです。これを本当に誇りに思っています。私の論文を雑誌論文として出版することを目的とした REV-ECIT 2024 への投稿の締め切りは 9 月 30 日です。私は現在、博士課程の指導教官のサポートを受けながら自分の研究に磨きをかけ...
    プログラミング 2024 年 11 月 8 日に公開
  • Better - AI を活用したコードレビューアー GitHub Action
    Better - AI を活用したコードレビューアー GitHub Action
    コードレビューは、標準を維持し、プロジェクト内のコードのベストプラクティスを強調する上で常に重要です。これは開発者がコードをどのようにレビューすべきかについての投稿ではなく、コードの一部を AI に委任することについての投稿です。 Michael Lynch が投稿「人間のようにコード レビューを行...
    プログラミング 2024 年 11 月 8 日に公開
  • Java 8 を使用してリスト内の単語の頻度を効率的にカウントするにはどうすればよいですか?
    Java 8 を使用してリスト内の単語の頻度を効率的にカウントするにはどうすればよいですか?
    Java 8 を使用した単語の出現頻度のカウントWeb 開発やデータ分析では、単語の出現頻度を理解することが重要です。これを達成するために、Java 8 を使用してリスト内の単語の頻度をカウントする方法を詳しく説明します。Java 8 ソリューションJava 8 の Stream API は、単語用...
    プログラミング 2024 年 11 月 8 日に公開
  • カプセル化とは何か、そしてその使用方法。
    カプセル化とは何か、そしてその使用方法。
    カプセル化とは何ですか? Java のカプセル化とは、何かがどのように動作するかの詳細を隠しながら、他の人がそれを使用できるようにすることです。データ (変数など) とメソッド (関数など) をクラスと呼ばれる 1 つの単位にグループ化します。誰もがデータに直接アクセスできるようにする代わりに、デー...
    プログラミング 2024 年 11 月 8 日に公開
  • Java でのバイナリ ツリーの反転
    Java でのバイナリ ツリーの反転
    最近、アルゴリズム/データ構造のスキルを向上させるために、LeetCode の演習をいくつか練習し始めました。このプラットフォームは、他の開発者と複数のプログラミング言語でソリューションを練習して学習したり、他の開発者とソリューションを議論したり共有したり、大企業から要求されたコードの課題を練習した...
    プログラミング 2024 年 11 月 8 日に公開
  • Python で数値の因数を効率的に見つけるにはどうすればよいですか?
    Python で数値の因数を効率的に見つけるにはどうすればよいですか?
    Python で数値の因数を効率的に見つける数値の因数を求めることはさまざまな分野で一般的なタスクであり、Python では複数の機能が提供されます。 最適化されたアプローチの 1 つは、リスト内包表記とともに Python の reduce 関数を利用することです。この簡潔なソリューションは、指定...
    プログラミング 2024 年 11 月 8 日に公開
  • JavaScript のジレンマ: スクリプトの埋め込みかインライン実行か?
    JavaScript のジレンマ: スクリプトの埋め込みかインライン実行か?
    外部スクリプト タグ内の JavaScript: ソースのジレンマ で外部スクリプト タグを使用する場合構文の制限を理解することが重要です。以下の例のように、JavaScript をこれらのタグ内に直接埋め込もうとすると、予期しない動作が発生します:<script src="myFi...
    プログラミング 2024 年 11 月 8 日に公開
  • プロパティフック PHP なし
    プロパティフック PHP なし
    11 月には、私たちが愛する PHP のバージョン 8.4 がリリースされます。それに伴い、コミュニティが待ち望んでいた新機能、プロパティ フックが追加されます。 C#、Swift、Kotlin などの他の言語からインスピレーションを得たこの新機能は、魔法のメソッド __set() や __get(...
    プログラミング 2024 年 11 月 8 日に公開
  • サーバーとクライアントをブロックせずに、サーバーにアップロードされたファイルの現在書き込みサイズをリアルタイムで読み取り、エコーする方法は?
    サーバーとクライアントをブロックせずに、サーバーにアップロードされたファイルの現在書き込みサイズをリアルタイムで読み取り、エコーする方法は?
    サーバーとクライアントをブロックせずに、サーバー側で書き込まれているアップロードされたファイルのサイズをリアルタイムで読み取り、出力するにはどうすればよいですか? この問題についてさらに詳しく説明します:ファイルのアップロードの進行状況をリアルタイムで取得するために、フェッチを通じて Blob、Fi...
    プログラミング 2024 年 11 月 8 日に公開
  • Python でうるう年を決定する方法: 包括的なガイド
    Python でうるう年を決定する方法: 包括的なガイド
    Python を使用したうるう年の計算年がうるう年かどうかの判断には、プログラムで評価できる特定の基準が必要です。うるう年は、100 で割り切れるが 400 で割り切れない年を除き、4 で割り切れます。この問題に対する考えられるアプローチの 1 つは、うるう年をチェックするカスタム関数を実装すること...
    プログラミング 2024 年 11 月 8 日に公開

免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。

Copyright© 2022 湘ICP备2022001581号-3