缩放算法优化步骤详解

news2024/11/17 3:03:00

添加链接描述

背景

假设数据存放在在unsigned char* m_pData 里面,宽和高分别是:m_nDataWidth m_nDataHeight
给定缩放比例:fXZoom fYZoom,返回缩放后的unsigned char* dataZoom
这里采用最简单的缩放算法即:
根据比例计算原图和缩放后图坐标的对应关系:缩放后图坐标*缩放比例 = 原图坐标

原始代码 未优化

#pragma once
class zoomBlock
{
public:
	zoomBlock() {};
	~zoomBlock();
	void zoomDataSSE128(unsigned char* dataZoom, float  fXZoom, float fYZoom);
	void zoomData(unsigned char* dataZoom, float  fXZoom, float fYZoom);
	void test(float  fXZoom =0.5, float fYZoom=0.5);
	void init(int DataWidth, int DataHeight);
private:
	void computeSrcValues(int* srcValues, size_t size, float zoom, int dataSize);

private:
	unsigned char* m_pData = nullptr;
	float m_fXZoom = 1 ;//x轴缩放比例  m_nXZoom=1时 不缩放
	float m_fYZoom = 1 ;//y轴缩放比例
	int m_nDataWidth = 0;
	int m_nDataHeight = 0;
};

#include "zoomBlock.h"
#include <stdio.h>
#include <iostream>
#include<iomanip>
#define SAFE_DELETE_ARRAY(p) { if( (p) != NULL ) delete[] (p); (p) = NULL; }

zoomBlock::~zoomBlock()
{
	SAFE_DELETE_ARRAY(m_pData);
}
void zoomBlock::init(int DataWidth, int DataHeight)
{
	m_nDataWidth = DataWidth;
	m_nDataHeight = DataHeight;
	m_pData = new unsigned char[m_nDataWidth* m_nDataHeight];
	for (int i = 0; i < m_nDataWidth * m_nDataHeight; ++i)
	{
		m_pData[i] = static_cast<unsigned char>(i);  // Replace this with your data initialization logic
	}
}



void zoomBlock::zoomData(unsigned char* dataZoom, float  fXZoom, float fYZoom)
{
	int nZoomDataWidth = fXZoom * m_nDataWidth;
	int nZoomDataHeight = fYZoom * m_nDataHeight;
	
	for (size_t row = 0; row < nZoomDataHeight; row++)
	{
		for (size_t column = 0; column < nZoomDataWidth; column ++)
		{
			//1
			int srcx = std::min(int(row / fYZoom), m_nDataHeight - 1);
			int srcy = std::min(int(column / fXZoom), m_nDataWidth - 1);

			//2
			int srcPos = srcx * m_nDataHeight + srcy;
			int desPos = row * nZoomDataHeight + column;
			dataZoom[desPos] = m_pData[srcPos];
		}
	}
}


void zoomBlock::test(float  fXZoom, float fYZoom)
{
	init(8,8);
	std::cout << "Values in m_pData:" << std::endl;

	for (int i = 0; i < m_nDataWidth * m_nDataHeight; ++i)
	{
		std::cout << std::setw(4) << static_cast<int>(m_pData[i]) << " ";
		if ((i + 1) % m_nDataWidth == 0) 
		{  // Adjust the value based on your data
			std::cout << std::endl;
		}
	}

	unsigned char* dataZoom = new unsigned char[fXZoom * m_nDataWidth * fYZoom * m_nDataHeight];

	zoomData(dataZoom, fXZoom, fYZoom);

	// Print or inspect the values in m_dataZoom
	int nZoomDataWidth = fXZoom * m_nDataWidth;
	int nZoomDataHeight = fYZoom * m_nDataHeight;

	std::cout << "Values in m_dataZoom:" << std::endl;
	for (int i = 0; i < nZoomDataHeight * nZoomDataWidth; ++i)
	{
		std::cout << std::setw(4)<< static_cast<int>(dataZoom[i]) << " ";
		if ((i + 1) % nZoomDataWidth == 0) {  // Adjust the value based on your data
			std::cout << std::endl;
		}
	}

	SAFE_DELETE_ARRAY(dataZoom);

}

测试代码

int main()
{
	 zoomBlock zoomBlocktest;
	 zoomBlocktest.test(1.5,1.5);
	return 0;
}

在这里插入图片描述
其中函数
·void zoomBlock::zoomData(unsigned char* dataZoom, float fXZoom, float fYZoom)·
没有使用任何加速优化,现在来分析它。

sse128

我们知道sse128可以一次性处理4个int类型,所以我们把最后一层for循环改成,4个坐标的算法,不满4个的单独计算

void zoomBlock::zoomDataSSE128(unsigned char* dataZoom, float  fXZoom, float fYZoom)
{
	int nZoomDataWidth = fXZoom * m_nDataWidth;
	int nZoomDataHeight = fYZoom * m_nDataHeight;

	for (size_t row = 0; row < nZoomDataHeight; row++)
	{
		int remian = nZoomDataWidth % 4;
		for (size_t column = 0; column < nZoomDataWidth - remian; column += 4)
		{
			//第一个坐标
			int srcx = std::min(int(row / fYZoom), m_nDataHeight - 1);
			int srcy = std::min(int(column / fXZoom), m_nDataWidth - 1);
			int srcPos = srcx * m_nDataHeight + srcy;
			int desPos = row * nZoomDataHeight + column;
			dataZoom[desPos] = m_pData[srcPos];

			//第二个坐标
			int srcx1 = std::min(int((row+1) / fYZoom), m_nDataHeight - 1);
			int srcy1 = std::min(int((column+1) / fXZoom), m_nDataWidth - 1);

			int srcPos1 = srcx1 * m_nDataHeight + srcy1;
			int desPos1 = (row+1) * nZoomDataHeight + column+1;
			dataZoom[desPos1] = m_pData[srcPos1];

			//第3个坐标
			// 。。。
			//第4个坐标
			// 。。。
		}
		// Process the remaining elements (if any) without SSE
		for (size_t column = nZoomDataWidth - remian; column < nZoomDataWidth; column++)
		{
			int srcx = std::min(int(row / fYZoom), m_nDataHeight - 1);
			int srcy = std::min(int(column / fXZoom), m_nDataWidth - 1);
			int srcPos = srcx * m_nDataHeight + srcy;
			int desPos = row * nZoomDataHeight + column;
			dataZoom[desPos] = m_pData[srcPos];
		}
	}
}

上面 一次处理四个坐标的代码要改成sse的代码

在最里层的循环里面,每次都要计算 row / fYZoom 和 column / fXZoom,这个实际上可以挪出for循环,计算一次存到数组里

数据坐标desPos和srcPos ,必须放在最内存的循环里

所以我们用calculateSrcIndex函数单独处理 row / fYZoom 和 column / fXZoom,希望达到如下效果:

void calculateSrcIndex(int* srcValues, int size, float zoom,int max)
{
	for (int i = 0; i < size; i++)
	{
		srcValues[i] = std::min(int(i/zoom),max);
	}
}

改成sse:

void calculateSrcIndex(int* srcValues, int size, float zoom,int max)
{
	__m128i mmIndex, mmSrcValue, mmMax;
	mmMax = _mm_set1_epi32(max);
	float zoomReciprocal = 1.0f / zoom;

	int remian = size % 4;
	for (size_t i = 0; i < size - remian; i += 4)
	{
		mmIndex = _mm_set_epi32(i + 3, i + 2, i + 1, i);
		mmSrcValue = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(mmIndex), _mm_set1_ps(zoomReciprocal)));

		// Ensure srcValues are within the valid range [0, max]
		mmSrcValue = _mm_min_epi32(mmSrcValue, mmMax);

		// Store the result to the srcValues array
		_mm_storeu_si128(reinterpret_cast<__m128i*>(&srcValues[i]), mmSrcValue);
	}

	// Process the remaining elements (if any) without SSE
	for (size_t i = size - remian; i < size; i++)
	{
		srcValues[i] = std::min(int(i / zoom), max);
	}
}

解释:
这里主要处理int型数据,为了使用sse加速,要使用__m128i类型来存储4个int

加载int到__m128i:

  1. __m128i _mm_set1_epi32(int i);
    这个指令是使用1个i,来设置__m128i,将__m128i看做4个32位的部分,则每个部分都被赋为i;

  2. __m128i _mm_set_epi32(int i3, int i2,int i1, int i0);
    说明:使用4个int(32bits)变量来设置__m128i变量;
    返回值:如果返回值__m128i,分为r0,r1,r2,r3返回值规则如下:

r0 := i0
r1 := i1
r2 := i2
r3 := i3

  1. __m128i _mm_cvtps_epi32 (__m128 a)
    Converts packed 32-bit integers in a to packed single-precision (32-bit) floating-point elements.

加载float到__m128

  1. __m128 _mm_set1_ps(float w)
    对应于_mm_load1_ps的功能,不需要字节对齐,需要多条指令。(r0 = r1 = r2 = r3 = w)
  2. __m128 _mm_cvtepi32_ps (__m128i a)
    Converts packed 32-bit integers in a to packed single-precision (32-bit) floating-point elements.

float乘法

__m128 dst = _mm_mul_ps (__m128 a, __m128 b)
将a, b中的32位浮点数相乘,结果打包给dst

取最小值

__m128i _mm_min_epi32 (__m128i a, __m128i b)
Compare packed signed 32-bit integers in a and b, and store packed minimum values in dst.
Operation
FOR j := 0 to 3
i := j*32
dst[i+31:i] := MIN(a[i+31:i], b[i+31:i])
ENDFOR

所以代码修改为

	int* srcX = new int[nZoomDataHeight];
	int* srcY = new int[nZoomDataWidth];
	calculateSrcIndex(srcX, nZoomDataHeight, fXZoom , m_nDataHeight - 1);
	calculateSrcIndex(srcY, nZoomDataWidth, fYZoom, m_nDataWidth - 1);

	for (size_t row = 0; row < nZoomDataHeight; row++)
	{
		int remian = nZoomDataWidth % 4;
		for (size_t column = 0; column < nZoomDataWidth - remian; column += 4)
		{
			//第一个坐标
			int srcPos = srcX[row] * m_nDataHeight + srcY[column];
			int desPos = row * nZoomDataHeight + column;
			dataZoom[desPos] = m_pData[srcPos];
			...
			}
	}

然后把坐标的计算转为sse

void zoomBlock::zoomDataSSE128(unsigned char* dataZoom, float  fXZoom, float fYZoom)
{
	int nZoomDataWidth = fXZoom * m_nDataWidth;
	int nZoomDataHeight = fYZoom * m_nDataHeight;

	
	int* srcX = new int[nZoomDataWidth];
	int* srcY = new int[nZoomDataHeight];
	
	calculateSrcIndex(srcX, nZoomDataWidth, fXZoom, m_nDataWidth - 1);
	calculateSrcIndex(srcY, nZoomDataHeight, fYZoom, m_nDataHeight - 1);

	for (size_t y = 0; y < nZoomDataHeight; y++)
	{
		int remian = nZoomDataWidth % 4;
		for (size_t x = 0; x < nZoomDataWidth - remian; x += 4)
		{
			__m128i mmsrcX = _mm_set_epi32(srcX[x + 3], srcX[x + 2], srcX[x+1], srcX[x]);
			__m128i srcPosIndices = _mm_add_epi32(
				_mm_set1_epi32(srcY[y] * m_nDataWidth),
				mmsrcX);

			__m128i desPosIndices = _mm_add_epi32(
				_mm_set1_epi32(y * nZoomDataWidth),
				_mm_set_epi32(x + 3, x + 2, x + 1, x)
				);
			dataZoom[desPosIndices.m128i_i32[0]] = m_pData[srcPosIndices.m128i_i32[0]];
			dataZoom[desPosIndices.m128i_i32[1]] = m_pData[srcPosIndices.m128i_i32[1]];
			dataZoom[desPosIndices.m128i_i32[2]] = m_pData[srcPosIndices.m128i_i32[2]];
			dataZoom[desPosIndices.m128i_i32[3]] = m_pData[srcPosIndices.m128i_i32[3]];
			/*cout << "srcPosIndices: " << srcPosIndices.m128i_i32[0] << " , desPosIndices : " << desPosIndices.m128i_i32[0] << endl;
				cout << "srcPosIndices: " << srcPosIndices.m128i_i32[1] << " , desPosIndices : " << desPosIndices.m128i_i32[1] << endl;
				cout << "srcPosIndices: " << srcPosIndices.m128i_i32[2] << " , desPosIndices : " << desPosIndices.m128i_i32[2] << endl;
				cout << "srcPosIndices: " << srcPosIndices.m128i_i32[3] << " , desPosIndices : " << desPosIndices.m128i_i32[3] << endl;*/
		}
		// Process the remaining elements (if any) without SSE
		for (size_t x = nZoomDataWidth - remian; x < nZoomDataWidth; x++)
		{
			int srcy = std::min(int(y / fYZoom), m_nDataHeight - 1);
			int srcx = std::min(int(x / fXZoom), m_nDataWidth - 1);
			int srcPos = srcy * m_nDataHeight + srcx;
			int desPos = y * nZoomDataHeight + x;
			dataZoom[desPos] = m_pData[srcPos];
		}
	}
	delete[] srcX;
	delete[] srcY;
}

完整的代码

 #pragma once
class zoomBlock
{
public:
	zoomBlock() {};
	~zoomBlock();
	void zoomDataSSE128(unsigned char* dataZoom, float  fXZoom, float fYZoom);
	void zoomData(unsigned char* dataZoom, float  fXZoom, float fYZoom);
	void test(float  fXZoom =0.5, float fYZoom=0.5);
	void init(int DataWidth, int DataHeight);
private:
	inline void calculateSrcIndex(int* srcValues, int size, float zoom, int max);

private:
	unsigned char* m_pData = nullptr;
	float m_fXZoom = 1 ;//x轴缩放比例  m_nXZoom=1时 不缩放
	float m_fYZoom = 1 ;//y轴缩放比例
	int m_nDataWidth = 0;
	int m_nDataHeight = 0;
};

#include "zoomBlock.h"
#include <stdio.h>
#include <iostream>
#include<iomanip>
#include<immintrin.h> 
using namespace std;
#define SAFE_DELETE_ARRAY(p) { if( (p) != NULL ) delete[] (p); (p) = NULL; }

zoomBlock::~zoomBlock()
{
	SAFE_DELETE_ARRAY(m_pData);
}
void zoomBlock::init(int DataWidth, int DataHeight)
{
	m_nDataWidth = DataWidth;
	m_nDataHeight = DataHeight;
	m_pData = new unsigned char[m_nDataWidth* m_nDataHeight];
	for (int i = 0; i < m_nDataWidth * m_nDataHeight; ++i)
	{
		m_pData[i] = static_cast<unsigned char>(i);  // Replace this with your data initialization logic
	}
}


void zoomBlock::zoomData(unsigned char* dataZoom, float  fXZoom, float fYZoom)
{
	int nZoomDataWidth = fXZoom * m_nDataWidth;
	int nZoomDataHeight = fYZoom * m_nDataHeight;
	
	for (size_t y = 0; y < nZoomDataHeight; y++)
	{
		for (size_t x = 0; x < nZoomDataWidth; x ++)
		{
			//1
			int srcy = std::min(int(y / fYZoom), m_nDataHeight - 1);
			int srcx = std::min(int(x / fXZoom), m_nDataWidth - 1);

			//2
			int srcPos = srcy * m_nDataWidth + srcx;
			int desPos = y * nZoomDataWidth + x;
			dataZoom[desPos] = m_pData[srcPos];
		}
	}
}

inline void zoomBlock::calculateSrcIndex(int* srcValues, int size, float zoom,int max)
{
	__m128i mmIndex, mmSrcValue, mmMax;
	mmMax = _mm_set1_epi32(max);
	float zoomReciprocal = 1.0f / zoom;

	int remian = size % 4;
	for (size_t i = 0; i < size - remian; i += 4)
	{
		mmIndex = _mm_set_epi32(i + 3, i + 2, i + 1, i);
		mmSrcValue = _mm_cvttps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(mmIndex), _mm_set1_ps(zoomReciprocal)));

		// Ensure srcValues are within the valid range [0, max]
		mmSrcValue = _mm_min_epi32(mmSrcValue, mmMax);

		// Store the result to the srcValues array
		_mm_storeu_si128(reinterpret_cast<__m128i*>(&srcValues[i]), mmSrcValue);
	}

	// Process the remaining elements (if any) without SSE
	for (size_t i = size - remian; i < size; i++)
	{
		srcValues[i] = std::min(int(i / zoom), max);
	}
}

void zoomBlock::zoomDataSSE128(unsigned char* dataZoom, float  fXZoom, float fYZoom)
{
	int nZoomDataWidth = fXZoom * m_nDataWidth;
	int nZoomDataHeight = fYZoom * m_nDataHeight;

	
	int* srcX = new int[nZoomDataWidth];
	int* srcY = new int[nZoomDataHeight];
	
	calculateSrcIndex(srcX, nZoomDataWidth, fXZoom, m_nDataWidth - 1);
	calculateSrcIndex(srcY, nZoomDataHeight, fYZoom, m_nDataHeight - 1);

	for (size_t y = 0; y < nZoomDataHeight; y++)
	{
		int remian = nZoomDataWidth % 4;
		for (size_t x = 0; x < nZoomDataWidth - remian; x += 4)
		{
			/*int srcPos = srcx * m_nDataHeight + srcy;
			int desPos = row * nZoomDataHeight + column;*/
			//dataZoom[desPos] = m_pData[srcPos];

			//__m128i mmsrcY = _mm_loadu_si128((__m128i*)(srcY));
			__m128i mmsrcX = _mm_set_epi32(srcX[x + 3], srcX[x + 2], srcX[x+1], srcX[x]);
			__m128i srcPosIndices = _mm_add_epi32(
				_mm_set1_epi32(srcY[y] * m_nDataWidth),
				mmsrcX);

			__m128i desPosIndices = _mm_add_epi32(
				_mm_set1_epi32(y * nZoomDataWidth),
				_mm_set_epi32(x + 3, x + 2, x + 1, x)
				);
			dataZoom[desPosIndices.m128i_i32[0]] = m_pData[srcPosIndices.m128i_i32[0]];
			dataZoom[desPosIndices.m128i_i32[1]] = m_pData[srcPosIndices.m128i_i32[1]];
			dataZoom[desPosIndices.m128i_i32[2]] = m_pData[srcPosIndices.m128i_i32[2]];
			dataZoom[desPosIndices.m128i_i32[3]] = m_pData[srcPosIndices.m128i_i32[3]];
			/*cout << "srcPosIndices: " << srcPosIndices.m128i_i32[0] << " , desPosIndices : " << desPosIndices.m128i_i32[0] << endl;
				cout << "srcPosIndices: " << srcPosIndices.m128i_i32[1] << " , desPosIndices : " << desPosIndices.m128i_i32[1] << endl;
				cout << "srcPosIndices: " << srcPosIndices.m128i_i32[2] << " , desPosIndices : " << desPosIndices.m128i_i32[2] << endl;
				cout << "srcPosIndices: " << srcPosIndices.m128i_i32[3] << " , desPosIndices : " << desPosIndices.m128i_i32[3] << endl;*/
		}
		// Process the remaining elements (if any) without SSE
		for (size_t x = nZoomDataWidth - remian; x < nZoomDataWidth; x++)
		{
			int srcy = std::min(int(y / fYZoom), m_nDataHeight - 1);
			int srcx = std::min(int(x / fXZoom), m_nDataWidth - 1);
			int srcPos = srcy * m_nDataHeight + srcx;
			int desPos = y * nZoomDataHeight + x;
			dataZoom[desPos] = m_pData[srcPos];
		}
	}
	delete[] srcX;
	delete[] srcY;
}


void zoomBlock::test(float  fXZoom, float fYZoom)
{
	init(8,4);
	std::cout << "Values in m_pData:" << std::endl;

	for (int i = 0; i < m_nDataWidth * m_nDataHeight; ++i)
	{
		std::cout << std::setw(4) << static_cast<int>(m_pData[i]) << " ";
		if ((i + 1) % m_nDataWidth == 0) 
		{  // Adjust the value based on your data
			std::cout << std::endl;
		}
	}

	
	int nZoomDataWidth = fXZoom * m_nDataWidth;
	int nZoomDataHeight = fYZoom * m_nDataHeight;
	unsigned char* dataZoom = new unsigned char[nZoomDataWidth * nZoomDataHeight];
	zoomDataSSE128(dataZoom, fXZoom, fYZoom);
	//zoomData(dataZoom, fXZoom, fYZoom);
	// Print or inspect the values in m_dataZoom

	std::cout << "Values in m_dataZoom:" << std::endl;
	for (int i = 0; i < nZoomDataHeight * nZoomDataWidth; ++i)
	{
		std::cout << std::setw(4)<< static_cast<int>(dataZoom[i]) << " ";
		if ((i + 1) % nZoomDataWidth == 0) {  // Adjust the value based on your data
			std::cout << std::endl;
		}
	}

	SAFE_DELETE_ARRAY(dataZoom);

}

int main()
{
	 zoomBlock zoomBlocktest;
	 zoomBlocktest.test(2,1);
	return 0;
}

在这里插入图片描述

AVX 256

inline void zoomBlock::calculateSrcIndex256(int* srcValues, int size, float zoom, int max)
{
	__m256i ymmIndex, ymmSrcValue, ymmMax;
	ymmMax = _mm256_set1_epi32(max);
	float zoomReciprocal = 1.0f / zoom;

	int remian = size % 8;
	for (size_t i = 0; i < size - remian; i += 8)
	{
		ymmIndex = _mm256_set_epi32(i + 7, i + 6, i + 5, i + 4, i + 3, i + 2, i + 1, i);
		ymmSrcValue = _mm256_cvtps_epi32(_mm256_mul_ps(_mm256_cvtepi32_ps(ymmIndex), _mm256_set1_ps(zoomReciprocal)));

		// Ensure srcValues are within the valid range [0, max]
		ymmSrcValue = _mm256_min_epi32(ymmSrcValue, ymmMax);

		// Store the result to the srcValues array
		_mm256_storeu_si256(reinterpret_cast<__m256i*>(&srcValues[i]), ymmSrcValue);
	}

	// Process the remaining elements (if any) without AVX2
	for (size_t i = size - remian; i < size; i++)
	{
		srcValues[i] = std::min(int(i / zoom), max);
	}
}
void zoomBlock::zoomDataAVX2(unsigned char* dataZoom, float fXZoom, float fYZoom)
{
	int nZoomDataWidth = fXZoom * m_nDataWidth;
	int nZoomDataHeight = fYZoom * m_nDataHeight;

	int* srcX = new int[nZoomDataWidth];
	int* srcY = new int[nZoomDataHeight];

	calculateSrcIndex(srcX, nZoomDataWidth, fXZoom, m_nDataWidth - 1);
	calculateSrcIndex(srcY, nZoomDataHeight, fYZoom, m_nDataHeight - 1);

	for (size_t y = 0; y < nZoomDataHeight; y++)
	{
		int remian = nZoomDataWidth % 8;
		for (size_t x = 0; x < nZoomDataWidth - remian; x += 8)
		{
			__m256i ymmSrcX = _mm256_set_epi32(srcX[x + 7], srcX[x + 6], srcX[x + 5], srcX[x + 4],
				srcX[x + 3], srcX[x + 2], srcX[x + 1], srcX[x]);
			__m256i srcPosIndices = _mm256_add_epi32(
				_mm256_set1_epi32(srcY[y] * m_nDataWidth),
				ymmSrcX);

			__m256i desPosIndices = _mm256_add_epi32(
				_mm256_set1_epi32(y * nZoomDataWidth),
				_mm256_set_epi32(x + 7, x + 6, x + 5, x + 4, x + 3, x + 2, x + 1, x));

			dataZoom[desPosIndices.m256i_i32[0]] = m_pData[srcPosIndices.m256i_i32[0]];
			dataZoom[desPosIndices.m256i_i32[1]] = m_pData[srcPosIndices.m256i_i32[1]];
			dataZoom[desPosIndices.m256i_i32[2]] = m_pData[srcPosIndices.m256i_i32[2]];
			dataZoom[desPosIndices.m256i_i32[3]] = m_pData[srcPosIndices.m256i_i32[3]];
			dataZoom[desPosIndices.m256i_i32[4]] = m_pData[srcPosIndices.m256i_i32[4]];
			dataZoom[desPosIndices.m256i_i32[5]] = m_pData[srcPosIndices.m256i_i32[5]];
			dataZoom[desPosIndices.m256i_i32[6]] = m_pData[srcPosIndices.m256i_i32[6]];
			dataZoom[desPosIndices.m256i_i32[7]] = m_pData[srcPosIndices.m256i_i32[7]];
		}

		// Process the remaining elements (if any) without AVX2
		for (size_t x = nZoomDataWidth - remian; x < nZoomDataWidth; x++)
		{
			int srcy = std::min(int(y / fYZoom), m_nDataHeight - 1);
			int srcx = std::min(int(x / fXZoom), m_nDataWidth - 1);
			int srcPos = srcy * m_nDataWidth + srcx;
			int desPos = y * nZoomDataWidth + x;
			dataZoom[desPos] = m_pData[srcPos];
		}
	}
	delete[] srcX;
	delete[] srcY;
}

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

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

相关文章

【C语言】数据类型和变量

前言&#x1f49e;&#x1f49e; 啦啦啦~这里是土土数据结构学习笔记&#x1f973;&#x1f973; &#x1f4a5;个人主页&#xff1a;大耳朵土土垚的博客 &#x1f4a5; 所属专栏&#xff1a;C语言笔记 &#x1f4a5;欢迎大家&#x1f973;&#x1f973;点赞✨收藏&#x1f49…

【Node.js从基础到高级运用】二、搭建开发环境

Node.js入门&#xff1a;搭建开发环境 在上一篇文章中&#xff0c;我们介绍了Node.js的基础概念。现在&#xff0c;我们将进入一个更实际的阶段——搭建Node.js的开发环境。这是每个Node.js开发者旅程中的第一步。接下来&#xff0c;我们将详细讨论如何安装Node.js和npm&#…

将python编写的网站制作成docker镜像并上传到Github Packages上

文章目录 前言Docker安装docker注意事项 创建Dockerfile注意事项 构建 Docker 镜像运行 Docker 镜像 发布到Github Packages坑坑到位申请token的坑docker登录的坑给镜像添加标签的坑docker推送的坑 在Github Packages上查看总结 前言 还记得上一篇《借助ChatGPT使用Python搭建…

借助Aspose.html控件,在 Java 中将 URL 转换为 PDF

如果您正在寻找一种将实时 URL 中的网页另存为 PDF文档的方法&#xff0c;那么您来对地方了。在这篇博文中&#xff0c;我们将学习如何使用 Java 将 URL 转换为 PDF。从实时 URL转换HTML网页可以像任何其他文档一样保存所需的网页以供离线访问。将网页保存为 PDF 格式可以轻松突…

PandasAI—让AI做数据分析

安装 pip install pandasai !pip install --upgrade pandas pandasai 导入依赖项 import pandas as pdfrom pandasai import PandasAIfrom pandasai.llm.openai import OpenAI使用pandas创建一个数据框 df pd.DataFrame({"country": ["United States",…

MKS Type 631C 真空计说明详细内容查看图中目录

MKS Type 631C 真空计说明详细内容查看图中目录

【sw网络监控】通过snmp协议相关的snmp-exporter(收集交换机网络监控数据)+ promethus + grafana

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

C++异常处理机制

目录 一&#xff0c;传统C语言处理异常 二&#xff0c;C异常概念 三&#xff0c;使用 1. 自定义异常体系 2. 在函数调用链中异常栈展开匹配原则 3. 异常的重新抛出 四&#xff0c;异常规范 五&#xff0c;异常安全 六&#xff0c;C标准库的异常体系 七&#xff0c;异…

【开源】JAVA+Vue.js实现高校宿舍调配管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能需求2.1 学生端2.2 宿管2.3 老师端 三、系统展示四、核心代码4.1 查询单条个人习惯4.2 查询我的室友4.3 查询宿舍4.4 查询指定性别全部宿舍4.5 初次分配宿舍 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的…

liteIDE 解决go root报错 go: cannot find GOROOT directory: c:\go

liteIDE环境配置 我使用的liteIDE为 x36 5.9.5版本 。在查看–>选项 中可以看到 LiteEnv&#xff0c;双击LiteEnv &#xff0c;在右侧选择对应系统的env文件&#xff0c;我的是win64系统&#xff0c;所以文件名为win64.env 再双击 win64.env &#xff0c;关闭当前窗口&…

python淘宝网页爬虫数据保存到 csv和mysql(selenium)

数据库连接设置&#xff08;表和字段要提前在数据库中建好&#xff09; # 数据库中要插入的表 MYSQL_TABLE goods# MySQL 数据库连接配置,根据自己的本地数据库修改 db_config {host: localhost,port: 3306,user: root,password: ma*****6,database: may2024,charset: utf8mb…

TCP包头、TCP为什么安全可靠、UDP和TCP的区别、http协议

我要成为嵌入式高手之3月8日Linux高编第十八天&#xff01;&#xff01; __________________________________________________ 学习笔记 TPC包头 1、序号 发送端发送数据包的编号 2、确认号 已经确认接收到的数据的编号&#xff0c;只有当ACK为1时&#xff0c;该位才有用 …

简介maven核心:pom项目对象模型

Maven Maven 意思是知识的积累者&#xff0c;最初是为了简化 Jakarta Turbine 项目中的构建过程。有几个项目&#xff0c;每个项目都有自己的 Ant 构建文件&#xff0c;它们都略有不同。JAR 被检入 CVS。我们想要一种标准的方式来构建项目&#xff0c;清楚地定义项目的组成&am…

Jmeter 测试使用基本组件结构

JMeter简介 Apache组织开发的开源免费压测工具纯Java程序&#xff0c;跨平台性强源程序可以从网上下载高扩展性可对服务器、网络或对象模拟巨大的负载&#xff0c;进行压力测试可以用于接口测试支持分布式、多节点部署 JMeter安装 下载位置 官网https://jmeter.apache.org/ …

基于单片机的灭火机器人设计

目 录 摘 要 I Abstract II 引 言 1 1 系统方案设计 4 1.1 方案论证 4 1.2 灭火机器人系统工作原理 4 2 系统硬件设计 6 2.1 单片机 6 2.2 火焰探测系统设计 8 2.3 灭火系统设计 8 2.4 循迹模块设计 9 2.5 电机驱动模块 10 3 系统软件设计 12 3.1 系统软件开发环境 12 3.2 系统…

防御保护 IPSEC VPPN实验

实验背景&#xff1a;FW1和FW2是双机热备 主备备份模式。 实验要求&#xff1a;在FW5和FW3之间建立一条IPSEC通道&#xff0c;保证10.0.2.0/24网段可以正常访问到192.168.1.0/24 IPSEC VPPN实验配置&#xff08;由于是双机热备状态&#xff0c;所以FW1和FW2只需要配置FW1主设…

STM32标准库——(20)WDG看门狗

1.WDG简介 独立看门狗(IWDG)由专用的低速时钟(LSI)驱动&#xff0c;即使主时钟发生故障它也仍然有效。窗口看门狗由从APB1时钟分频后得到的时钟驱动&#xff0c;通过可配置的时间窗口来检测应用程序非正常的过迟或过早的操作 2.IWDG框图 它的结构和定时器是非常相似的&#xf…

打印机项目需求

打印机项目需求 工作情况如下 我拿着一张带有二维码的纸张对准“打印机的摄像头”“打印机的摄像头”解析我的二维码假如解析后的二维码内容是&#xff1a;24030924发送一个http请求&#xff1a;https://…/getMessage?code24030924外部服务器会返回一个json数据{‘地址’:‘…

Python编程实验六:面向对象应用

目录 一、实验目的与要求 二、实验内容 三、主要程序清单和程序运行结果 第1题 第2题 四、实验结果分析与体会 一、实验目的与要求 &#xff08;1&#xff09;通过本次实验&#xff0c;学生应掌握类的定义与对象的创建、类的继承与方法的覆盖&#xff1b; &#xff08;2…

高级语言讲义2016计专(仅高级语言部分)

1.斐波那契序列的第n项可以表示成以下形式&#xff0c;编写一个非递归函数&#xff0c;返回该数列的第n项的数值 #include <stdio.h>int func(int n) {if(n1||n2)return 1;int p1,q1,num;for(int i3; i<n; i) {numpq;qp;pnum;}return num; } 2.在MXN的二维数组A中&am…