"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Brincando com bytes de imagem bruta

Brincando com bytes de imagem bruta

Publicado em 01/11/2024
Navegar:945

Olá pessoal, hoje vamos brincar com bytes brutos de imagem. O que vou fazer é manipular os bytes de pixel brutos da imagem e fazer algo bobo, isso é divertido de fazer. Este artigo contém alguma teoria, bem como implementação prática. Então vamos lá…

Como sabemos que uma imagem é formada por um monte de pixels juntos, pixel nada mais é do que uma combinação de RGB (Vermelho, Verde, Azul) ou RGBA (Vermelho, Verde, Azul, Alfa) cada um com 1 byte.

As imagens que visualizamos com extensões como PNG ou JPG são os formatos compactados da imagem, PNG é sem perdas que PNG usa algoritmos como DEFLATE para compactar sem perder o pixel e JPG é uma compactação com perdas que irá perder algum pixel então haverá alguma perda na qualidade da imagem, se quisermos visualizar a imagem sem a compressão precisamos converter a imagem para o BMP (Bitmap Image File) ou também existem alguns outros formatos, se convertermos para isso obteremos a imagem descompactada. Mas não precisamos disso, extrairemos esses bytes brutos e brincaremos com eles e os converteremos novamente para PNG ou JPG.

Primeiro, vamos configurar o cliente para fazer upload de imagens, vou configurar um aplicativo de reação simples para isso

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;

Então esse é um código simples, para fazer upload das imagens, a parte principal fica no lado do servidor.

Agora vamos calcular manualmente os bytes da imagem e verificar com o código do lado do servidor.

Escolhi a imagem abaixo.

Playing with raw image bytes

A imagem é da miniatura do meu artigo anterior. Então este é um arquivo PNG, se formos para a seção de propriedades podemos ver a largura e a altura da imagem. Para isso a largura e a altura são 722 x 407 o que equivale a 293854 pixels, também não é um número total de bytes, é apenas um número total de pixels. Como sabemos, cada pixel tem 3 ou 4 bytes, RGB ou RGBA. Portanto, se a imagem acima for RGB, o total de bytes seria 722 x 407 x 3 = 881562 ou se a imagem tivesse o canal alfa, o total de bytes seria 722 x 407 x 4 = 1175416.

Vamos para o lado do servidor, estou usando o node js.

Existe uma biblioteca chamada multer para analisar dados multiformes.

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

Armazenamos os bytes da imagem no array de buffer, se pegarmos o comprimento do array de buffer a resposta é 30929, existem muitos bytes no array, mas espere o número total de bytes deve ser 1175416 certo? O que acontece aqui é que o multer não faz nenhuma compactação nem nada, ele apenas pega a imagem do usuário e a armazena no buffer como está, então carregamos o arquivo PNG, o buffer que você está vendo é do mesmo tamanho que o Tamanho da imagem PNG.

Agora vamos alterar os bytes no byte da imagem compactada.

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

Usei o fs para criar uma nova imagem com a existente. Portanto, agora, se alterarmos o primeiro byte arr[0] = 231, a imagem não será aberta.

Playing with raw image bytes

Porque os primeiros bytes são reservados para os metadados, se alterarmos esses metadados, a imagem pode ser corrompida.

Então, vamos pular para o 500º byte. arr[500] = 123, então escreva a imagem. Mas agora que a imagem está quebrada, não devemos manipular diretamente os bytes da imagem compactada porque isso pode alterar os dados codificados do algoritmo de compactação.

Precisamos dos bytes brutos da imagem e, então, podemos manipular os bytes de forma independente e, para isso, podemos usar uma biblioteca sharp.

instalação npm nítida

instale o sharp, agora vou criar um arquivo separado para lidar com essas lógicas,

sharp.js

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

Esta é uma função assíncrona. Agora vamos obter os metadados do png que carregamos.

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

Estes são os metadados da imagem, como podemos ver os últimos dados hasAlpha: true então tem o canal alfa, então cada pixel tem 4 bytes.

Agora vamos obter os bytes brutos da imagem.

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

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

Agora podemos ver que o comprimento do array é igual ao nosso cálculo. Portanto, esta imagem contém 1175416 bytes. Agora estamos livres.. para alterar quaisquer bytes, agora os metadados não são armazenados no buffer, o buffer contém apenas os bytes brutos da imagem.

Vamos mudar apenas um pixel para vermelho.

  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

Como podemos, um pixel é alterado para vermelho, precisamos aumentar o zoom na imagem para ver a mudança de pixel.

Agora vamos dividir a imagem e mudar a cor, a metade superior é amarela e a metade inferior é verde

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



Estamos incrementando o loop 4 vezes porque estamos alterando um pixel a cada iteração. Agora a saída será assim.

Playing with raw image bytes

Podemos ver a transparência nesta imagem porque o canal Alpha está definido como 0,8

Esqueci de avisar para escrever a imagem, não precisamos de fs para escrever uma nova imagem, podemos usar o próprio sharp.

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

estamos gerando a nova imagem com os mesmos metadados.

Aqui está o código completo do lado do servidor,

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



Então é isso, acabamos de brincar com esses pixels. e, finalmente, a miniatura deste artigo é feita com esta linha no loop.

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

Eu apenas mudei aleatoriamente cada byte?

para o código completo, confira meu repositório: pixel-byte-manipulation

se houver algum erro por favor comente

Obrigado!!!

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/sanx/playing-with-raw-image-bytes-2k8a?1 Se houver alguma violação, entre em contato com [email protected] para excluí-la
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3