GraphicsMagick 的 OpenCL 开发记录(十一)

<2022-03-26 Sat>

ImageMagicknumber_channelsPixelChannelMap结构体中的channeloffset成员的理解

这个标题有点长,可能文章的内容也有点长,但是思路越来越清晰。先来看PixelChannelMap的结构体定义:

typedef struct _PixelChannelMap
{PixelChannelchannel;PixelTraittraits;ssize_toffset;
} PixelChannelMap;

PixelChannelMapImageMagick_Image结构体中对应成员channel_map

PixelChannelMap*channel_map;

首先要说的是,channel_map在逐个计算像素的过程中非常重要,拿ImageMagickresize.c:HorizontalFilter()函数为例:

for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{doublealpha,gamma,pixel;PixelChannelchannel;PixelTraitresize_traits,traits;ssize_tj;ssize_tk;channel=GetPixelChannelChannel(image,i);traits=GetPixelChannelTraits(image,channel);resize_traits=GetPixelChannelTraits(resize_image,channel);if ((traits == UndefinedPixelTrait) ||(resize_traits == UndefinedPixelTrait))continue;if (((resize_traits & CopyPixelTrait) != 0) ||(GetPixelWriteMask(resize_image,q) <= (QuantumRange/2))){j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)stop-1.0)+0.5);k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+(contribution[j-start].pixel-contribution[0].pixel);SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],q);continue;}pixel=0.0;if ((resize_traits & BlendPixelTrait) == 0){/*No alpha blending.*/for (j=0; j < n; j++){k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+(contribution[j].pixel-contribution[0].pixel);alpha=contribution[j].weight;pixel+=alpha*p[k*GetPixelChannels(image)+i];}SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);continue;}/*Alpha blending.*/gamma=0.0;for (j=0; j < n; j++){k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+(contribution[j].pixel-contribution[0].pixel);alpha=contribution[j].weight*QuantumScale*GetPixelAlpha(image,p+k*GetPixelChannels(image));pixel+=alpha*p[k*GetPixelChannels(image)+i];gamma+=alpha;}gamma=PerceptibleReciprocal(gamma);SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
}

这个循环中的GetPixelChannels()函数就是返回number_channels的值:

static inline size_t GetPixelChannels(const Image *magick_restrict image)
{return(image->number_channels);
}

即处理每个像素的所有channel,通过GetPixelChannelChannel()函数以通道的offset成员获得对应的channel

static inline PixelChannel GetPixelChannelChannel(const Image *magick_restrict image,const ssize_t offset)
{return(image->channel_map[offset].channel);
}

返回的channelPixelChannel类型,这个定义在上面的文章中已经给出了:

typedef enum
{UndefinedPixelChannel = 0,RedPixelChannel = 0,CyanPixelChannel = 0,GrayPixelChannel = 0,LPixelChannel = 0,LabelPixelChannel = 0,YPixelChannel = 0,aPixelChannel = 1,GreenPixelChannel = 1,MagentaPixelChannel = 1,CbPixelChannel = 1,bPixelChannel = 2,BluePixelChannel = 2,YellowPixelChannel = 2,CrPixelChannel = 2,BlackPixelChannel = 3,AlphaPixelChannel = 4,IndexPixelChannel = 5,ReadMaskPixelChannel = 6,WriteMaskPixelChannel = 7,MetaPixelChannel = 8,CompositeMaskPixelChannel = 9,IntensityPixelChannel = MaxPixelChannels,  /* ???? */CompositePixelChannel = MaxPixelChannels,  /* ???? */SyncPixelChannel = MaxPixelChannels+1      /* not a real channel */
} PixelChannel;  /* must correspond to ChannelType */

看定义,我之前还在奇怪为什么enum类型指定了好多相同的0值,1值,现在终于明白了。即比如RGBCMYK两种形式RC都是第0个通道,GM都是第1个通道,依次类推。CMYK中的K就是black,在PixelChannel中对应的是BlackPixelChannel

重点就是SetPixelChannel()这个函数:

static inline void SetPixelChannel(const Image *magick_restrict image,const PixelChannel channel,const Quantum quantum,Quantum *magick_restrict pixel)
{if (image->channel_map[channel].traits != UndefinedPixelTrait)pixel[image->channel_map[channel].offset]=quantum;
}

这里忽略理解traits,最后一个参数pixel就是处理像素后的目标地址,代码中的channel是通过循环i获取的,offset是通过channel获取的,最终计算出了pixel的真正地址,然后将计算好的quantum赋值进去。

最后,channel_map中的channeloffset是在哪里初始化的?我对比了代码发现只在:

static inline void SetPixelChannelAttributes(const Image *magick_restrict image,const PixelChannel channel,const PixelTrait traits,const ssize_t offset)
{if ((ssize_t) channel >= MaxPixelChannels)return;if (offset >= MaxPixelChannels)return;image->channel_map[offset].channel=channel;image->channel_map[channel].offset=offset;image->channel_map[channel].traits=traits;
}

SetPixelChannelAttributes()只在InitializePixelChannelMap()函数中会被调用,InitializePixelChannelMap()这个函数有点熟悉,在之前了解number_channels的文章中做过了介绍,这个函数内部计算并初始化了number_channels的值。

我对GraphicsMagickImageMagick进行了比较,GraphicsMagickImageMagick进行了精简,代码:

for (y=0; y < (long) destination->rows; y++){doubleweight;DoublePixelPacketpixel;longj;register longi;pixel=zero;if ((destination->matte) || (destination->colorspace == CMYKColorspace)){doubletransparency_coeff,normalize;normalize=0.0;for (i=0; i < n; i++){j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+(contribution[i].pixel-contribution[0].pixel);weight=contribution[i].weight;transparency_coeff = weight * (1 - ((double) p[j].opacity/TransparentOpacity));pixel.red+=transparency_coeff*p[j].red;pixel.green+=transparency_coeff*p[j].green;pixel.blue+=transparency_coeff*p[j].blue;pixel.opacity+=weight*p[j].opacity;normalize += transparency_coeff;}normalize = 1.0 / (AbsoluteValue(normalize) <= MagickEpsilon ? 1.0 : normalize);pixel.red *= normalize;pixel.green *= normalize;pixel.blue *= normalize;q[y].red=RoundDoubleToQuantum(pixel.red);q[y].green=RoundDoubleToQuantum(pixel.green);q[y].blue=RoundDoubleToQuantum(pixel.blue);q[y].opacity=RoundDoubleToQuantum(pixel.opacity);}else{for (i=0; i < n; i++){j=(long) (y*(contribution[n-1].pixel-contribution[0].pixel+1)+(contribution[i].pixel-contribution[0].pixel));weight=contribution[i].weight;pixel.red+=weight*p[j].red;pixel.green+=weight*p[j].green;pixel.blue+=weight*p[j].blue;}q[y].red=RoundDoubleToQuantum(pixel.red);q[y].green=RoundDoubleToQuantum(pixel.green);q[y].blue=RoundDoubleToQuantum(pixel.blue);q[y].opacity=OpaqueOpacity;}if ((indexes != (IndexPacket *) NULL) &&(source_indexes != (IndexPacket *) NULL)){i=Min(Max((long) (center+0.5),start),stop-1);j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+(contribution[i-start].pixel-contribution[0].pixel);indexes[y]=source_indexes[j];}}

从上面的代码可看出,在GraphicsMagick中只处理了matte通道或CMYKColorsapce,它们都是四通道的。但是看到代码中的indexes,不知道这个在GraphicsMagick中的意义及是否在ImageMagick中有相关对应。

可以在GraphicsMagick中通过搜索indexes_valid来找到一些有用的信息:

/*Indexes are valid if the image storage class is PseudoClass or thecolorspace is CMYK.
*/
cache_info->indexes_valid=((image->storage_class == PseudoClass) ||(image->colorspace == CMYKColorspace));

原来是这样,这里的PseudoClass就是pseudocolor,可以在wikiIndexed color中找到介绍,之所以叫做索引颜色,是因为为了节省内存或磁盘空间,颜色信息不是直接由图片的像素所携带,而是存放在一个单独的颜色表中或者调色板中。

从上面贴出来的ImageMagickGraphicsMagick的代码对比发现,下面两段代码类似:

// ImageMagick
if (((resize_traits & CopyPixelTrait) != 0) ||(GetPixelWriteMask(resize_image,q) <= (QuantumRange/2))){j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)stop-1.0)+0.5);k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+(contribution[j-start].pixel-contribution[0].pixel);SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],q);continue;}
// GraphicsMagick
if ((indexes != (IndexPacket *) NULL) &&(source_indexes != (IndexPacket *) NULL)){i=Min(Max((long) (center+0.5),start),stop-1);j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+(contribution[i-start].pixel-contribution[0].pixel);indexes[y]=source_indexes[j];}

依然参考InitializePixelChannelMap()的代码,结合刚刚知道的GraphicsMagick对于PseudoClassCMYKColorspaceindexes有效,在ImageMagick中则CopyPixelTrait对应IndexPixelChannel

if (image->colorspace == CMYKColorspace)SetPixelChannelAttributes(image,BlackPixelChannel,trait,n++);
if (image->alpha_trait != UndefinedPixelTrait)SetPixelChannelAttributes(image,AlphaPixelChannel,CopyPixelTrait,n++);
if (image->storage_class == PseudoClass)SetPixelChannelAttributes(image,IndexPixelChannel,CopyPixelTrait,n++);
if ((image->channels & ReadMaskChannel) != 0)SetPixelChannelAttributes(image,ReadMaskPixelChannel,CopyPixelTrait,n++);
if ((image->channels & WriteMaskChannel) != 0)SetPixelChannelAttributes(image,WriteMaskPixelChannel,CopyPixelTrait,n++);
if ((image->channels & CompositeMaskChannel) != 0)SetPixelChannelAttributes(image,CompositeMaskPixelChannel,CopyPixelTrait,n++);

不同的是ImageMagickCMYKColorspace没有CopyPixelTrait特性。

小结一下:以目前的开发状态,将ImageMagick中的CopyPixelTraitGraphicsMagick中的indexes对应起来。

又一个闪退问题

今天突然发现:

[ysouyno@arch ~]$ export MAGICK_OCL_DEVICE=true
[ysouyno@arch ~]$ gm display ~/temp/bg1a.jpg
Abort was called at 39 line in file:
/build/intel-compute-runtime/src/compute-runtime-22.11.22682/shared/source/built_ins/built_ins.cpp
gm display: abort due to signal 6 (SIGABRT) "Abort"...
Aborted (core dumped)
[ysouyno@arch ~]$ clinfo
Abort was called at 39 line in file:
/build/intel-compute-runtime/src/compute-runtime-22.11.22682/shared/source/built_ins/built_ins.cpp
Aborted (core dumped)

clinfo都运行不了了,它也同样闪退了,看来肯定不是我代码的问题了。可能就是因为intel-compute-runtime的版本更新引起的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/637939.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【每日一题】按分隔符拆分字符串

文章目录 Tag题目来源解题思路方法一&#xff1a;遍历方法二&#xff1a;getline 写在最后 Tag 【遍历】【getline】【字符串】【2024-01-20】 题目来源 2788. 按分隔符拆分字符串 解题思路 方法一&#xff1a;遍历 思路 分隔符在字符串开始和结束位置时不需要处理。 分隔…

Crow:实现点击下载功能

Crow:设置网站的index.html-CSDN博客 讲述了如何完成一个最简单的网页的路由 很多网页提供了下载功能,怎么实现呢,其实也很简单。 假设网页的目录结构如图 $ tree static static ├── img │ └── goodday.jpg └── index.html //index.html <html> <body&…

专业137总分439东南大学920专业基础综合考研经验电子信息与通信电路系统芯片

我本科是南京信息工程大学&#xff0c;今年报考东南大学信息学院&#xff0c;成功逆袭&#xff0c;专业137&#xff0c;政治69&#xff0c;英语86&#xff0c;数一147&#xff0c;总分439。以下总结了自己的复习心得和经验&#xff0c;希望对大家复习有一点帮助。啰嗦一句&…

C++ :命名空间域

目录 冲突与命名&#xff1a; 举个例子&#xff1a; 全局与局部&#xff1a; 域作用限定符&#xff1a; 命名空间域&#xff1a; 冲突与命名&#xff1a; 在C语言中&#xff0c;我们通常会使用stdlib.h 而stdlib.h 本质上是一个函数的库&#xff0c;在程序中使用的大多数…

Java学习笔记(八)——Lambda表达式

文章目录 Lambda表达式Lambda表达式的省略写法Lambda练习练习1练习2 算法题算法题1 斐波那契数列算法题2 猴子吃桃子算法题3 爬楼梯 Lambda表达式 Lambda表达式是JDK8开始的一种新语法形式。 基本作用&#xff1a;简化函数式接口的匿名内部类的写法。 注意&#xff1a; Lam…

2023年总结我所经历的技术大变革

&#x1f4e2;欢迎点赞 &#xff1a;&#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;&#x1f4e2;本文作者&#xff1a;由webmote 原创&#x1f4e2;作者格言&#xff1a;新的征程&#xff0c;我们面对的不仅…

STL---Stack和Queue

一、stack的介绍和使用 &#xff08;1&#xff09;介绍 翻译: &#xff08;1&#xff09;stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提取操作。 &#xff08;2&#xff09; stack是作为容器…

STL-deque

双端数组&#xff0c;可以对头端进行插入删除操作 deque与vector区别&#xff1a; vector对于头部的插入删除效率低&#xff0c;数据量越大&#xff0c;效率越低 deque相对而言&#xff0c;对头部的插入删除速度回比vector快 vector访问元素时的速度会比deque快,这和两者内部…

03 OSPF

参考文章 1 初步认识OSPF的大致内容(第三课)-CSDN博客 2

微服务环境搭建:docker+nacos单机

nacos需要连接mysql&#xff0c;持久化相关配置。 1. 部署好mysql后&#xff0c;新建nacos数据库然后初始化nacos脚本 -- -------------------------------------------------------- -- 主机: 192.168.150.101 -- 服务器版本: …

c#调用matlab生成的COM DLL,Matlab GUI程序封装成exe文件并在不安装Matlab的电脑上运行

最近根据需求用Matlab写了一个简单的软件&#xff0c;但需要安装到其他电脑上运行&#xff0c;倒腾了很久最终成功在其他电脑上运行&#xff0c;现将方法共享给大家。安装方法&#xff1a;①程序封装 首先用Matlab写完程序并封装好&#xff08;我用的是Matlab2018b&#xff0c;…

【Linux系统编程】环境变量详解

文章目录 1. 环境变量的基本概念2. 如何理解呢&#xff1f;&#xff08;测试PATH&#xff09;2.1 切入点1查看具体的环境变量原因剖析常见环境变量 2.2 切入点2给PATH环境变量添加新路径将我们自己的命令拷贝到PATH已有路径里面 2.3 切入点3 3. 显示所有环境变量4. 测试HOME5. …

Django ORM 中的单表查询 API(1)

在 Django 中&#xff0c;对象关系映射&#xff08;ORM&#xff09;提供了一种功能强大、表现力丰富的数据库交互方式。ORM 允许开发人员使用高级 Python 代码执行数据库查询&#xff0c;从而更轻松地处理数据库实体。 下面&#xff0c;我们将探讨 Django ORM 中单表查询 API …

JNPF低代码开发平台总体架构介绍

目录 一、JNPF介绍 二、团队能力 三、技术选型 1.后端技术栈 2.前端技术栈 3.数据库支持 四、JNPF界面示意图 五、开发环境 一、JNPF介绍 JNPF是一款企业级低代码开发平台。基于Springboot、Vue技术&#xff0c;采用微服务、前后端分离架构&#xff0c;基于可视化数据建…

【论文解读】用于代码处理的语言模型综述

目录 1.简要介绍 2.代码处理的语言模型的评估 3.通用语言模型 4.用于代码处理的特定语言模型 5.语言模型的代码特性 6.软件开发中的LLM 7.结论与挑战 ​​​​​​​1.简要介绍 在这项工作中&#xff0c;论文系统地回顾了在代码处理方面的最新进展&#xff0c;包括50个模…

Ubuntu下安装Gazebo仿真器

Ubuntu下安装Gazebo仿真器 Gazebo仿真平台通常需要配合ROS使用&#xff0c;因此需要先安装ROS。可以参考ROS安装教程 首先安装一些必要的工具 sudo apt-get update sudo apt-get install lsb-release wget gnupg修改源 sudo wget https://packages.osrfoundation.org/gazebo…

cpp_12_异常处理

1 异常理论 1.1 何为异常&#xff1f; 在实际运行环境中发生&#xff0c;却在设计、编码、测试阶段无法预料的&#xff0c;各种潜在的问题。 1.2 报告异常的2种机制 1&#xff09;通过 return 返回值报告异常信息&#xff1a; 所有局部对象都能正确地被析构、被释放 定位错…

中仕教育:公务员政审需要哪些材料?

公务员政审则是公务员招聘过程中的重要环节&#xff0c;政审需要哪些材料呢? 一、个人基本信息 1. 身份证、户口本原件及复印件; 2. 学历学位证书原件及复印件; 3. 近期免冠一寸彩色照片2张; 4. 无犯罪记录证明材料; 5. 个人自传 6. 提供本人的现实表现证明材料(应届生…

Unity 面试篇|(七)Unity渲染与Shader篇 【全面总结 | 持续更新】

目录 1.问一个Terrain&#xff0c;分别贴3张&#xff0c;4张&#xff0c;5张地表贴图&#xff0c;渲染速度有什么区别&#xff1f;为什么&#xff1f;2.什么是LightMap&#xff1f;3.MipMap是什么&#xff0c;作用&#xff1f;4.请问alpha test在何时使用&#xff1f;能达到什么…

【数据结构初阶】——顺序表

本文由睡觉待开机原创&#xff0c;转载请注明出处。 本内容在csdn网站首发 欢迎各位点赞—评论—收藏 如果存在不足之处请评论留言&#xff0c;共同进步&#xff01; 这里写目录标题 1.数据结构2.顺序表线性表顺序表的结构 3.动态顺序表的实现 1.数据结构 数据结构的概念&…