Hallo Leute, heute lasst uns mit Bild-Rohbytes spielen. Was ich tun werde, ist, die Rohpixelbytes des Bildes zu manipulieren und etwas Albernes zu machen, das macht Spaß. Dieser Artikel enthält sowohl etwas Theorie als auch praktische Umsetzung. Also los geht's…
Da wir wissen, dass ein Bild aus einer Reihe von Pixeln besteht, ist Pixel nichts anderes als eine Kombination aus RGB (Rot, Grün, Blau) oder RGBA (Rot, Grün, Blau, Alpha) jeweils mit 1 Byte.
Die Bilder, die wir mit Erweiterungen wie PNG oder JPG ansehen, sind die komprimierten Formate des Bildes. PNG ist verlustfrei, PNG verwendet Algorithmen wie DEFLATE, um ohne Pixelverlust zu komprimieren, und JPG ist eine verlustbehaftete Komprimierung Wir verlieren einige Pixel, sodass es zu einem gewissen Verlust an Bildqualität kommt. Wenn wir das Bild ohne Komprimierung anzeigen möchten, müssen wir das Bild in BMP (Bitmap-Bilddatei) konvertieren, oder es gibt auch andere Formate, wenn wir in dieses konvertieren, erhalten wir das unkomprimierte Bild. Aber wir brauchen das nicht, wir extrahieren diese Rohbytes, spielen damit und konvertieren sie wieder zurück in PNG oder JPG.
Zuerst stellen wir den Client so ein, dass er Bilder hochlädt. Ich werde dafür eine einfache Reaktionsanwendung einrichten
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;
Das ist also einfacher Code zum Hochladen der Bilder, der Hauptteil befindet sich auf der Serverseite.
Lassen Sie uns nun die Bildbytes manuell berechnen und mit dem serverseitigen Code überprüfen.
Ich habe das folgende Bild ausgewählt.
Das Bild stammt aus der Miniaturansicht meines vorherigen Artikels. Es handelt sich also um eine PNG-Datei. Wenn wir zum Abschnitt „Eigenschaften“ gehen, können wir die Breite und Höhe des Bildes sehen. Hier betragen Breite und Höhe 722 x 407, was 293854 Pixeln entspricht. Dabei handelt es sich nicht um die Gesamtzahl der Bytes, sondern nur um die Gesamtzahl der Pixel. Wie wir wissen, besteht jedes Pixel entweder aus 3 oder 4 Bytes, RGB oder RGBA. Wenn das obige Bild also RGB ist, wären die Gesamtbytes 722 x 407 x 3 = 881562, oder wenn das Bild den Alphakanal hätte, wären die Gesamtbytes 722 x 407 x 4 = 1175416.
Lassen Sie uns etwas zur Serverseite sagen, ich verwende den Knoten js.
Es gibt eine Bibliothek namens Multer zum Parsen von Multiform-Daten.
app.post("/img", upload.single("img"), async (req, res) => { const arr = req.file.buffer console.log(arr.length) //output: 30929 res.send("success") });
Wir speichern die Bildbytes im Pufferarray. Wenn wir die Länge des Pufferarrays nehmen, lautet die Antwort 30929. Das Array enthält so viele Bytes, aber warten Sie, die Gesamtzahl der Bytes sollte 1175416 betragen, oder? Was hier passiert, ist, dass Multer keine Komprimierung oder ähnliches durchführt, sondern nur das Bild vom Benutzer erhält und es so wie es ist im Puffer speichert. Wir haben also die PNG-Datei hochgeladen, der Puffer, den Sie sehen, hat die gleiche Größe wie die PNG-Bildgröße.
Jetzt ändern wir die Bytes im komprimierten Bildbyte.
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"); });
Ich habe das fs verwendet, um mit dem vorhandenen ein neues Bild zu erstellen. Wenn wir nun also das erste Byte arr[0] = 231 ändern, wird das Bild nicht geöffnet.
Da die ersten bestimmten Bytes für die Metadaten reserviert sind, kann das Bild beschädigt werden, wenn wir diese Metadaten ändern.
Also lasst uns zum 500. Byte springen. arr[500] = 123, dann schreibe das Bild. Aber jetzt, da das Bild kaputt ist, sollten wir die komprimierten Bildbytes nicht direkt manipulieren, da dies die codierten Daten des Komprimierungsalgorithmus ändern kann.
Wir benötigen die Rohbytes aus dem Bild, und dann können wir die Bytes unabhängig manipulieren, und dafür können wir eine sharp-Bibliothek verwenden.
npm install scharf
Installieren Sie das Sharp. Jetzt werde ich eine separate Datei erstellen, um diese Logiken zu verwalten,
sharp.js
export async function convert(buffer) { try { const data = await sharp(buffer).metadata(); console.log(data) }catch(err){ console.log(err) } }
Dies ist eine asynchrone Funktion. Jetzt holen wir uns die Metadaten aus dem PNG, das wir hochgeladen haben.
{ format: 'png', size: 30929, width: 722, height: 407, space: 'srgb', channels: 4, depth: 'uchar', density: 72, isProgressive: false, hasProfile: false, hasAlpha: true }
Dies sind die Metadaten aus dem Bild, wie wir sehen können, haben die letzten Daten Alpha: true, also hat es den Alphakanal, also ist jedes Pixel 4 Bytes groß.
Jetzt holen wir uns die Rohbytes aus dem Bild.
const rawBytes = await sharp(buffer) .raw() .toBuffer({ resolveWithObject: true }); console.log(rawBytes.data.length) //1175416
Jetzt können wir sehen, dass die Array-Länge unserer Berechnung entspricht. Dieses Bild enthält also 1175416 Bytes. Jetzt können wir beliebige Bytes ändern. Jetzt werden die Metadaten nicht mehr im Puffer gespeichert, der Puffer enthält nur die Rohbytes des Bildes.
Lassen Sie uns nur ein Pixel in Rot ändern.
rawBytes.data[0] = 225; //red rawBytes.data[1] = 10; //green rawBytes.data[2] = 10; //blue rawBytes.data[3] = Math.floor(0.8 * 255); //alpha
Jetzt teilen wir das Bild und ändern die Farbe, die obere Hälfte ist gelb und die untere Hälfte ist grün
Ich habe vergessen, zum Schreiben des Bildes anzugeben, wir brauchen kein fs, um ein neues Bild zu schreiben, wir können das Sharp selbst verwenden.
rawBytes.data[0] = 225; //red rawBytes.data[1] = 10; //green rawBytes.data[2] = 10; //blue rawBytes.data[3] = Math.floor(0.8 * 255); //alphaWir generieren das neue Bild mit denselben Metadaten.
Hier ist der vollständige serverseitige Code,
//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 scharf aus „scharf“ importieren; Asynchrone Funktion exportieren, konvertieren (Puffer) { versuchen { const data = wait scharf(buffer).metadata(); console.log(data); //Rohdaten const rawBytes = Warten auf Sharp(Puffer) .roh() .toBuffer({ discoverWithObject: true }); console.log(rawBytes.data.length); const div = rawBytes.data.length / 2; for (let i = 0; i rawBytes.data[0] = 225; //red rawBytes.data[1] = 10; //green rawBytes.data[2] = 10; //blue rawBytes.data[3] = Math.floor(0.8 * 255); //alpha Das ist es also. Wir haben gerade mit diesen Pixeln gespielt. und schließlich wird dieses Miniaturbild des Artikels mit dieser einen Zeile in der Schleife erstellt.
rawBytes.data[0] = 225; //red rawBytes.data[1] = 10; //green rawBytes.data[2] = 10; //blue rawBytes.data[3] = Math.floor(0.8 * 255); //alphaIch habe einfach jedes Byte zufällig geändert?
Den vollständigen Code finden Sie in meinem Repo: Pixel-Byte-Manipulation
Wenn es Fehler gibt, kommentieren Sie bitte
Danke schön!!!
Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.
Copyright© 2022 湘ICP备2022001581号-3