当前位置: 首页 > news >正文

Qt编程Action:Qt的自动反色方案

Qt编程Action:Qt的自动反色方案

笔者最近正在做的事情是制作一款简单的ToDo软件,在编写和设计控件的时候,笔者就注意到一个很经典的问题,我们如何进行科学的反色,从而给编程软件使用者,也就是我们的用户以最好的阅读体验呢?这一篇文章,笔者尝试给出我的一些方案。

我是遇到了什么场景需要思考这个问题?

笔者正在制作Microsoft Edge首页的带有箴言的图片Widget的时候遇到的这个问题。举个例子,笔者打算放置每日自动推送的NASA APOD图像,显示在CCImageWidget上,这个时候,箴言为了方便看清,最好加大图像和文字的颜色对比度。所以摆在我们的面前的依次有如下这些问题:

  1. 如何计算一个区域下的最好反色色调?
  2. 上述问题是静态需求,如果我们的CCImageWidget上出现图片变更,如何进行下一步的计算是高内聚低耦合的?

笔者构思了一下,发现这些都是UI底层问题:

  • FloatingLabelHelper 常是独立顶层窗口,不能直接用 parentWidget()->palette() 得到真实像素颜色;
  • 图片可能缩放、裁剪或保持纵横比,需把 widget 坐标映射回 pixmap/image 坐标;
  • 图片可能有透明通道(alpha);
  • 要在性能与准确度之间权衡。

快速勾勒一下总体方案(流程)

约束好我们的相关接口:首先,背景Widget需要具备裁剪出来自己ViewPort的Pixmap的能力,笔者这里不再赘述如何获取任意QWidget的Pixmap了,假定我们存在一个接口可以获取给定的Widget的Pixmap

QPixmap	getWidgetPixmap() const;

和一个设置Pixmap后触发的signal:onPixmapChanged,或者是采用非Qt风格的回调机制,来通知触发自动重新计算发色的办法。这样,我们就可以在图片或浮动位置更新的时候采取以下的行动:

  1. 计算浮动 Label 的中心(或其他采样点)的全局坐标
  2. 把这个点映射到 cover_widget 的本地坐标(Qt的mapFromGlobal就是做这个的,将一个全局坐标变化怒到Widget本地坐标)
  3. cover_widget 的本地坐标映射为 pixmap/image 的像素坐标(这里我们是考虑缩放/居中/aspect-fit);
  4. 这一步开始我们只需要采样即可,为了迅速,采样我们简单做平均或中位数,得到代表颜色
  5. 经典的反色方案包含简单亮度法或 WCAG 对比率法,笔者不计划把事情搞得非常的复杂。所以采用简单亮度法
  6. 将得到的颜色拿来设置Label的字体颜色即可

关键代码

我们直接讨论最核心的关键代码,把思路捋清即可。

把 QLabel 内坐标映射到 pixmap 像素坐标(处理 aspect-fit 居中缩放)
static QPoint widgetPointToPixmapPoint(QLabel* imageLabel, const QPoint& widgetPt) {if (!imageLabel->pixmap() || imageLabel->pixmap()->isNull()) return QPoint(-1, -1);const QPixmap* pm = imageLabel->pixmap();const QSize pixmapSize = pm->size();const QSize widgetSize = imageLabel->size();const double pw = pixmapSize.width();const double ph = pixmapSize.height();const double ww = widgetSize.width();const double wh = widgetSize.height();const double scale = qMin(ww / pw, wh / ph); // Qt代码笔者喜欢入乡随俗,就是用qMin吧!const QSizeF scaledPixmapSize(pw * scale, ph * scale);const double left = (ww - scaledPixmapSize.width()) / 2.0;const double top  = (wh - scaledPixmapSize.height()) / 2.0;const double xInPixmap = (widgetPt.x() - left) / scale;const double yInPixmap = (widgetPt.y() - top)  / scale;const int ix = qBound(0, int(std::round(xInPixmap)), pixmapSize.width() - 1);const int iy = qBound(0, int(std::round(yInPixmap)), pixmapSize.height() - 1);return QPoint(ix, iy);
}

如果我们的 QLabel::scaledContentstrue(像素拉伸),则 scale 计算应改为 scaleX = ww/pw, scaleY = wh/ph 并分别映射。


在 image 上采样附近平均颜色
static QColor averageColorAround(const QImage& img, const QPoint& center, int radius = 12, int step = 3) {if (center.x() < 0 || center.y() < 0) return QColor();int x0 = qMax(0, center.x() - radius);int x1 = qMin(img.width() - 1, center.x() + radius);int y0 = qMax(0, center.y() - radius);int y1 = qMin(img.height() - 1, center.y() + radius);long long r=0,g=0,b=0,count=0;for (int y=y0; y<=y1; y+=step) {for (int x=x0; x<=x1; x+=step) {QColor c = img.pixelColor(x,y);if (c.alpha() == 0) continue; // 忽略完全透明像素r += c.red(); g += c.green(); b += c.blue();++count;}}if (count == 0) return QColor(); // 全透明或无像素return QColor(int(r/count), int(g/count), int(b/count));
}

radius 控制观察范围;step 控制采样密度(性能权衡)。


简单亮度法选择黑/白
static inline QColor bestTextColorForBackground(const QColor& bg) {double L = 0.2126 * bg.redF() + 0.7152 * bg.greenF() + 0.0722 * bg.blueF();return (L < 0.5) ? QColor(Qt::white) : QColor(Qt::black);
}

这里我们很容易看到,简单亮度法可以快速的筛选对比色,当然这个就很单调了,我们没办法更好的裁决二维的最佳反色。

性能考虑

我们很容易想到性能问题。这里容易触发性能顾虑的无非这一个点:图像太大了。对于大图像的计算,我们完全可以采用异步的解决方案。

  • 使用 QtConcurrent::run()std::asyncaverageColorAround 的计算;
  • 计算完后用 QMetaObject::invokeMethod(labelHelper, "setTextColor", Qt::QueuedConnection, Q_ARG(QColor, result)) 回主线程更新 UI。

或者是自动选择更好的step算法:比如说step=3~5 对通常图片已足够。

当然,还有一种可能我们的确要思考的,就是高频设置图像的场景。对于任何一个只看最终结果的场景,我们完全可以采用延迟分析,比如说,当发现用户正在高频设置图像的时候(Example:大规模触发setPixmap,我们可以手动延迟计算)

Ideas

笔者注:所有的Ideas就是我想到的但是暂时不会做,留给读者朋友思考的:

  • 使用中位数而非平均值去抗噪点(std::nth_element);
  • 返回三级颜色(白 / 黑 / 柔和灰)以处理中等亮度背景;
  • 对结果文字颜色做平滑过渡(QVariantAnimation)避免闪烁;
  • 在高分辨率图片上按不同缩放层级自适应 radius
  • 若想做更智能的排版,基于局部对比计算是否需要加投影、描边或半透明底栏(轮廓/阴影能显著提升可读性)。
http://www.dtcms.com/a/589529.html

相关文章:

  • 福田网站建设费用明细国际消息新闻
  • 网站备案失败wordpress 模版标签
  • Linux开发工具(4)
  • 怎么办一个网站wordpress个人中心无法登录
  • U-Net:用于生物医学图像分割的卷积网络,经典中的经典,后续所有创新改进的起点
  • 基于n8n构建最新资讯自动获取:从RSS抓取到智能分析实践
  • 深圳注明企业网站设计山西网站建设方案
  • 【强化学习】可证明安全强化学习(Provably Safe RL)算法详细介绍
  • 【性能优化】前端高性能优化策略
  • 【LeetCode】106. 从中序与后序遍历序列构造二叉树
  • 手机怎么建自己的网站管理网络的网站
  • SpringCloud02-服务拆分远程调用
  • JavaScript判空最佳实践
  • 做的网站缩小内容就全乱了珠海网站建设官网
  • 突破AI助手成本壁垒:知识图谱思维架构让小模型实现大性能
  • 做网站使用明星照片可以吗保护动物网站建设策划书
  • 【Linux学习】新建系统(Ubuntu)后的一些开局必要操作配置
  • 十大免费网站推广网络规划工程师
  • Java 大视界 -- Java 大数据机器学习模型在电商用户流失预测与留存策略制定中的应用
  • 山东网站开发苏州建设工程招标在哪个网站
  • 网站费用单企业速成网站
  • 电子商务网站建设的意义0基础怎么做网站模版
  • 深入理解C语言共用体/联合体(union):大小计算与大小端判断实战
  • ITIL 4 测评题库试卷及详细分析
  • 数据库基础-01Mysql库和表的操作
  • linux服务ping不通百度的解决过程【ping www.baidu.comping: unknown host】
  • 广州网站建设模板设计素材库
  • 深入浅出蓝桥杯:算法基础概念与实战应用(一)基础算法(上)
  • C++ vector 全面解析:从接口使用到底层机制
  • 亚马逊欧洲站vat怎么申请湖南企业做网站