如何旋转YUV图片数据且使用Qt显示

news2024/11/28 16:45:53

在这里插入图片描述

前言

提一下这篇文章的需求:将USB相机获取到的YUV数据进行旋转,然后转为QImage进行显示。原本程序中是有旋转的代码,但不知道为什么,旋转出来的图片会花屏。关于花屏的问题,后面会稍微阐述一下。所以,经过查询各种资料,终于将这个问题给解决了,所以,自己也稍微总结一下解决问题的过程,以及最终的结果。

正文

一、环境

基本上这个的旋转跟你机器的环境也没多大关系,主要还是跟你YUV数据的格式有关系。

我所使用的环境是ARM+Qt+Win10

二、解决过程

1、解决思路

首先,我们解决问题,一定要明确自己解决问题的思路,才能不浪费时间。

起初对图片旋转的问题,没有一个清晰的解题思路,只知道不断的尝试各种旋转算法,导致浪费了部分时间,网上单独查询到的资料基本都是NV21或是NV12的图片旋转算法,所以,大部分都不可用。
最终梳理了一下,采取了以下流程解决了图片旋转的问题:

1、先将采集出的YUV数据保存成文件->

2、使用YUV Viwer进行显示,确认属于哪种格式的YUV数据。->

3、在网上搜索对应格式的YUV数据的旋转方式->

4、将YUV经过上一步的YUV数据旋转算法旋转->

5、保存旋转后的YUV数据、确保旋转后的YUV数据时没问题->

6、然后将YUV转QImage ,这里需注意,若旋转90度或270度,生成QImage时要注意宽高对调,否则会产生花屏。

我也大概按照这样的思路,来阐述我解决问题的思路。

2、如何保存采集到的YUV数据

以下有两种保存方式:

第一:把文件名称、YUV数据、YUV数据的宽高都传进去。

void CUsbVideo::_Yuv420pSaveFile(QString _sFileName, unsigned char *_inFile, int _iWidth, int _iHeight)
{
#if 1
    FILE *pFileOut;
    //    _sFileName = QString("/ics/File/test.yuv");
    std::string fileName = _sFileName.toStdString();

    if (NULL == (pFileOut = fopen(fileName.c_str(), "wb")))
    {
        LOG_INFO << "File output can not open";
        fclose(pFileOut);
        return;
    }
    int iBufLen = 0;
    iBufLen = _iWidth * _iHeight * 3 /2;//注意,这里YUV数据的大小一般是3/2  具体原因可以网上查下。
    fwrite(_inFile, iBufLen*sizeof(unsigned char), 1, pFileOut);
    fclose(pFileOut);
#endif
}

关于上面的fopen 也有一些知识:

以上的w和r分别代表写和读,但是这个是只写和只读,那要同时进行读写怎么办呢?没办法,那再来一个参数吧,于是+就出现了,于是r+、w+、a+就出现了,对应二进制文件就是rb+、rw+、ab+了。

1、采用r、rb、r+、rb+方式打开文件的时候,文件必须已经存在,否则会出错

2、采用w、wb、w+、wb+方式打开文件的时候,如果文件不存在,则新建该文件;如果文件已经存在,则删除该文件再重新建立

3、采用a、ab、a+、ab+方式打开文件,文件必须已经存在,否则出错。

3、使用YUV Viwer进行显示

将保存下的数据使用YUV Viewer进行显示。

YUView 2.13 这个版本的也不错

我是用上面这个

还有下面这个:

YUV-Viewer工具的下载

这两个工具好像不太一样,可以都下来看一下。

还有一个软件叫7yuv

使用软件去显示保存下来的Yuv文件,确定对应的类型。

4、YUV420 I420的旋转方式

我这边主要用到的是I420的yuv数据旋转方式,网上很多查询到的是NV21或是NV12的数据类型的旋转算法。都不太适用,都会造成花屏。

底下,直接贴算法了,包含了四种,90度,180度,270度,镜像:

//输入:_pYUV   输出:_pYUV180 
void CUsbVideo::_Yuv420I420Rotate180(unsigned char* _pYUV, unsigned char* _pYUV180)
{
    int width = m_iWidth;
    int height = m_iHeight;

    uint8_t* src_y = _pYUV;
    uint8_t* src_u = _pYUV + width * height;
    uint8_t* src_v = _pYUV + width * height + width * height / 4;
    uint8_t* dst_y = _pYUV180;
    uint8_t* dst_u = _pYUV180 + width * height;
    uint8_t* dst_v = _pYUV180 + width * height + width * height / 4;
    int index = 0;

    for (int i = width * height-1; i >= 0 ; --i)
    {
        dst_y[index++] = src_y[i];
    }

    int uv_index = 0;

    for (int i = width / 2 * height / 2; i >= 0; i--)
    {
        dst_u[uv_index] = src_u[i];
        dst_v[uv_index] = src_v[i];
        uv_index++;
    }
}

void CUsbVideo::_Yuv420I420Rotate270(unsigned char* _pYUV, unsigned char* _pYUV270)
{
    int width = m_iWidth;
    int height = m_iHeight;

    uint8_t* src_y = _pYUV;
    uint8_t* src_u = _pYUV + width * height;
    uint8_t* src_v = _pYUV + width * height + width * height / 4;
    uint8_t* dst_y = _pYUV270;
    uint8_t* dst_u = _pYUV270 + width * height;
    uint8_t* dst_v = _pYUV270 + width * height + width * height / 4;

    int index = 0;
    for (int i = width-1; i >= 0; --i)
    {
        for (int j = 0; j < height; ++j) {
            dst_y[index++] = src_y[j * width+i];
        }
    }

    int uv_index = 0;
    for (int i = width / 2-1; i >= 0; i--) {
        for (int j =  0; j< height / 2; j++)
        {
            dst_u[uv_index] = src_u[width / 2 * j+i];
            dst_v[uv_index] = src_v[width / 2 * j+ i];
            uv_index++;
        }
    }
}

void CUsbVideo::_Yuv420I420Rotate90(unsigned char* _pYUV, unsigned char* _pYUV90)
{
    int width = m_iWidth;
    int height = m_iHeight;

    uint8_t* src_y = _pYUV;
    uint8_t* src_u = _pYUV+ width* height;
    uint8_t* src_v = _pYUV + width * height + width * height/4;
    uint8_t* dst_y = _pYUV90 ;
    uint8_t* dst_u = _pYUV90 + width * height;
    uint8_t* dst_v = _pYUV90 + width * height + width * height / 4;

    int index = 0;
    for (int i = 0; i < width; ++i) {
        for (int j = height - 1; j >= 0; --j) {
            dst_y[index++] = src_y[j * width + i];
        }
    }

    int uv_index = 0;
    for (int i = 0; i < width / 2; i++) {
        for (int j = height / 2 - 1; j >= 0; j--)
        {
            dst_u[uv_index] = src_u[width / 2 * j + i];
            dst_v[uv_index] = src_v[width / 2 * j + i];
            uv_index++;
        }
    }
}

void CUsbVideo::_Yuv420I420RotateMirror(unsigned char* _pYUV, unsigned char* _pYUVMirror)
{
    int width = m_iWidth;
    int height = m_iHeight;

    uint8_t* src_y = _pYUV;
    uint8_t* src_u = _pYUV + width * height;
    uint8_t* src_v = _pYUV + width * height + width * height / 4;
    uint8_t* dst_y = _pYUVMirror;
    uint8_t* dst_u = _pYUVMirror + width * height;
    uint8_t* dst_v = _pYUVMirror + width * height + width * height / 4;

    for (int j = 0; j < height; ++j) {
        for (int i = 0; i < width; i++)
        {
            dst_y[j * width + width-1-i] = src_y[j * width + i];
        }
    }

    for (int j = 0; j < height / 2; ++j) {
        for (int i = 0; i < width / 2; i++)
        {
            dst_u[j * width / 2 + width / 2-1 - i] = src_u[j * width / 2 + i];
            dst_v[j * width / 2 + width / 2-1 - i] = src_v[j * width / 2 + i];
        }
    }
}

调用方式:

char* m_pData; ///<图像数据 YUV数据
m_pConverData = new unsigned char[m_iWidth * m_iHeight * 3/2];
_Yuv420I420Rotate90((unsigned char*)m_pData, (unsigned char*)m_pConverData);

5、将旋转后的YUV数据转为QImage进行显示

void CQueryVideoDialog::SLOT_NewImage(const QByteArray &_oYuv420, int _iWidth, int _iHeight)
{
    QMutexLocker oLocker(&m_mutex);
    //注意这里的宽高一定要进行对调,也就是创建QImage的宽高一定要进行对调,否则,就会产生花屏
    m_iWidth = _iHeight;
    m_iHeight = _iWidth;

    if (m_pRGBData == nullptr)
    {
        m_pRGBData = new uchar[_iWidth * _iHeight * 3];
        m_oImage = std::move(QImage(m_pRGBData, m_iWidth, m_iHeight, QImage::Format_RGB888));
    }

    m_iIndex++;

    CPixelFormatConverter::YUV420ToRGB24((uchar*)_oYuv420.data(), m_iWidth, m_iHeight, &m_pRGBData);

	m_pwgtCamera->SLOT_NewFrame(m_oImage);

    if (!m_oWatcher.isFinished())
    {
        return;
    }

    QImage oImg = m_oImage.copy();

    if (oImg.isNull())
    {
        LOG_INFO << "--> CQueryVideoDialog::SLOT_NewImage oImg is Null";
        return;
    }
    auto fn = [this, oImg]() {
        QVector<QRect> vtFaceRectPos;
        auto oRet = gKldFaceDevice::instance()->DetectFace(oImg, vtFaceRectPos);
        if (oRet.first && false == m_bStop)
        {
            QMetaObject::invokeMethod(m_pTimer, "start");
            m_bStop = true;
        }
        QMetaObject::invokeMethod(this, "_ShowFaceRect",Q_ARG(QVector<QRect>, vtFaceRectPos));
    };
    m_oWatcher.setFuture(QtConcurrent::run(fn));
}

int CPixelFormatConverter::YUV420ToRGB24(const unsigned char* data, int width, int height,unsigned char** pOutData)
{
    int iRet = 0;

    int dstStride[4];
    av_image_fill_linesizes(dstStride, AV_PIX_FMT_RGB24, width);

    AVFrame* pFrameYuv = av_frame_alloc();
    av_image_fill_linesizes(pFrameYuv->linesize, AV_PIX_FMT_YUV420P, width);
    av_image_fill_arrays(pFrameYuv->data, pFrameYuv->linesize, data, AV_PIX_FMT_YUV420P, width, height, 1);

    SwsContext * pSwsContext = nullptr;
    pSwsContext = sws_getCachedContext(pSwsContext, width, height, AV_PIX_FMT_YUV420P, width, height, AV_PIX_FMT_RGB24, SWS_BILINEAR, NULL, NULL, NULL);

    iRet = sws_scale(pSwsContext, pFrameYuv->data, pFrameYuv->linesize, 0, height, pOutData, dstStride);

    sws_freeContext(pSwsContext);
    av_frame_free(&pFrameYuv);
    return iRet >= 0 ? 0 : -1;
}

主要遇到的花屏的问题都是因为宽高的问题,宽高没有适当的对调,当对YUV数据进行90度和270度旋转的时候,一定要对宽高进行对调

基本上我这边使用的旋转的主题的流程就都在上面了,下面就稍微介绍一下我在解决问题的过程中,所记录下来的一些知识,也稍微扩展一下知识面。

三、知识扩展

1、YUV 的相关知识

1、YUV数据的相关格式可以看这篇文章,总结的很好:详解 YUV 格式(I420/YUV420/NV12/NV12/YUV422)

2、我的I420的图像旋转主要就是参考这篇文章:视音频数据处理入门:I420 图像90° 、180°、270°旋转和左右镜像翻转

3、主要参考这篇文章:c++ 读取 yuv 图片_从YUV到RGB 主流的YUV420的格式:

这些主流YUV格式中,YUV420更常见(因为更节省空间啊),而从YUV420这种采样格式引申出来,叠加上不同的存储格式,就又可以细分为很多。Gemfield没有精力介绍这么多,你可以参考:http://www.fourcc.org/yuv.php 。

当YUV420采样格式叠加上planar 储存格式后,可以产生出YUV420P和YUV420SP格式。YUV420P是Y之后U之后V来储存,相当于RGB中的chw了,又可以细分为I420(又叫YU12)和YV12格式;而YUV420SP是Y之后UV交替…,又可以细分为NV12和NV21格式。这四种细分格式如下所示:

  • NV12 ,FourCC为0x3231564E ,1个像素12 bit, 8-bit Y plane,Y通道平面结束后是交叉进行的 U/V 通道平面,U/V使用2x2的采样率(各是Y的四分之一)。
  • NV21 ,FourCC为0x3132564E ,1个像素12 bit, 和NV12一样——除了U/V交叉的时候是先V再U,也就是U/V和V/U的区别;这个是Android上摄像头图像的标准
  • I420 (也叫YU12),FourCC为0x30323449 ,1个像素12 bit,8 bit Y 通道平面结束后,是U通道平面,最后是V通道平面;
  • YV12 ,FourCC为0x32315659,1个像素12 bit,8 bit Y 通道平面结束后,是V通道平面,最后是U通道平面;

4、YUV420的图片旋转:

1、Y表示亮度(Luminance),也就是灰度值。 "U"和"V"表示的则是色度(Chrominance),描述影像色彩及其饱和度,用于指定像素的颜色。

2、YUV的四个结论:

三个结论先记好:
1.YUV 4:4:4采样,每一个Y对应一组UV分量。
2.YUV 4:2:2采样,每两个Y共用一组UV分量。
3.YUV 4:2:0采样,每四个Y共用一组UV分量。

3、特别提醒旋转90和270后宽高要记得对调,不然会花屏

5、花屏问题该如何解决:音视频开发之视频花屏问题汇总分析

除了需要注意的,旋转的时候,一定要注意宽高对调。其他的问题,可以从上面找找答案。

2、YUV420sp 图片旋转90度

网上应该可以找到更好的,这个是我找到的,这里只是做一个记录,只有一个90度的:

void CUsbVideo::_Yuv420spRotate90(unsigned char* _pYUV, unsigned char* _pYUV90)
{
    int width = m_iHeight;
    int height = m_iWidth;
    int count = 0;
    int uvHeight = height >> 1;
    int imgSize = width * height;
    //    byte[] des = new byte[imgSize * 3 >> 1];
    //copy y
    for (int j = width - 1; j >= 0; j--) {
        for (int i = 0; i < height; i++) {
            _pYUV90[count++] = _pYUV[width * i + j];
        }
    }
    //u,v
    for (int j = width - 1; j > 0; j -= 2) {
        for (int i = 0; i < uvHeight; i++) {
            _pYUV90[count++] = _pYUV[imgSize + width * i + j - 1];
            _pYUV90[count++] = _pYUV[imgSize + width * i + j];
        }
    }
}

3、YUV NV21或NV12的旋转

这个代码没有测试过,不保真,麻烦自己测试下,有啥问题,概不负责。

void CUsbVideo::_Yuv420Rotate180(unsigned char* _pYUV, unsigned char* _pYUV180)
{
    //旋转180:将右下角的点作为第一个点,从右往左,从下往上取点
    //Y 宽:[0,w-1]  高:[0,h-1]
    int idx = 0;
    for (int i = m_iHeight-1; i >=0; i--){
        for (int j = m_iWidth-1 ; j >= 0; j--){
            _pYUV180[idx++] = *(_pYUV+(i*m_iWidth+j));
        }
    }
    uint8_t* uheader = _pYUV + m_iWidth*m_iHeight;
    uint8_t* vheader = uheader + m_iWidth*m_iHeight/4;
    int iVSize = m_iWidth*m_iHeight/4;
    //U
    for (int i = m_iHeight/2 - 1; i >= 0; i--){
        for (int j = m_iWidth/2-1 ; j >= 0; j--){
            _pYUV180[idx] = *(uheader + (i*m_iWidth / 2 + j));
            _pYUV180[idx++ + iVSize] = *(vheader + (i*m_iWidth / 2 + j));
        }
    }
}

void CUsbVideo::_Yuv420RotateFlip(unsigned char* _pYUV, unsigned char* _pYUVFlip)
{
    int idx = 0;
    //水平翻转:将右上角的点作为第一个点,从右往左,从上往下取点
    //Y 宽:[0,w-1]  高:[0,h-1]
    for (int i = 0; i<m_iHeight; i++){
        for (int j = m_iWidth - 1; j >= 0; j--){
            _pYUVFlip[idx++] = *(_pYUV + (i*m_iWidth + j));
        }
    }
    uint8_t* uheader = _pYUV + m_iWidth*m_iHeight;
    uint8_t* vheader = uheader + m_iWidth*m_iHeight / 4;
    int iVSize = m_iWidth*m_iHeight / 4;
    //U
    for (int i = 0; i < m_iHeight / 2 ; i++){
        for (int j = m_iWidth / 2 - 1; j >= 0; j--){
            _pYUVFlip[idx] = *(uheader + (i*m_iWidth / 2 + j));
            _pYUVFlip[idx++ + iVSize] = *(vheader + (i*m_iWidth / 2 + j));
        }
    }
}

void CUsbVideo::_Yuv420Rotate270(unsigned char* _pYUV, unsigned char* _pYUV270)
{
    // Rotate the Y luma
    int width = m_iHeight;
    int height = m_iWidth;
    int count = 0;
    int uvHeight = height >> 1;
    //copy y
    for (int j = width - 1; j >= 0; j--) {
        for (int i = 0; i < height; i++) {
            _pYUV270[count++] = _pYUV[width * i + j];
        }
    }
    //u,v
    uint8_t* uheader = _pYUV + m_iWidth * m_iHeight;
    uint8_t* vheader = uheader + m_iWidth * m_iHeight / 4;
    int iVSize = m_iWidth*m_iHeight / 4;
    width = width / 2;
    for (int j = width - 1; j > 0; j--) {
        for (int i = 0; i < uvHeight; i++) {
            _pYUV270[count] = uheader[width * i + j];
            _pYUV270[count++ + iVSize] = vheader[+ width * i + j];
        }
    }
}

void CUsbVideo::_Yuv420Rotate90(unsigned char* _pYUV, unsigned char* _pYUV90)
{
    // Rotate the Y luma
    int width = m_iHeight;
    int height = m_iWidth;
    int count = 0;
    int uvHeight = height >> 1;
    //copy y
    for (int j = 0; j < width; j++) {
        for (int i = 0; i < height; i++) {
            _pYUV90[count++] = _pYUV[width * i + j];
        }
    }
    //u,v
    uint8_t* uheader = _pYUV + m_iWidth * m_iHeight;
    uint8_t* vheader = uheader + m_iWidth * m_iHeight / 4;
    int iVSize = m_iWidth*m_iHeight / 4;
    width = width / 2;
    for (int j = 0; j < width; j++) {
        for (int i = 0; i < uvHeight; i++) {
            _pYUV90[count] = uheader[width * i + j];
            _pYUV90[count++ + iVSize] = vheader[+ width * i + j];
        }
    }
}

4、使用OpenCV进行图像翻转

Mat angleRectify(Mat img, float angle)
{
	cout << "--> angleRectify:" << img.rows << img.cols;
	Mat retMat = Mat::zeros(img.cols, img.rows, CV_8UC3);
	float anglePI = (float)(angle * CV_PI / 180);
	int xSm, ySm;
	for (int i = 0; i < retMat.rows; i++)
		for (int j = 0; j < retMat.cols; j++)
		{
			xSm = (int)((i - retMat.rows / 2) * cos(anglePI) - (j - retMat.cols / 2) * sin(anglePI) + 0.5);
			ySm = (int)((i - retMat.rows / 2) * sin(anglePI) + (j - retMat.cols / 2) * cos(anglePI) + 0.5);
			xSm += img.rows / 2;
			ySm += img.cols / 2;
			if (xSm >= img.rows || ySm >= img.cols || xSm <= 0 || ySm <= 0) {
				retMat.at<Vec3b>(i, j) = Vec3b(0, 0);
			}
			else {
				retMat.at<Vec3b>(i, j) = img.at<Vec3b>(xSm, ySm);
			}
		}
	return retMat;
}

Mat dstImg2 = angleRectify(srcImg, 270);
imshow("src", srcImg);

参考

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

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

相关文章

[多线程进阶] 常见锁策略

专栏简介: JavaEE从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录: 1. 常见的锁策略 1.1 乐观锁 vs 悲观锁 1.2 读写…

bootstrap 框架

文章目录bootstrap必须使用 HTML5 文档类型排版和链接默认栅格系统带有基本栅格的 HTML 代码媒体类型媒体类型逻辑运算符 用来做条件判断页面布局&#xff1a; 引入 css&#xff08;bootstrap.min.css&#xff09; 类名03-面包屑导航警告框、徽章、面包屑导航、按钮、按钮组卡…

css行内块元素垂直居中

css行内块元素垂直居中 div里边有个img标签&#xff0c;要想让img垂直居中&#xff0c;需要 给父盒子设置line-heightheightimg设置vertical-align:middle <div style"background-color: red; height: 150px;line-height: 150px;"><img src"images/…

Unity开发环境配置

Unity本体安装 1.首先下载安装unityhub,中文管网https://unity.cn/ 2.登录unityhub&#xff0c;选择你想要的版本安装 选择后按照提示选择个人免费试用的license,然后等待unity本体下载安装即可。 VSCode安装和配置 1.去官网https://code.visualstudio.com/下载vscode 2.u…

微信小程序 Springboot ssm房屋租赁系统uniapp设计与实现

房屋租赁系统用户和户主是基于微信端&#xff0c;管理员是基于网页端&#xff0c;系统采用java编程语言&#xff0c;mysql数据库&#xff0c; idea工具开发&#xff0c;本系统分为用户&#xff0c;户主&#xff0c;管理员三个角色&#xff0c;其中用户可以注册登陆小程序&#…

C++11入门

目录 C11简介 统一的列表初始化 {}初始化 std::initializer_list 文档介绍 std::initializer_list的类型 使用场景 initializer_list接口函数模拟实现 auto与decltype nullptr 范围for STL的变化 新容器 新方法 新函数 C11简介 1.在2003年C标准委员会曾经提交了一…

【浅学Redis】缓存 以及 缓存穿透、缓存击穿、缓存雪崩

缓存 以及 缓存击穿、缓存穿透、缓存雪崩1. 缓存1.1 缓存的作用1.2 缓存的应用场景1.3 引入缓存后的执行流程1.4 缓存的优点2. 缓存穿透2.1 场景2.2 解决策略1. 参数校验2. 缓存空值3. 缓存击穿3.1 场景3.2 解决策略4. 缓存雪崩4.1 场景4.2 解决策略5. 上面三者的区别1. 缓存 …

图像分割--入门了解

一. 三种分割 1. 语义分割&#xff08;semantic segmentation&#xff09; 语义分割&#xff1a;语义分割通过对图像中的每个像素进行密集的预测、推断标签来实现细粒度的推理&#xff0c;从而使每个像素都被标记为一个类别&#xff0c;但不区分属于相同类别的不同实例。 比…

ChatGPT之父:世界正被他搅动

阿尔特曼&#xff08;左&#xff09;与马斯克Mac LC2电脑ChatGPT这款聊天应用程序最近太火了&#xff01; 美国北密歇根大学的一名学生用它生成了一篇哲学课小论文&#xff0c;“惊艳”了教授&#xff0c;还得到了全班最高分。美国一项调查显示&#xff0c;53%的学生用它写过论…

Vue (2)

文章目录1. 模板语法1.1 插值语法1.2 指令语法2. 数据绑定3. 穿插 el 和 data 的两种写法4. MVVM 模型1. 模板语法 root 容器中的代码称为 vue 模板 1.1 插值语法 1.2 指令语法 图一 &#xff1a; 简写 &#xff1a; v-bind: 是可以简写成 &#xff1a; 的 总结 &#xff1a; …

Springboot + RabbitMq 消息队列

前言 一、RabbitMq简介 1、RabbitMq场景应用&#xff0c;RabbitMq特点 场景应用 以订单系统为例&#xff0c;用户下单之后的业务逻辑可能包括&#xff1a;生成订单、扣减库存、使用优惠券、增加积分、通知商家用户下单、发短信通知等等。在业务发展初期这些逻辑可能放在一起…

【23种设计模式】创建型模式详细介绍

前言 本文为 【23种设计模式】创建型模式详细介绍 相关内容介绍&#xff0c;下边具体将对单例模式&#xff0c;工厂方法模式&#xff0c;抽象工厂模式&#xff0c;建造者模式&#xff0c;原型模式&#xff0c;具体包括它们的特点与实现等进行详尽介绍~ &#x1f4cc;博主主页&…

计算机组成原理(一)

1.了解计算机硬件的发展和软件的发展历程&#xff1b; 硬件&#xff1a;   电子管时代&#xff08;1946-1959&#xff09;&#xff1a;电子管、声汞延迟线、磁鼓   晶体管时代&#xff08;1959-1964&#xff09;&#xff1a;晶体管、磁芯   中、小规模集成电路时代&#…

OpenStack云平台搭建(1) | 基础环境准备

目录 一、环境准备 1.1、关闭selinxu 1.2、关闭防火墙 1.3、修改主机名 1.4、配置时间同步服务器 1.5、配置域名 二、安装OpenStack库 2.1、启用OpenStack仓库的包 2.2、安装python-openstackclient 2.3、controller安装数据库 2.4、安装消息队列 2.5、配置缓存 2.…

Linux驱动开发基础__中断的线程化处理

目录 1 引入 2 内核机制 2.1 调用 request_threaded_irq 后内核的数据结构 2.2 request_threaded_irq 2.3 中断的执行过程 1 引入 复杂、耗时的事情&#xff0c;尽量使用内核线程来处理。工作队列用起来挺简单&#xff0c;但是它有一个缺点&#xff1a;工作队列中有多个 …

【Java 面试合集】HashMap中为什么要使用红黑树

HashMap中为什么要使用红黑树1. 概述 从源码的结构方面讲述下为什么HashMap要使用红黑树。那没有红黑树的时候&#xff0c;底层是基于什么逻辑进行存储的。 2. 底层结构 如果忽略红黑树的话&#xff0c;HashMap底层的数据存储结构如下&#xff1a; 总体而言就是数组 链表的形…

Vscode使用

我是四五年的webstorm忠粉&#xff0c;一直觉得它是世界上最好用、强大、方便的编辑器。 为了它深谙各种破解方法&#xff0c;突然有一天我知道的几种方法都不奏效了。破解的实在太累了&#xff0c;算了&#xff0c;尝试尝试不同的工具吧。 含泪挥别webstrom&#xff0c;捏着…

JDBC编程复习

文章目录JDBC1.概念2.原理3. 如何使用JDBC编程1. 下载mysql的jdbc驱动2. 项目中引入驱动4. JDBC使用1. 和数据库建立连接2.获取连接3. Statement对象4. 释放资源JDBC 1.概念 JDBC,即Java Database Connectivity&#xff0c;java数据库连接。是Java提供的API用来执行SQL语句&a…

SWPU新生赛WriteUp

一个线上赛&#xff0c;这个NSSCTF最爽的就是没有靶机操作的一分钟冷却&#xff0c;10.11比赛结束&#xff0c;但是我还要看看工控&#xff0c;所以不打这个比赛了&#xff0c;先把wp写了&#xff0c;pwn入门真TM艰难 WEB 前面送分题&#xff0c;中间的也是基础题&#xff0c;…

SQL概述及数据定义

文章目录一、SQL概述1.简介2.特点3.组成4.SQL分类5.书写规范二、数据定义1.模式的定义与删除①定义模式②删除模式2.基本表的定义、删除与修改①定义基本表②数据类型③模式与表④修改基本表⑤删除基本表3.索引的建立与删除①建立索引②删除索引一、SQL概述 1.简介 SQL (Stru…