如何使用向量 SSE 运算将图像像素数据的字节数组转换为灰度

2023-12-21

我在转换存储在中的图像数据时遇到问题byte[] array到灰度。我想使用矢量 SIMD 操作,因为将来需要编写 ASM 和 C++ DLL 文件来测量操作时间。

当我阅读有关 SIMD 的内容时,我发现 SSE 命令是在 128 位寄存器上运行的,因此存在问题,因为我需要将我的byte[] array分成几个Vector<T>存储到List<T>.

图像是四通道 RGBA JPEG,所以我还需要知道如何使用基于单个 128 位的 R、G、B 数据创建矢量Vector<T>。之后,我可以使用灰度算法

fY(R,G,B)=R×0.29891+G×0.58661+B×0.11448


总而言之,问题是:

  1. 如何加载大块byte[] array存入 128 位寄存器Vector<T>.
  2. 一个人如何分开Vector<T>R、G、B 值相乘并复制到源向量。

它需要 System.Runtime.Intrinsics.Experimental.dll 并且不安全,但它相对简单,并且对于许多实际应用程序来说可能足够快。

/// <summary>Load 4 pixels of RGB</summary>
static unsafe Vector128<int> load4( byte* src )
{
    return Sse2.LoadVector128( (int*)src );
}

/// <summary>Pack red channel of 8 pixels into ushort values in [ 0xFF00 .. 0 ] interval</summary>
static Vector128<ushort> packRed( Vector128<int> a, Vector128<int> b )
{
    Vector128<int> mask = Vector128.Create( 0xFF );
    a = Sse2.And( a, mask );
    b = Sse2.And( b, mask );
    return Sse2.ShiftLeftLogical128BitLane( Sse41.PackUnsignedSaturate( a, b ), 1 );
}

/// <summary>Pack green channel of 8 pixels into ushort values in [ 0xFF00 .. 0 ] interval</summary>
static Vector128<ushort> packGreen( Vector128<int> a, Vector128<int> b )
{
    Vector128<int> mask = Vector128.Create( 0xFF00 );
    a = Sse2.And( a, mask );
    b = Sse2.And( b, mask );
    return Sse41.PackUnsignedSaturate( a, b );
}

/// <summary>Pack blue channel of 8 pixels into ushort values in [ 0xFF00 .. 0 ] interval</summary>
static Vector128<ushort> packBlue( Vector128<int> a, Vector128<int> b )
{
    a = Sse2.ShiftRightLogical128BitLane( a, 1 );
    b = Sse2.ShiftRightLogical128BitLane( b, 1 );
    Vector128<int> mask = Vector128.Create( 0xFF00 );
    a = Sse2.And( a, mask );
    b = Sse2.And( b, mask );
    return Sse41.PackUnsignedSaturate( a, b );
}

/// <summary>Load 8 pixels, split into RGB channels.</summary>
static unsafe void loadRgb( byte* src, out Vector128<ushort> red, out Vector128<ushort> green, out Vector128<ushort> blue )
{
    var a = load4( src );
    var b = load4( src + 16 );
    red = packRed( a, b );
    green = packGreen( a, b );
    blue = packBlue( a, b );
}

const ushort mulRed = (ushort)( 0.29891 * 0x10000 );
const ushort mulGreen = (ushort)( 0.58661 * 0x10000 );
const ushort mulBlue = (ushort)( 0.11448 * 0x10000 );

/// <summary>Compute brightness of 8 pixels</summary>
static Vector128<short> brightness( Vector128<ushort> r, Vector128<ushort> g, Vector128<ushort> b )
{
    r = Sse2.MultiplyHigh( r, Vector128.Create( mulRed ) );
    g = Sse2.MultiplyHigh( g, Vector128.Create( mulGreen ) );
    b = Sse2.MultiplyHigh( b, Vector128.Create( mulBlue ) );
    var result = Sse2.AddSaturate( Sse2.AddSaturate( r, g ), b );
    return Vector128.AsInt16( Sse2.ShiftRightLogical( result, 8 ) );
}

/// <summary>Convert buffer from RGBA to grayscale.</summary>
/// <remarks>
/// <para>If your image has line paddings, you'll want to call this once per line, not for the complete image.</para>
/// <para>If width of the image is not multiple of 16 pixels, you'll need to do more work to handle the last few pixels of every line.</para>
/// </remarks>
static unsafe void convertToGrayscale( byte* src, byte* dst, int count )
{
    byte* srcEnd = src + count * 4;
    while( src < srcEnd )
    {
        loadRgb( src, out var r, out var g, out var b );
        var low = brightness( r, g, b );
        loadRgb( src + 32, out r, out g, out b );
        var hi = brightness( r, g, b );

        var bytes = Sse2.PackUnsignedSaturate( low, hi );
        Sse2.Store( dst, bytes );

        src += 64;
        dst += 16;
    }
}

然而,等效的 C++ 实现会更快。 C# 在内联这些函数方面做得不错,即convertToGrayscale不包含任何函数调用。 但该函数的代码远非最佳。 .NET 无法传播常量,对于幻数,它在循环内发出如下代码:

mov         r8d,962Ch
vmovd       xmm1,r8d
vpbroadcastw xmm1,xmm1

生成的代码仅使用 16 个寄存器中的 6 个。有足够的可用寄存器来容纳所有涉及的幻数。

此外.NET 还发出许多冗余指令,这些指令只是对数据进行打乱:

vmovaps xmm2, xmm0
vmovaps xmm3, xmm1
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用向量 SSE 运算将图像像素数据的字节数组转换为灰度 的相关文章

随机推荐

  • 是否可以在运行时传递 Typescript 装饰器对象值?

    我有一个用 MinDate 约束装饰的类 如下所示 export default class Order purchaseDate Date MinDate this purchaseDate receiptDate Date 当尝试验证一
  • 在 mvc ef 中动态选择两个标签作为显示名称

    我正在使用实体框架 MVC 如何在一个数据字段的两个标签之间动态更改 基于从同一车辆的另一个数据库检索的数据 理想情况下 我希望我的模型类中包含类似的内容 这是伪代码 预计不会编译 Display Name resCatalyst Reso
  • frama-c生成的pdgs中的圆节点是什么意思

    我使用frama c工具来分析下面的代码 int main int argc char argv int i a for i 0 i lt 100 i 1 a 0 if a 0 continue else break return 0 命令
  • 您如何知道 Kafka 代理上的主题何时创建?

    你怎么知道Kafka中的主题是什么时候创建的 似乎有一些主题是使用错误数量的分区创建的 有没有办法知道主题的创建日期 据说 创建了一个名为 test 的主题n分区数 如何找到在 Kafka 上创建此 测试 主题的日期和时间 可以看到Kafk
  • 使用 AFNetworking 3.0 上传图像[关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我正在尝试使用 AFNetworking 3 0 将图像上传到服务器 这是我的代码 AFHTTPSessionManag
  • 将 kerberos 票证添加到肥皂请求

    我有一个问题如下 我有一个 Web 服务 并且我想使用 Kerberos 进行身份验证 我通过添加服务引用来添加我的 Visual Studio wsdl 它创建了一个代理类 可以这么说 reference cs 可以调用该服务 他现在想要
  • 通过HCE付款

    我一直在开发需要实现点击付款的应用程序 我能够将 HCE 服务与 NFC 终端连接 现在我的问题是接下来的步骤是什么 用它进行实际付款 我到处搜索 但找不到合适的文档 请帮我 下面是我编写的用于将 HCE 服务连接到 NFC 终端的代码 安
  • Plotly:如何更改绘图表达散点图的配色方案?

    我正在尝试与plotly https plotly com 具体来说ploty express https plotly com python plotly express 构建一些可视化 我正在建造的东西之一是散点图 https plot
  • Android 选项菜单项中没有显示图标[重复]

    这个问题在这里已经有答案了 我创建了一个带有我自己制作的图标 24px x 24px 的选项菜单 但它没有显示 我的 xml 中的代码 位于 res menu 中 如下 menu menu
  • 创建一个循环来检查排列中的循环

    我的家庭作业让我检查用户输入的数字中所有可能的循环符号 我已将输入发送到数组中 但我不确定如何启动循环 我如何编辑此循环以不多次显示相同的数字 第一次发帖 格式不对 请见谅 example of user input var permuta
  • 这可能是初学者最好的backbonejs教程[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我对backbone js很感兴趣 但当我学习的时候 在创建结构时有很多困惑 有人可以向我展示一个带有很好示例的简单教程吗 这对所有新学习
  • Bootstrap Carousel 多个项目一次移动一个项目[重复]

    这个问题在这里已经有答案了 我尝试使用引导程序创建多个项目轮播 我的问题是它会同时移动所有 3 个项目 而不是仅移动一个 请在此处查看演示 http plnkr co edit Fl0HZaU5x5ZkPEVo87u3 p preview
  • 双重释放或腐败(fasttop)

    我的代码的以下部分在执行时给了我这条消息 检测到glibc a out 双重释放或损坏 fasttop 0x08e065d0 问题就在这里 temp2 first 基本上 当您释放 temp2 时 您首先释放 而不是此处分配的内存 temp
  • 无法将动态生成的图像从任何浏览器粘贴到 MS Word

    我有一个生成图像然后将其推送到浏览器的应用程序 图像显示完全没有困难 还可以右键保存 并且可以毫无问题地粘贴到Gimp等应用程序中 但不能粘贴到MS Word中 我摆弄了应用程序的各个方面 以确保内容类型和所有其他标题都正确 但这对粘贴图像
  • 如何设置上传文件的最大大小

    我正在使用 JHipster 开发基于 Spring Boot 和 AngularJS 的应用程序 我的问题是如何设置上传文件的最大大小 如果我尝试上传到大文件 我会在控制台中收到以下信息 DEBUG 11768 io 8080 exec
  • 如何将回调函数传递给 StreamController

    我想知道我正在创建这样的 StreamController class StreamController controller new StreamController onListen onListen onPause onPause o
  • 谷歌云存储交易?

    看来GCS没有任何交易机制 它是否正确 我希望能够进行长期交易 例如 如果我可以启动一个事务并指定过期时间 如果未在 X 时间内提交 它将自动回滚 那就太好了 然后我可以使用这个句柄插入对象 组合 删除等 如果一切顺利 发出 isCommi
  • UIView 的 contentScaleFactor 取决于实现drawRect:?

    我偶然发现了一件奇怪的事情 看起来像UIView s contentScaleFactor即使在 Retina 设备上也始终为 1 除非您实现drawRect 考虑这段代码 interface MyView UIView end imple
  • 为 iOS 应用内购买提供折扣代码

    所以我知道 iOS 中没有用于应用内购买的促销代码 我想知道的是 苹果会拒绝这种机制吗 提供两种应用内购买 一种是全价 例如 9 99 美元 另一种是折扣价 例如 7 99 美元 对于同一商品 当您点击 7 99 美元的价格时 系统首先会要
  • 如何使用向量 SSE 运算将图像像素数据的字节数组转换为灰度

    我在转换存储在中的图像数据时遇到问题byte array到灰度 我想使用矢量 SIMD 操作 因为将来需要编写 ASM 和 C DLL 文件来测量操作时间 当我阅读有关 SIMD 的内容时 我发现 SSE 命令是在 128 位寄存器上运行的