”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 处理原始图像字节

处理原始图像字节

发布于2024-11-01
浏览:643

大家好,今天我们来玩一下图像原始字节。我要做的是,操纵图像原始像素字节,并做一些愚蠢的事情,这是有趣的事情。本文包含一些理论和实际实现,所以让我们开始......

众所周知,图像是由一堆像素组合而成,像素只不过是RGB(红、绿、蓝)RGBA(红、绿、蓝、 Alpha) 每个占用 1 个字节。

我们使用 PNG 或 JPG 等扩展名查看的图像是图像的压缩格式,PNG 是无损压缩,PNG 使用 DEFLATE 等算法进行压缩而不丢失像素,而 JPG 是有损压缩,它将丢失一些像素,因此图像质量会有所损失,如果我们想在不压缩的情况下查看图像,我们需要将图像转换为BMP(位图图像​​文件)或者还有其他一些格式,如果我们转换为这种格式,我们会得到未压缩的图像。但我们不需要这个,我们将提取这些原始字节并使用它们,我们将再次将它们转换回 PNG 或 JPG。

首先,让我们设置客户端上传图片,我将为此设置一个简单的React应用程序

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,或者如果图像具有 Alpha 通道,则总字节数将为 722 x 407 x 4 = 1175416。

让我们一些服务器端,我使用的是node 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 install Sharp

安装 Sharp,现在我将创建一个单独的文件来处理这些逻辑,

sharp.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 所以它有 Alpha 通道,所以每个像素是 4 个字节。

现在让我们从图像中获取原始字节。

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

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

现在我们可以看到数组长度等于我们的计算结果。所以这个图像包含 1175416 字节。 现在我们可以自由..更改任何字节,现在元数据不存储在缓冲区中,缓冲区仅包含图像的原始字节。

我们只将一个像素更改为红色。

  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

由于我们可以将一个像素更改为红色,因此我们需要放大图像才能看到像素变化。

现在我们来分割图像并改变颜色,上半部分是黄色,下半部分是绿色

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



我们将循环增加 4 倍,因为我们在每次迭代时都会更改一个像素。现在输出将是这样的。

Playing with raw image bytes

我们可以看到该图像的透明度,因为 Alpha 通道设置为 0.8

我忘了告诉你写入图像,我们不需要fs来写入新图像,我们可以使用sharp本身。

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 



所以就是这样,我们只是玩了这些像素。最后这篇文章的缩略图是用循环中的这一行制作的。

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

我只是随机改变了每个字节?

有关完整代码,请查看我的存储库:pixel-byte-manipulation

如有错误欢迎评论

谢谢你!!!

版本声明 本文转载于:https://dev.to/sanx/playing-with-raw-image-bytes-2k8a?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何使用Java.net.urlConnection和Multipart/form-data编码使用其他参数上传文件?
    如何使用Java.net.urlConnection和Multipart/form-data编码使用其他参数上传文件?
    使用http request 上传文件上传到http server,同时也提交其他参数,java.net.net.urlconnection and Multipart/form-data Encoding是普遍的。 Here's a breakdown of the process:Mu...
    编程 发布于2025-04-16
  • Python高效去除文本中HTML标签方法
    Python高效去除文本中HTML标签方法
    在Python中剥离HTML标签,以获取原始的文本表示 仅通过Python的MlStripper 来简化剥离过程,Python Standard库提供了一个专门的功能,MLSTREPERE,MLSTREPERIPLE,MLSTREPERE,MLSTREPERIPE,MLSTREPERCE,MLST...
    编程 发布于2025-04-16
  • 如何在php中使用卷发发送原始帖子请求?
    如何在php中使用卷发发送原始帖子请求?
    如何使用php 创建请求来发送原始帖子请求,开始使用curl_init()开始初始化curl session。然后,配置以下选项: curlopt_url:请求 [要发送的原始数据指定内容类型,为原始的帖子请求指定身体的内容类型很重要。在这种情况下,它是文本/平原。要执行此操作,请使用包含以下标头...
    编程 发布于2025-04-16
  • 如何使用FormData()处理多个文件上传?
    如何使用FormData()处理多个文件上传?
    )处理多个文件输入时,通常需要处理多个文件上传时,通常是必要的。 The fd.append("fileToUpload[]", files[x]); method can be used for this purpose, allowing you to send multi...
    编程 发布于2025-04-16
  • 如何在其容器中为DIV创建平滑的左右CSS动画?
    如何在其容器中为DIV创建平滑的左右CSS动画?
    通用CSS动画,用于左右运动 ,我们将探索创建一个通用的CSS动画,以向左和右移动DIV,从而到达其容器的边缘。该动画可以应用于具有绝对定位的任何div,无论其未知长度如何。问题:使用左直接导致瞬时消失 更加流畅的解决方案:混合转换和左 [并实现平稳的,线性的运动,我们介绍了线性的转换。这...
    编程 发布于2025-04-16
  • 如何检查对象是否具有Python中的特定属性?
    如何检查对象是否具有Python中的特定属性?
    方法来确定对象属性存在寻求一种方法来验证对象中特定属性的存在。考虑以下示例,其中尝试访问不确定属性会引起错误: >>> a = someClass() >>> A.property Trackback(最近的最新电话): 文件“ ”,第1行, AttributeError: SomeClass...
    编程 发布于2025-04-16
  • 点击显示图片的技巧及方法
    点击显示图片的技巧及方法
    网络上的大多数图像都是多余的。如果我可能有点混蛋,那么其中99%的人甚至根本没有帮助(尽管有极少数例外)。那是因为图像通常不补充他们应该支持的文本,而是用户,将永远加载和炸毁像某种绩效税之类的数据上限。 值得庆幸的是,这主要是一个设计问题,因为使图像表现效果和更易于用户友好比以前要容易得多。我们具有...
    编程 发布于2025-04-16
  • 如何修复\“常规错误:2006 MySQL Server在插入数据时已经消失\”?
    如何修复\“常规错误:2006 MySQL Server在插入数据时已经消失\”?
    How to Resolve "General error: 2006 MySQL server has gone away" While Inserting RecordsIntroduction:Inserting data into a MySQL database can...
    编程 发布于2025-04-16
  • 您如何在Laravel Blade模板中定义变量?
    您如何在Laravel Blade模板中定义变量?
    在Laravel Blade模板中使用Elegance 在blade模板中如何分配变量对于存储以后使用的数据至关重要。在使用“ {{}}”分配变量的同时,它可能并不总是最优雅的解决方案。幸运的是,Blade通过@php Directive提供了更优雅的方法: $ old_section =“...
    编程 发布于2025-04-16
  • HTML格式标签
    HTML格式标签
    HTML 格式化元素 **HTML Formatting is a process of formatting text for better look and feel. HTML provides us ability to format text without us...
    编程 发布于2025-04-16
  • 如何解决由于Android的内容安全策略而拒绝加载脚本... \”错误?
    如何解决由于Android的内容安全策略而拒绝加载脚本... \”错误?
    Unveiling the Mystery: Content Security Policy Directive ErrorsEncountering the enigmatic error "Refused to load the script..." when deployi...
    编程 发布于2025-04-16
  • CSS强类型语言解析
    CSS强类型语言解析
    您可以通过其强度或弱输入的方式对编程语言进行分类的方式之一。在这里,“键入”意味着是否在编译时已知变量。一个例子是一个场景,将整数(1)添加到包含整数(“ 1”)的字符串: result = 1 "1";包含整数的字符串可能是由带有许多运动部件的复杂逻辑套件无意间生成的。它也可以是故意从单个真理...
    编程 发布于2025-04-16
  • 如何使用PHP从XML文件中有效地检索属性值?
    如何使用PHP从XML文件中有效地检索属性值?
    从php PHP陷入困境。使用simplexmlelement :: attributes()函数提供了简单的解决方案。此函数可访问对XML元素作为关联数组的属性: - > attributes()为$ attributeName => $ attributeValue){ echo ...
    编程 发布于2025-04-16
  • 使用Lambda表达式与PyQt槽函数为何导致意外行为?
    使用Lambda表达式与PyQt槽函数为何导致意外行为?
    使用lambda表达式连接pyqt 中的插槽,可以使用lambda表达式将信号连接到插槽。但是,在某些方案中使用lambda表达式可能会导致意外行为。考虑以下代码:类mainwindow(qtgui.qwidget): def __init __(自我): ... ...
    编程 发布于2025-04-16
  • 包在构建时找不到原因及解决方法
    包在构建时找不到原因及解决方法
    fixing fixing“无法在go build Understanding the Package Directory StructureGo expects packages to reside in directories with the same name as their pack...
    编程 发布于2025-04-16

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3