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

处理原始图像字节

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

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

众所周知,图像是由一堆像素组合而成,像素只不过是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]删除
最新教程 更多>
  • 如何使用不同数量列的联合数据库表?
    如何使用不同数量列的联合数据库表?
    合并列数不同的表 当尝试合并列数不同的数据库表时,可能会遇到挑战。一种直接的方法是在列数较少的表中,为缺失的列追加空值。 例如,考虑两个表,表 A 和表 B,其中表 A 的列数多于表 B。为了合并这些表,同时处理表 B 中缺失的列,请按照以下步骤操作: 确定表 B 中缺失的列,并将它们添加到表的末...
    编程 发布于2025-03-12
  • 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-03-12
  • 如何从Python中的字符串中删除表情符号:固定常见错误的初学者指南?
    如何从Python中的字符串中删除表情符号:固定常见错误的初学者指南?
    从python import codecs import codecs import codecs 导入 text = codecs.decode('这狗\ u0001f602'.encode('utf-8'),'utf-8') 印刷(文字)#带有...
    编程 发布于2025-03-12
  • 如何使用组在MySQL中旋转数据?
    如何使用组在MySQL中旋转数据?
    在关系数据库中使用mySQL组使用mySQL组进行查询结果,在关系数据库中使用MySQL组,转移数据的数据是指重新排列的行和列的重排以增强数据可视化。在这里,我们面对一个共同的挑战:使用组的组将数据从基于行的基于列的转换为基于列。 Let's consider the following ...
    编程 发布于2025-03-12
  • 如何克服PHP的功能重新定义限制?
    如何克服PHP的功能重新定义限制?
    克服PHP的函数重新定义限制在PHP中,多次定义一个相同名称的函数是一个no-no。尝试这样做,如提供的代码段所示,将导致可怕的“不能重新列出”错误。 但是,PHP工具腰带中有一个隐藏的宝石:runkit扩展。它使您能够灵活地重新定义函数。 runkit_function_renction_re...
    编程 发布于2025-03-12
  • 为什么使用Firefox后退按钮时JavaScript执行停止?
    为什么使用Firefox后退按钮时JavaScript执行停止?
    导航历史记录问题:JavaScript使用Firefox Back Back 此行为是由浏览器缓存JavaScript资源引起的。要解决此问题并确保在后续页面访问中执行脚本,Firefox用户应设置一个空功能。 警报'); }; alert('inline Alert')...
    编程 发布于2025-03-12
  • 如何从PHP中的数组中提取随机元素?
    如何从PHP中的数组中提取随机元素?
    从阵列中的随机选择,可以轻松从数组中获取随机项目。考虑以下数组:; 从此数组中检索一个随机项目,利用array_rand( array_rand()函数从数组返回一个随机键。通过将$项目数组索引使用此键,我们可以从数组中访问一个随机元素。这种方法为选择随机项目提供了一种直接且可靠的方法。
    编程 发布于2025-03-12
  • 版本5.6.5之前,使用current_timestamp与时间戳列的current_timestamp与时间戳列有什么限制?
    版本5.6.5之前,使用current_timestamp与时间戳列的current_timestamp与时间戳列有什么限制?
    在时间戳列上使用current_timestamp或MySQL版本中的current_timestamp或在5.6.5 此限制源于遗留实现的关注,这些限制需要对当前的_timestamp功能进行特定的实现。 创建表`foo`( `Productid` int(10)unsigned not n...
    编程 发布于2025-03-12
  • 如何使用PHP从XML文件中有效地检索属性值?
    如何使用PHP从XML文件中有效地检索属性值?
    从php $xml = simplexml_load_file($file); foreach ($xml->Var[0]->attributes() as $attributeName => $attributeValue) { echo $attributeName,...
    编程 发布于2025-03-12
  • 如何从Google API中检索最新的jQuery库?
    如何从Google API中检索最新的jQuery库?
    从Google APIS 问题中提供的jQuery URL是版本1.2.6。对于检索最新版本,以前有一种使用特定版本编号的替代方法,它是使用以下语法:获取最新版本:未压缩)While these legacy URLs still remain in use, it is recommended ...
    编程 发布于2025-03-12
  • 如何干净地删除匿名JavaScript事件处理程序?
    如何干净地删除匿名JavaScript事件处理程序?
    删除匿名事件侦听器将匿名事件侦听器添加到元素中会提供灵活性和简单性,但是当要删除它们时,可以构成挑战,而无需替换元素本身就可以替换一个问题。 element? element.addeventlistener(event,function(){/在这里工作/},false); 要解决此问题,请考虑...
    编程 发布于2025-03-12
  • 哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    在Python Matplotlib's path.contains_points FunctionMatplotlib's path.contains_points function employs a path object to represent the polygon.它...
    编程 发布于2025-03-12
  • 可以在纯CS中将多个粘性元素彼此堆叠在一起吗?
    可以在纯CS中将多个粘性元素彼此堆叠在一起吗?
    [2这里: https://webthemez.com/demo/sticky-multi-header-scroll/index.html </main> <section> { display:grid; grid-template-...
    编程 发布于2025-03-12
  • PHP阵列键值异常:了解07和08的好奇情况
    PHP阵列键值异常:了解07和08的好奇情况
    PHP数组键值问题,使用07&08 在给定数月的数组中,键值07和08呈现令人困惑的行为时,就会出现一个不寻常的问题。运行print_r($月份)返回意外结果:键“ 07”丢失,而键“ 08”分配给了9月的值。此问题源于PHP对领先零的解释。当一个数字带有0(例如07或08)的前缀时,PHP将...
    编程 发布于2025-03-12
  • jQuery滚动动画入门教程详解
    jQuery滚动动画入门教程详解
    核心要点 基于滚动的动画和特效是一种让网页开发者创建动态交互式网页体验的技术。它们在用户向下滚动页面时触发,并能用CSS和jQuery进行操控和实现。 要创建响应式的基于滚动的特效,必须定义浏览器窗口的宽度和高度属性。如果没有这些属性,当用户调整窗口大小时,特效将无法正常工作。 本教程提供了四个基...
    编程 发布于2025-03-12

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

Copyright© 2022 湘ICP备2022001581号-3