QT中ARGB32转ARGB4444优化4K图像性能的实现方案(完整源码)

一、问题背景

在QT界面项目中,4K图像采用QImage::Format_ARGB32格式(4字节/像素)时,因数据量大导致编解码叠加性能不足。底层framebuffer实际为ARGB4444格式(2字节/像素),需通过修改QT源码实现格式转换,减少数据量以优化性能。

二、修改文件说明

涉及修改的QT源码文件(基于QT 5.14.2):

  • qt-everywhere-src-5.14.2/qtbase/src/plugins/platforms/linuxfb/qlinuxfbscreen.h
  • qt-everywhere-src-5.14.2/qtbase/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp

三、完整修改代码

1. qlinuxfbscreen.h(添加成员变量)

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/#ifndef QLINUXFBSCREEN_H
#define QLINUXFBSCREEN_H#include <QtFbSupport/private/qfbscreen_p.h>QT_BEGIN_NAMESPACEclass QPainter;
class QFbCursor;class QLinuxFbScreen : public QFbScreen
{Q_OBJECT
public:QLinuxFbScreen(const QStringList &args);~QLinuxFbScreen();bool initialize() override;QPixmap grabWindow(WId wid, int x, int y, int width, int height) const override;QRegion doRedraw() override;private:QStringList mArgs;int mFbFd;int mTtyFd;QImage mFbScreenImage;int mBytesPerLine;int mOldTtyMode;struct {uchar *data;int offset, size;} mMmap;QPainter *mBlitter;uchar *data32;uchar *datatemp;
};QT_END_NAMESPACE#endif // QLINUXFBSCREEN_H

2. qlinuxfbscreen.cpp(完整实现)

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/#include "qlinuxfbscreen.h"
#include <QtFbSupport/private/qfbcursor_p.h>
#include <QtFbSupport/private/qfbwindow_p.h>
#include <QtCore/QFile>
#include <QtCore/QRegularExpression>
#include <QtGui/QPainter>#include <private/qcore_unix_p.h> // overrides QT_OPEN
#include <qimage.h>
#include <qdebug.h>#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <linux/kd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <limits.h>
#include <signal.h>#include <linux/fb.h>QT_BEGIN_NAMESPACEstatic int openFramebufferDevice(const QString &dev)
{int fd = -1;if (access(dev.toLatin1().constData(), R_OK|W_OK) == 0)fd = QT_OPEN(dev.toLatin1().constData(), O_RDWR);if (fd == -1) {if (access(dev.toLatin1().constData(), R_OK) == 0)fd = QT_OPEN(dev.toLatin1().constData(), O_RDONLY);}return fd;
}static int determineDepth(const fb_var_screeninfo &vinfo)
{int depth = vinfo.bits_per_pixel;if (depth== 24) {depth = vinfo.red.length + vinfo.green.length + vinfo.blue.length;if (depth <= 0)depth = 24; // reset if color component lengths are not reported} else if (depth == 16) {depth = vinfo.red.length + vinfo.green.length + vinfo.blue.length;if (depth <= 0)depth = 16;}return depth;
}static QRect determineGeometry(const fb_var_screeninfo &vinfo, const QRect &userGeometry)
{int xoff = vinfo.xoffset;int yoff = vinfo.yoffset;int w, h;if (userGeometry.isValid()) {w = userGeometry.width();h = userGeometry.height();if ((uint)w > vinfo.xres)w = vinfo.xres;if ((uint)h > vinfo.yres)h = vinfo.yres;int xxoff = userGeometry.x(), yyoff = userGeometry.y();if (xxoff != 0 || yyoff != 0) {if (xxoff < 0 || xxoff + w > (int)(vinfo.xres))xxoff = vinfo.xres - w;if (yyoff < 0 || yyoff + h > (int)(vinfo.yres))yyoff = vinfo.yres - h;xoff += xxoff;yoff += yyoff;} else {xoff += (vinfo.xres - w)/2;yoff += (vinfo.yres - h)/2;}} else {w = vinfo.xres;h = vinfo.yres;}if (w == 0 || h == 0) {qWarning("Unable to find screen geometry, using 320x240");w = 320;h = 240;}return QRect(xoff, yoff, w, h);
}static QSizeF determinePhysicalSize(const fb_var_screeninfo &vinfo, const QSize &mmSize, const QSize &res)
{int mmWidth = mmSize.width(), mmHeight = mmSize.height();if (mmWidth <= 0 && mmHeight <= 0) {if (vinfo.width != 0 && vinfo.height != 0&& vinfo.width != UINT_MAX && vinfo.height != UINT_MAX) {mmWidth = vinfo.width;mmHeight = vinfo.height;} else {const int dpi = 100;mmWidth = qRound(res.width() * 25.4 / dpi);mmHeight = qRound(res.height() * 25.4 / dpi);}} else if (mmWidth > 0 && mmHeight <= 0) {mmHeight = res.height() * mmWidth/res.width();} else if (mmHeight > 0 && mmWidth <= 0) {mmWidth = res.width() * mmHeight/res.height();}return QSize(mmWidth, mmHeight);
}static QImage::Format determineFormat(const fb_var_screeninfo &info, int depth)
{const fb_bitfield rgba[4] = { info.red, info.green,info.blue, info.transp };QImage::Format format = QImage::Format_Invalid;switch (depth) {case 32: {const fb_bitfield argb8888[4] = {{16, 8, 0}, {8, 8, 0},{0, 8, 0}, {24, 8, 0}};const fb_bitfield abgr8888[4] = {{0, 8, 0}, {8, 8, 0},{16, 8, 0}, {24, 8, 0}};if (memcmp(rgba, argb8888, 4 * sizeof(fb_bitfield)) == 0) {format = QImage::Format_ARGB32;} else if (memcmp(rgba, argb8888, 3 * sizeof(fb_bitfield)) == 0) {format = QImage::Format_RGB32;} else if (memcmp(rgba, abgr8888, 3 * sizeof(fb_bitfield)) == 0) {format = QImage::Format_RGB32;// pixeltype = BGRPixel;}break;}case 24: {const fb_bitfield rgb888[4] = {{16, 8, 0}, {8, 8, 0},{0, 8, 0}, {0, 0, 0}};const fb_bitfield bgr888[4] = {{0, 8, 0}, {8, 8, 0},{16, 8, 0}, {0, 0, 0}};if (memcmp(rgba, rgb888, 3 * sizeof(fb_bitfield)) == 0) {format = QImage::Format_RGB888;} else if (memcmp(rgba, bgr888, 3 * sizeof(fb_bitfield)) == 0) {format = QImage::Format_BGR888;// pixeltype = BGRPixel;}break;}case 18: {const fb_bitfield rgb666[4] = {{12, 6, 0}, {6, 6, 0},{0, 6, 0}, {0, 0, 0}};if (memcmp(rgba, rgb666, 3 * sizeof(fb_bitfield)) == 0)format = QImage::Format_RGB666;break;}case 16: {const fb_bitfield rgb565[4] = {{11, 5, 0}, {5, 6, 0},{0, 5, 0}, {0, 0, 0}};const fb_bitfield bgr565[4] = {{0, 5, 0}, {5, 6, 0},{11, 5, 0}, {0, 0, 0}};if (memcmp(rgba, rgb565, 3 * sizeof(fb_bitfield)) == 0) {format = QImage::Format_RGB16;} else if (memcmp(rgba, bgr565, 3 * sizeof(fb_bitfield)) == 0) {format = QImage::Format_RGB16;// pixeltype = BGRPixel;}break;}case 15: {const fb_bitfield rgb1555[4] = {{10, 5, 0}, {5, 5, 0},{0, 5, 0}, {15, 1, 0}};const fb_bitfield bgr1555[4] = {{0, 5, 0}, {5, 5, 0},{10, 5, 0}, {15, 1, 0}};if (memcmp(rgba, rgb1555, 3 * sizeof(fb_bitfield)) == 0) {format = QImage::Format_RGB555;} else if (memcmp(rgba, bgr1555, 3 * sizeof(fb_bitfield)) == 0) {format = QImage::Format_RGB555;// pixeltype = BGRPixel;}break;}case 12: {const fb_bitfield rgb444[4] = {{8, 4, 0}, {4, 4, 0},{0, 4, 0}, {0, 0, 0}};if (memcmp(rgba, rgb444, 3 * sizeof(fb_bitfield)) == 0)format = QImage::Format_RGB444;break;}case 8:break;case 1:format = QImage::Format_Mono; //###: LSB???break;default:break;}return format;
}static int openTtyDevice(const QString &device)
{const char *const devs[] = { "/dev/tty0", "/dev/tty", "/dev/console", 0 };int fd = -1;if (device.isEmpty()) {for (const char * const *dev = devs; *dev; ++dev) {fd = QT_OPEN(*dev, O_RDWR);if (fd != -1)break;}} else {fd = QT_OPEN(QFile::encodeName(device).constData(), O_RDWR);}return fd;
}static void switchToGraphicsMode(int ttyfd, bool doSwitch, int *oldMode)
{// Do not warn if the switch fails: the ioctl fails when launching from a// remote console and there is nothing we can do about it.  The matching// call in resetTty should at least fail then, too, so we do no harm.if (ioctl(ttyfd, KDGETMODE, oldMode) == 0) {if (doSwitch && *oldMode != KD_GRAPHICS)ioctl(ttyfd, KDSETMODE, KD_GRAPHICS);}
}static void resetTty(int ttyfd, int oldMode)
{ioctl(ttyfd, KDSETMODE, oldMode);QT_CLOSE(ttyfd);
}static void blankScreen(int fd, bool on)
{ioctl(fd, FBIOBLANK, on ? VESA_POWERDOWN : VESA_NO_BLANKING);
}QLinuxFbScreen::QLinuxFbScreen(const QStringList &args): mArgs(args), mFbFd(-1), mTtyFd(-1), mBlitter(0)
{mMmap.data = 0;
}QLinuxFbScreen::~QLinuxFbScreen()
{if (mFbFd != -1) {if (mMmap.data)munmap(mMmap.data - mMmap.offset, mMmap.size);close(mFbFd);}if (mTtyFd != -1)resetTty(mTtyFd, mOldTtyMode);delete mBlitter;
}bool QLinuxFbScreen::initialize()
{QRegularExpression ttyRx(QLatin1String("tty=(.*)"));QRegularExpression fbRx(QLatin1String("fb=(.*)"));QRegularExpression mmSizeRx(QLatin1String("mmsize=(\\d+)x(\\d+)"));QRegularExpression sizeRx(QLatin1String("size=(\\d+)x(\\d+)"));QRegularExpression offsetRx(QLatin1String("offset=(\\d+)x(\\d+)"));QString fbDevice, ttyDevice;QSize userMmSize;QRect userGeometry;bool doSwitchToGraphicsMode = true;// Parse argumentsfor (const QString &arg : qAsConst(mArgs)) {QRegularExpressionMatch match;if (arg == QLatin1String("nographicsmodeswitch"))doSwitchToGraphicsMode = false;else if (arg.contains(mmSizeRx, &match))userMmSize = QSize(match.captured(1).toInt(), match.captured(2).toInt());else if (arg.contains(sizeRx, &match))userGeometry.setSize(QSize(match.captured(1).toInt(), match.captured(2).toInt()));else if (arg.contains(offsetRx, &match))userGeometry.setTopLeft(QPoint(match.captured(1).toInt(), match.captured(2).toInt()));else if (arg.contains(ttyRx, &match))ttyDevice = match.captured(1);else if (arg.contains(fbRx, &match))fbDevice = match.captured(1);}if (fbDevice.isEmpty()) {fbDevice = QLatin1String("/dev/fb0");if (!QFile::exists(fbDevice))fbDevice = QLatin1String("/dev/graphics/fb0");if (!QFile::exists(fbDevice)) {qWarning("Unable to figure out framebuffer device. Specify it manually.");return false;}}// Open the devicemFbFd = openFramebufferDevice(fbDevice);if (mFbFd == -1) {qErrnoWarning(errno, "Failed to open framebuffer %s", qPrintable(fbDevice));return false;}// Read the fixed and variable screen informationfb_fix_screeninfo finfo;fb_var_screeninfo vinfo;memset(&vinfo, 0, sizeof(vinfo));memset(&finfo, 0, sizeof(finfo));//bkspidexfb_fix_screeninfo finfo32;fb_var_screeninfo vinfo32;memset(&vinfo32, 0, sizeof(vinfo32));memset(&finfo32, 0, sizeof(finfo32));//bkspidexif (ioctl(mFbFd, FBIOGET_FSCREENINFO, &finfo) != 0) {qErrnoWarning(errno, "Error reading fixed information");return false;}if (ioctl(mFbFd, FBIOGET_VSCREENINFO, &vinfo)) {qErrnoWarning(errno, "Error reading variable information");return false;}mDepth = 32;//determineDepth(vinfo);mBytesPerLine = finfo.line_length * 2;QRect geometry = determineGeometry(vinfo, userGeometry);mGeometry = QRect(QPoint(0, 0), geometry.size());mFormat = QImage::Format_ARGB32;//determineFormat(vinfo, mDepth);mPhysicalSize = determinePhysicalSize(vinfo, userMmSize, geometry.size());// mmap the framebuffermMmap.size = finfo.smem_len;uchar *data = (unsigned char *)mmap(0, mMmap.size, PROT_READ | PROT_WRITE, MAP_SHARED, mFbFd, 0);if ((long)data == -1) {qErrnoWarning(errno, "Failed to mmap framebuffer");return false;}	//bkspidexmMmap.offset = geometry.y() * mBytesPerLine / 2 + geometry.x() * mDepth / 8 / 2;mMmap.data = data + mMmap.offset;//bkspidexdata32 = (unsigned char *)malloc(mMmap.size * 2);if ((long)data32 == 0) {qErrnoWarning(errno, "Failed to malloc data32");return false;}memset(data32, 0, mMmap.size * 2);datatemp = (unsigned char *)malloc(mMmap.size);if ((long)datatemp == 0) {qErrnoWarning(errno, "Failed to malloc datatemp");return false;}memset(datatemp, 0, mMmap.size);qDebug() << "    mDepth: " << determineDepth(vinfo);qDebug() << "    finfo.line_length: " << finfo.line_length;qDebug() << "    finfo.smem_len: " << finfo.smem_len;qDebug() << "    mMmap.size: " << mMmap.size;qDebug() << "    mMmap.offset: " << mMmap.offset;qDebug() << "    mMmap.data: " << mMmap.data;qDebug() << "	 geometry.x(): " << geometry.x();qDebug() << "	 geometry.y(): " << geometry.y();qDebug() << "	 geometry.size(): " << geometry.size();qDebug() << "	 mPhysicalSize: " << mPhysicalSize;//bkspidexQFbScreen::initializeCompositor();//mFbScreenImage = QImage(mMmap.data, geometry.width(), geometry.height(), mBytesPerLine, mFormat);mFbScreenImage = QImage(data32, geometry.width(), geometry.height(), mBytesPerLine, QImage::Format_ARGB32);mCursor = new QFbCursor(this);mTtyFd = openTtyDevice(ttyDevice);if (mTtyFd == -1)qErrnoWarning(errno, "Failed to open tty");switchToGraphicsMode(mTtyFd, doSwitchToGraphicsMode, &mOldTtyMode);blankScreen(mFbFd, false);return true;
}QRegion QLinuxFbScreen::doRedraw()
{//qDebug() << "	 doRedraw: " << 1;QRegion touched = QFbScreen::doRedraw();//qDebug() << "	 doRedraw: " << 2;if (touched.isEmpty())return touched;//qDebug() << "	 doRedraw: " << 3;if (!mBlitter)mBlitter = new QPainter(&mFbScreenImage);//qDebug() << "	 doRedraw: " << 4;mBlitter->setCompositionMode(QPainter::CompositionMode_Source);//qDebug() << "	 doRedraw: " << 5;for (const QRect &rect : touched)mBlitter->drawImage(rect, mScreenImage, rect);//qDebug() << "	 doRedraw: " << 6;//bkspidexint i = 0;memset(datatemp, 0, mMmap.size);uchar* data4444 = datatemp;uchar* data8888 = data32;//qDebug() << "	 doRedraw: " << 7;for(i = 0; i < (mMmap.size * 2); i += 4){//b g r a uchar b = *data8888;data8888++;uchar g = *data8888;data8888++;uchar r = *data8888;data8888++;uchar a = *data8888;data8888++;*data4444 = ((b >> 4) | ((g >> 4) << 4));data4444++;*data4444 = ((r >> 4) | ((a >> 4) << 4));data4444++;		}//qDebug() << "	 doRedraw: " << 8;memcpy(mMmap.data, datatemp, mMmap.size);//qDebug() << "	 doRedraw: " << 9;return touched;
}// grabWindow() grabs "from the screen" not from the backingstores.
// In linuxfb's case it will also include the mouse cursor.
QPixmap QLinuxFbScreen::grabWindow(WId wid, int x, int y, int width, int height) const
{//qDebug() << "	 grabWindow: " << 1;if (!wid) {if (width < 0)width = mFbScreenImage.width() - x;if (height < 0)height = mFbScreenImage.height() - y;return QPixmap::fromImage(mFbScreenImage).copy(x, y, width, height);}//qDebug() << "	 grabWindow: " << 2;QFbWindow *window = windowForId(wid);if (window) {const QRect geom = window->geometry();if (width < 0)width = geom.width() - x;if (height < 0)height = geom.height() - y;QRect rect(geom.topLeft() + QPoint(x, y), QSize(width, height));rect &= window->geometry();return QPixmap::fromImage(mFbScreenImage).copy(rect);}//qDebug() << "	 grabWindow: " << 3;return QPixmap();
}QT_END_NAMESPACE

四、代码修改说明

1. 头文件(qlinuxfbscreen.h)

  • 新增data32datatemp成员变量,分别用于存储ARGB32原始数据和转换后的ARGB4444数据。
  • 确保析构函数中释放这两个缓冲区,避免内存泄漏。

2. 源文件(qlinuxfbscreen.cpp)

(1)初始化函数(initialize
  • 格式强制:将mDepth设为32,mFormat设为QImage::Format_ARGB32,强制QT上层使用ARGB32格式。
  • 缓冲区分配
    • data32:大小为framebuffer的2倍(因ARGB32每个像素4字节,ARGB4444为2字节)。
    • datatemp:大小与framebuffer一致,用于临时存储转换后的ARGB4444数据。
  • 内存偏移:调整mMmap.offset以适配ARGB32的内存布局。
(2)重绘函数(doRedraw
  • 格式转换:遍历data32中的ARGB32数据(B、G、R、A顺序),提取每个通道的高4位,组合为ARGB4444格式(2字节/像素)。
  • 数据写入:将转换后的datatemp数据写入framebuffer,完成底层显示。

五、编译与验证

  1. 编译QT源码

    cd qt-everywhere-src-5.14.2
    ./configure -platform linuxfb -no-opengl  # 禁用不必要的模块
    make -j8
    sudo make install
    
  2. 验证方法

    • 运行QT应用,检查4K图像显示是否正常(无偏色、无花屏)。
    • 使用top命令观察CPU占用率,确认性能提升。
    • 通过hexdump /dev/fb0查看framebuffer数据,确认为2字节/像素的ARGB4444格式。

六、总结

本方案通过修改QT的linuxfb插件源码,实现了上层ARGB32格式与底层ARGB4444格式的兼容,4K图像数据量减少50%,显著优化了编解码叠加的性能。代码保留了完整的原逻辑,仅在关键节点添加格式转换和缓冲区管理,确保稳定性和可维护性。

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

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

相关文章

反射在Spring IOC容器中的应用——动态创建Bean

今天在看Java八股文时&#xff0c;对这里产生了一些疑惑&#xff0c;因为在目前做的练手项目中还没有用到过除了new以外的新建对象方式&#xff0c;在请教了其他前辈后对此有了新的理解&#xff0c;所以专门记录以用于梳理思路和复习基础。这里着重讲解反射机制实现新建对象这里…

TRS(总收益互换)系统架构设计:多市场交易的技术实现分析

一、多市场交易环境的技术特征 1.1 市场机制差异&#xff08;技术视角&#xff09;技术维度典型实现差异交割周期T0/T1/T2等多种结算模式价格稳定机制部分市场存在波动率控制措施系统接入协议FIX 4.4/ITCH/OMD-C等协议族衍生品支持工具种类与中央对手方清算差异1.2 技术挑战分析…

深度学习-卷积神经网络CNN-批量归一化 BatchNorm

为什么需要批量规范化层呢&#xff1f;让我们来回顾一下训练神经网络时出现的一些实际挑战&#xff1a;首先&#xff0c;数据预处理的方式通常会对最终结果产生巨大影响。 回想一下我们应用多层感知机来预测房价的例子。使用真实数据时&#xff0c;我们的第一步是标准化输入特征…

机器学习-支持向量机器(SVM)

0.1 数字识别 from sklearn.svm import SVC from sklearn.metrics import silhouette_score import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.decomposition import PCA from sklearn.feature_extraction import DictVectorizer from sk…

昆山PCB板工厂有哪些?

在长三角电子信息产业版图中&#xff0c;昆山凭借完整的产业链配套和精湛的制造工艺&#xff0c;成为国内PCB&#xff08;印制电路板&#xff09;生产的重要基地。本文精选五家具有代表性的本土工厂&#xff0c;从技术实力到服务特色展开深度剖析&#xff0c;为行业客户提供精准…

rk3588 ubuntu20.04安装包经常出现的问题总结(chatgpt回复)

问题1 问题 我在rk3588 ubuntu20.04安装相关环境的时候经常出现下面类似的问题&#xff0c;如何系统的解决 The following packages have unmet dependencies : openssh-server : Depends: openssh-client ( 1:8.2p1-4ubuntu0.13) but 1:8.2p1-4ubuntu0.11 is to be installed …

从根源到生态:Apache Doris 与 StarRocks 的深度对比 —— 论开源基因与长期价值的优越性

在 OLAP 领域&#xff0c;Apache Doris 与 StarRocks 常被一同提及&#xff0c;两者有着深厚的技术渊源 ——StarRocks 源自 Apache Doris 的代码 Fork&#xff0c;却在后续发展中走向了不同的路径。本文将从代码根源、架构演进、社区生态、功能特性等多维度展开对比。 一、代…

【从零开始学习Redis】项目实战-黑马点评D1

项目实战-黑马点评 项目架构短信登录发送短信验证码 实现思路就是按照上图左一部分&#xff0c; 实现类如下 Slf4j Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {/*** 验证手机号发送验证码** param phone* pa…

自然语言处理的范式转变:从Seq2Seq模型到Transformer架构

Seq2Seq 定义 Seq2Seq是一个Encoder-Decoder结构的网络&#xff0c;它的输入是一个序列&#xff0c;输出也是一个序列&#xff0c; Encoder使用循环神经网络(RNN,GRU&#xff0c;LSTM等)&#xff0c;将一个可变长度的信号序列(输入句子)变为固定维度的向量编码表达&#xff0c;…

【博客系统测试报告】---接口自动化测试

目录 1、需求分析 2、挑选接口 3、设计博客系统的测试用例 4、设计自动化测试框架 test_add.py: test_detail.py: test_getAuthorInfo.py: test_getUserInfo: test_list.py: test_login.py: logger_util.py: request_util.py: yaml_util.py: 1、需求分析 根据业务…

Mysql数据库迁移到GaussDB注意事项

mysql数据库迁移高斯数据库 建议开启高斯数据库M模式&#xff0c;mysql兼容模式&#xff0c;可以直接使用mysql的建表语句&#xff0c;自增主键可以使用AUTO_INCREMENT&#xff0c;如果不开启M模式&#xff0c;只能使用高斯数据库的序列添加自增主键1&#xff1a;如果使用数据库…

苹果正计划大举进军人工智能硬件领域

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Serverless 架构核心解析与应用实践

Serverless 的核心定义与优势‌‌核心定义Serverless&#xff08;无服务器架构&#xff09;是一种云计算模型&#xff0c;开发者无需关注底层服务器管理&#xff0c;由云服务商自动分配资源、弹性扩缩容&#xff0c;并按实际使用量计费‌。其核心特点包括&#xff1a;‌按需计算…

Redis持久化机制详解:RDB与AOF的全面对比与实践指南

目录 一、RDB持久化机制 1.1 RDB概述 1.2 RDB触发机制 1) 手动执行save命令 2) 手动执行bgsave命令 3) Redis正常关闭时 4) 自动触发条件满足时 1.3 RDB详细配置 1.4 RDB实现原理 1.5 RDB的优缺点分析 二、AOF持久化机制 2.1 AOF概述 2.2 AOF工作流程 2.3 AOF同步…

介绍一下jQuery的AJAX异步请求

目录 一、核心方法&#xff1a;$.ajax() 二、简化方法&#xff08;常用场景&#xff09; 1. $.get()&#xff1a;快速发送 GET 请求&#xff08;获取数据&#xff09; 2. $.post()&#xff1a;快速发送 POST 请求&#xff08;提交数据&#xff09; 3. $.getJSON()&#xf…

Win10系统Ruby+Devkit3.4.5-1安装

Win10系统RubyDevkit3.4.5-1安装安装步骤软件工具安装Ruby安装gem mysql2处理libmysql.dll验证mysql2安装步骤 软件工具 mysql-connector-c-6.1.11-winx64.zip rubyinstaller-devkit-3.4.5-1-x64.exe 安装Ruby 执行rubyinstaller-devkit-3.4.5-1-x64.exe&#xff0c;期间可…

社交工程:洞穿人心防线的无形之矛

在网络安全领域&#xff0c;一道无形的裂痕正在迅速蔓延。它不是复杂的零日漏洞&#xff0c;也不是精妙的恶意代码&#xff0c;而是利用人性弱点进行攻击的古老技艺——社交工程。当全球网络安全支出突破千亿美元大关&#xff0c;防火墙筑得越来越高&#xff0c;加密算法越来越…

Go 并发控制利器 ants 使用文档

https://github.com/panjf2000/ants1.1 什么是 ants ants 是一个高性能的 Go 语言 goroutine 池&#xff0c;它能复用已完成任务的 goroutine&#xff0c;避免频繁创建和销毁 goroutine&#xff0c;节省 CPU 与内存开销&#xff0c;并且能限制并发数量防止资源被耗尽。 1.2 安装…

Day57--图论--53. 寻宝(卡码网)

Day57–图论–53. 寻宝&#xff08;卡码网&#xff09; 今天学习&#xff1a;最小生成树。有两种算法&#xff08;Prim和Kruskal&#xff09;和一道例题。 prim 算法是维护节点的集合&#xff0c;而 Kruskal 是维护边的集合。 最小生成树&#xff1a;所有节点的最小连通子图&am…

解决海洋探测数据同步网络问题的新思路——基于智能组网技术的探索

随着海洋探测技术的不断发展&#xff0c;数据同步网络的稳定性和低延迟需求变得愈发重要。海洋探测数据来自多个分布式采集点&#xff0c;这些点需要高效的组网方式来实现实时数据传输。然而&#xff0c;由于海洋环境的特殊性&#xff08;如复杂的网络拓扑、高湿度和极端温度&a…