[로그인] 빠르게 결정할 곤충이 있는지 여부를 확인 만 흑백 그레이 스케일 이미지의 값 (즉, 이진 영상인지)의 오른쪽 및 불리언 변수 중에 잘못.

  우리가 자주 발생하는 이미지의 바이너리 이미지 처리, 때때로 우리는 사전 처리 알고리즘은 수요에 맞춰 이진 이미지의 이미지 데이터, 이번에는 우리가 간단하게 쓸 수 있는지 여부를 확인해야합니다 실시 판사로 기능, 예를 들어, 나는 다음과 같이 매우 간단한 코드가 쓴 :

부울 IM_IsBinaryImage_C (부호없는 문자는 *의 Src, int로 폭, INT의 높이, int로 보폭) 
{ 
    INT 채널 = 보폭 / 폭;
    경우 SRC (== NULL)이                             반환  거짓 ;
    경우 ((폭 <= 0 ) || (높이 <= 0 ))             반환  거짓 ;
    경우 (채널! = 1 )                             반환  거짓 ;
    위한 ( int로 Y = 0; Y <높이; Y ++ )                                         
    { 
        부호 CHAR * LinePS의 Src = + Y * 보폭;
         ( INT X = 0 , X <채널 폭 *, X ++ ) 
        { 
            경우 (! (LinePS [X] = 255 ) && (LinePS [X]! = 0 ))     반환  거짓 ;
            // (((LinePS [X] == 255) || (LinePS [X] == 0)) == false)의 경우 false를 반환; 
        } 
    } 
    반환  사실 ; 
}

  그렇지 않으면, 화소 (255)가있는 경우, 즉, 그 쌍은도 1의 바이너리 이미지가 아니라, 0이 아닌 후속 판정 없이도 즉시 복귀 할 수있다.  

  차트는 바이너리 이미지가 아닌 경우, 일반적으로, 우리는 곧 그 최악의 시나리오는 그가 바이너리 이미지 될 일이다, 결과를 반환 할 수 있습니다, 그래서 우리는 모든 픽셀을 가로 지르는 완료하여야한다. 우리는 다음은 결정 SIMD 명령어를 최적화 할 수 있습니다 시간이 많이 소요 최소화하기 위해 바이너리 이미지 (* 4000 4000) 16메가바이트, 테스트 소요 시간이 15ms, 테스트 :

부울 IM_IsBinaryImage_SSE_Bug (부호없는 문자는 *의 Src, int로 폭, INT의 높이, int로 보폭) 
{ 
    INT 채널 = 보폭 / 폭;
    경우 SRC (== NULL)이                             반환  거짓 ;
    경우 ((폭 <= 0 ) || (높이 <= 0 ))             반환  거짓 ;
    경우 (채널! = 1 )                             반환  거짓 ;
    INT 블록 크기 = 16 , = 블록 (폭 * 채널) /블록 크기; 

     ( INT Y = 0 ; Y <높이 Y ++)                                         //     速度提升约16倍
    { 
        부호 문자 * LinePS의 Src = + Y * 보폭;
         ( INT X = 0 , X <블록 * 블록 크기; X + = 블록 사이즈) 
        { 
            __m128i SrcV = _mm_loadu_si128 ((__ m128i의 *) (LinePS + X)); 
            __m128i MaskW = _mm_cmpeq_epi8 (SrcV, _mm_set1_epi8 ( 255 ));        
            __m128i MaskB =  _mm_cmpeq_epi8 (SrcV, _mm_setzero_si128 ());
            __m128i 마스크 =_mm_or_si128 (MaskW, MaskB);
            경우 (! _mm_movemask_epi8 (마스크) = 65535 )     돌아  거짓 ;            //     (((LinePS [X] == 255) || (LinePS [X] == 0)) == false)의 경우 false를 반환; 
        }
          ( INT X = 블록 * 블록 크기; X <채널 폭 *, X ++ ) 
        { 
            경우 (! (LinePS [X] = 255 ) && (LinePS [X]! = 0 ))     반환  거짓 ; 
        } 
    } 
    반환  사실 ; 
}

  由于SIMD指令里没有_mm_cmpneq_epi8函数,我们该用代码1片段里被注释掉的那种逻辑来判断一个像素是否是黑色和白色,这里当然也有一些技巧,比如_mm_movemask_epi8指令的运用。我们判断这个像素是否等于255和0,当然,一个像素不可能同时满足这两个条件,不满足的Mask返回0,满足则Mask返回255,所以如果他是黑色和白色,你们这两个Mask进行或操作肯定就为255,否则或操作后就为0,SIMD中这样的比较可以一次性进行16个像素,如果这16个像素都符合条件,那么或操作后的mask都为255,这样通过使用_mm_movemask_epi8来判断这个mask就完成了16个像素的判断。

  很显然,这个过程的效率要高很多,测试16MB的真二值图,也就1ms就完成了判断。

  好,我用上面的那个代码写成DLL,供C#调用,相关的函数声明如下:

[DllImport("IsBinaryImage.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true)]
 private static extern bool IM_IsBinaryImage_C(byte* Src, int Width, int Height, int Stride);
[DllImport("IsBinaryImage.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern bool IM_IsBinaryImage_SSE_Bug(byte* Src, int Width, int Height, int Stride);

  可出来的结果令我非常诧异,我测试了下面这2幅图:

           

            测试图1                               测视图2 (页面压缩了)

  这两幅图都不是二值图,他们在某些边缘位置都有抗锯齿操作。但是那个IM_IsBinaryImage_C检测图1不是二值图像,检测图2 是二值图像,而IM_IsBinaryImage_SSE_Bug则检测图1是二值图像,图2不是二值图像。开始我以为是我的SSE代码写错了,我就又换了一种写法,如下所示:

bool IM_IsBinaryImage_SSE(unsigned char *Src, int Width, int Height, int Stride)
{
    int Channel = Stride / Width;
    if (Src == NULL)                            return false;
    if ((Width <= 0) || (Height <= 0))            return false;
    if (Channel != 1)                            return false;
    int BlockSize = 16, Block = (Width * Channel) / BlockSize;
    bool Flag = true;
    for (int Y = 0; Y < Height; Y++)                                        //    速度提升约16倍
    {
        unsigned char *LinePS = Src + Y * Stride;
        if (Flag == false)    break;
        for (int X = 0; X < Block * BlockSize; X += BlockSize)
        {
            __m128i SrcV = _mm_loadu_si128((__m128i *)(LinePS + X));
            __m128i MaskW = _mm_cmpeq_epi8(SrcV, _mm_set1_epi8(255));        //    _mm_cmpeq_epi8是自带的,如果使用_mm_cmpneq_epu8则慢了一些。
            __m128i MaskB = _mm_cmpeq_epi8(SrcV, _mm_setzero_si128());
            __m128i Mask = _mm_or_si128(MaskW, MaskB);
            if (_mm_movemask_epi8(Mask) != 65535)
            {
                Flag = false;                            //    if ((LinePS[X] == 255) || (LinePS[X] == 0)) = false, return  false
                break;
            }
        }
        for (int X = Block * BlockSize; X < Width * Channel; X++)
        {
            if ((LinePS[X] != 255) && (LinePS[X] != 0))
            {
                Flag = false;
                break;
            }
        }
    }
    return Flag;
}

  这个时候测绘对所有的图像结果都正确了。

  但是,我觉得代码片段2应该是不会有任何错误的啊。为什么会出现这种现象呢。

  后面从网上查了下,C++的bool变量就只有true和false, 是字节变量,这个可以用printf("%d", sizeof(false));来验证,会打印1。而在其他语言中,似乎是int类型。但是我在C#中用 MessageBox.Show(sizeof(bool).ToString());  似乎也是弹出1。

  但是,当我们把这些函数的返回值都改为int后,在C#中调用就正常了,比如:

int IM_IsBinaryImage_C(unsigned char *Src, int Width, int Height, int Stride)

  也就是说上述的IM_IsBinaryImage_SSE_Bug函数体并无Bug。这到底是怎么回事,还请万能的网络高手有空予以解疑。

  附上测试工程和代码:https://files.cnblogs.com/files/Imageshop/ISBinaryImage.rar

추천

출처www.cnblogs.com/Imageshop/p/11110198.html