C++ 解释ARM霓虹灯图像采样

C++ 解释ARM霓虹灯图像采样,c++,opencv,arm,computer-vision,neon,C++,Opencv,Arm,Computer Vision,Neon,我正在尝试编写OpenCV的cv::resize()的更好版本,我遇到了一个错误,代码如下: 这段代码是用来将一幅图像下采样2次,但我无法得到算法。我想首先将该算法转换为C,然后尝试修改它以便于学习。将其转换为任何大小的下采样是否容易 功能是: static void inline resizeRow(uint32_t *dst, uint32_t *src, uint32_t pixelsPerRow) { const uint32_t * rowB = src + pixelsPer

我正在尝试编写OpenCV的cv::resize()的更好版本,我遇到了一个错误,代码如下: 这段代码是用来将一幅图像下采样2次,但我无法得到算法。我想首先将该算法转换为C,然后尝试修改它以便于学习。将其转换为任何大小的下采样是否容易

功能是:

static void inline resizeRow(uint32_t *dst, uint32_t *src, uint32_t pixelsPerRow)
{
    const uint32_t * rowB = src + pixelsPerRow;

    // force the number of pixels per row to a multiple of 8
    pixelsPerRow = 8 * (pixelsPerRow / 8);

    __asm__ volatile("Lresizeloop: \n" // start loop
                     "vld1.32 {d0-d3}, [%1]! \n" // load 8 pixels from the top row
                     "vld1.32 {d4-d7}, [%2]! \n" // load 8 pixels from the bottom row
                     "vhadd.u8 q0, q0, q2 \n" // average the pixels vertically
                     "vhadd.u8 q1, q1, q3 \n"
                     "vtrn.32 q0, q2 \n" // transpose to put the horizontally adjacent pixels in different registers
                     "vtrn.32 q1, q3 \n"
                     "vhadd.u8 q0, q0, q2 \n" // average the pixels horizontally
                     "vhadd.u8 q1, q1, q3 \n"
                     "vtrn.32 d0, d1 \n" // fill the registers with pixels
                     "vtrn.32 d2, d3 \n"
                     "vswp d1, d2 \n"
                     "vst1.64 {d0-d1}, [%0]! \n" // store the result
                     "subs %3, %3, #8 \n" // subtract 8 from the pixel count
                     "bne Lresizeloop \n" // repeat until the row is complete
: "=r"(dst), "=r"(src), "=r"(rowB), "=r"(pixelsPerRow)
: "0"(dst), "1"(src), "2"(rowB), "3"(pixelsPerRow)
: "q0", "q1", "q2", "q3", "cc"
);
}

To call it:

 // downscale the image in place
    for (size_t rowIndex = 0; rowIndex < height; rowIndex+=2)
    {
        void *sourceRow = (uint8_t *)buffer + rowIndex * bytesPerRow;
        void *destRow = (uint8_t *)buffer + (rowIndex / 2) * bytesPerRow;
        resizeRow(destRow, sourceRow, width);
    }
static void inline resizeRow(uint32_t*dst、uint32_t*src、uint32_t像素如箭头所示)
{
const uint32_t*rowB=src+pixelsPerRow;
//将每行的像素数强制为8的倍数
像素箭头=8*(像素箭头/8);
__asm\uuuuvolatile(“Lresizeloop:\n”//start循环
“vld1.32{d0-d3},[%1]!\n”//从顶行加载8个像素
“vld1.32{d4-d7},[%2]!\n”//从最下面一行加载8个像素
“vhadd.u8 q0、q0、q2\n”//垂直平均像素数
vhadd.u8 q1、q1、q3\n
“vtrn.32 q0,q2\n”//transpose将水平相邻的像素放入不同的寄存器中
vtrn.32第一季度,第三季度\n
“vhadd.u8 q0、q0、q2\n”//水平平均像素数
vhadd.u8 q1、q1、q3\n
“vtrn.32 d0,d1\n”//用像素填充寄存器
vtrn.32 d2,d3\n
vswp d1,d2\n
“vst1.64{d0-d1},[%0]!\n”//存储结果
“subs%3,%3,#8\n”//从像素计数中减去8
“bne Lresizeloop\n”//重复该行,直到该行完成
:“=r”(dst),“=r”(src),“=r”(行B),“=r”(像素箭头)
:“0”(dst)、“1”(src)、“2”(行B)、“3”(像素箭头)
:“q0”、“q1”、“q2”、“q3”、“cc”
);
}
称之为:
//缩小图像的比例
对于(大小\u t行索引=0;行索引<高度;行索引+=2)
{
void*sourceRow=(uint8_t*)缓冲区+行索引*bytesPerRow;
void*destRow=(uint8_t*)缓冲区+(行索引/2)*bytesPerRow;
resizeRow(destRow、sourceRow、width);
}

算法非常简单。它从当前行读取8个像素,从下面的行读取8个像素。然后,它使用vhadd(减半添加)指令垂直平均8个像素。然后变换像素的位置,使水平相邻的像素对现在位于单独的寄存器中(垂直排列)。然后再进行另一组减半相加,将这些加起来平均。然后再次转换结果,将其置于原始位置并写入目标。该算法可以重写以处理不同的缩放积分大小,但正如所述,它只能通过平均值进行2x2到1的缩减。下面是C代码的等效代码:

static void inline resizeRow(uint32_t *dst, uint32_t *src, uint32_t pixelsPerRow)
{
    uint8_t * pSrc8 = (uint8_t *)src;
    uint8_t * pDest8 = (uint8_t *)dst;
    int stride = pixelsPerRow * sizeof(uint32_t);
    int x;
    int r, g, b, a;

    for (x=0; x<pixelsPerRow; x++)
    {
       r = pSrc8[0] + pSrc8[4] + pSrc8[stride+0] + pSrc8[stride+4];
       g = pSrc8[1] + pSrc8[5] + pSrc8[stride+1] + pSrc8[stride+5];
       b = pSrc8[2] + pSrc8[6] + pSrc8[stride+2] + pSrc8[stride+6];
       a = pSrc8[3] + pSrc8[7] + pSrc8[stride+3] + pSrc8[stride+7];
       pDest8[0] = (uint8_t)((r + 2)/4); // average with rounding
       pDest8[1] = (uint8_t)((g + 2)/4);
       pDest8[2] = (uint8_t)((b + 2)/4);
       pDest8[3] = (uint8_t)((a + 2)/4);
       pSrc8 += 8; // skip forward 2 source pixels
       pDest8 += 4; // skip forward 1 destination pixel
    }
static void inline resizeRow(uint32_t*dst、uint32_t*src、uint32_t像素如箭头所示)
{
uint8_t*pSrc8=(uint8_t*)src;
uint8_t*pDest8=(uint8_t*)dst;
int stride=像素箭头*大小f(uint32_t);
int x;
int r,g,b,a;

对于(x=0;x而言,该算法非常简单。它从当前行读取8个像素,从下面的行读取8个像素。然后使用vhadd(对半添加)指令对8个像素进行垂直平均。然后它转换像素的位置,以便水平相邻的像素对现在位于单独的寄存器(垂直排列)中。然后,它再进行一组减半相加,将它们平均起来。然后再次转换结果,将它们放在原始位置,并写入目标。该算法可以重写以处理不同的缩放整数大小,但在编写时,它只能通过平均进行2x2到1的缩减。下面是C代码等价于四旬斋:

static void inline resizeRow(uint32_t *dst, uint32_t *src, uint32_t pixelsPerRow)
{
    uint8_t * pSrc8 = (uint8_t *)src;
    uint8_t * pDest8 = (uint8_t *)dst;
    int stride = pixelsPerRow * sizeof(uint32_t);
    int x;
    int r, g, b, a;

    for (x=0; x<pixelsPerRow; x++)
    {
       r = pSrc8[0] + pSrc8[4] + pSrc8[stride+0] + pSrc8[stride+4];
       g = pSrc8[1] + pSrc8[5] + pSrc8[stride+1] + pSrc8[stride+5];
       b = pSrc8[2] + pSrc8[6] + pSrc8[stride+2] + pSrc8[stride+6];
       a = pSrc8[3] + pSrc8[7] + pSrc8[stride+3] + pSrc8[stride+7];
       pDest8[0] = (uint8_t)((r + 2)/4); // average with rounding
       pDest8[1] = (uint8_t)((g + 2)/4);
       pDest8[2] = (uint8_t)((b + 2)/4);
       pDest8[3] = (uint8_t)((a + 2)/4);
       pSrc8 += 8; // skip forward 2 source pixels
       pDest8 += 4; // skip forward 1 destination pixel
    }
static void inline resizeRow(uint32_t*dst、uint32_t*src、uint32_t像素如箭头所示)
{
uint8_t*pSrc8=(uint8_t*)src;
uint8_t*pDest8=(uint8_t*)dst;
int stride=像素箭头*大小f(uint32_t);
int x;
int r,g,b,a;

对于(x=0;xAmazing答案。垂直方向上的像素平均值是多少?两个像素,然后是两个像素?需要什么来支持不同的缩放大小?我在纸上试过了,但仍然没有得到转置操作和水平相邻像素:/vhadd指令相当于c=(a+b+1)/2.代码的第一部分垂直平均像素,因为包含顶行和底行的霓虹灯寄存器一起平均。由于没有跨霓虹灯矢量元素水平运行的vhadd,因此需要对值进行转置,以便将水平相邻的元素放置在单独的寄存器中。转置后然后,再次对它们进行平均(将像素“水平”平均)。请参阅此处的VTRN描述:它写得很正确。我写了4个字节,然后将目标指针向前推进4个字节。嗯?2D数组?不,它是指向一维像素行的简单指针。不知道你的困惑来自何处。惊人的答案。像素如何垂直平均?两个像素,然后两个像素?需要做什么支持不同大小的缩放?我在纸上试过,但仍然没有得到转置操作和水平相邻像素:/vhadd指令相当于c=(a+b+1)/2.代码的第一部分垂直平均像素,因为包含顶行和底行的霓虹灯寄存器一起平均。由于没有跨霓虹灯矢量元素水平运行的vhadd,因此需要对值进行转置,以便将水平相邻的元素放置在单独的寄存器中。转置后注意,再次对它们进行平均(对像素进行“水平”平均)。请参阅此处的VTRN描述:它写得正确。我写了4个字节,然后将目标指针向前推进4个字节。嗯?2D数组?不,它是指向一维像素行的简单指针。不确定您的混淆来自何处。您发现了一个非常糟糕的示例:1)它会随着时间的推移而截断