私たちはしばしば遭遇されている画像における2値画像処理は、時には我々は、前処理アルゴリズムは、需要に合わせて、二値画像の画像データ、及びこの時間は、我々は簡単なを書くことができるかどうかを判断する必要がある行って裁判官もする機能は、例えば、私は次のように非常に単純なコードは書きました:
BOOL IM_IsBinaryImage_C(符号なしのchar *の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がある場合、すなわち、その対が図バイナリ画像ではない、0ではない、その後の決意を必要とせず、すぐに戻すことができます。
チャートはバイナリイメージでない場合は、通常、我々はすぐに結果を返すことができるようになりますし、最悪のシナリオでは、彼は、二値画像であることを起こるということですので、我々はすべてのピクセルを横断終えたものとします。我々は、二値画像(* 4000 4000)16メガバイトについて試験し、試験の所要時間15msのは、時間を浪費を最小化するために、以下では、決意のSIMD命令を最適化するために使用されてもよいです。
BOOL IM_IsBinaryImage_SSE_Bug(符号なしのchar *のSrc、int型の幅、int型の高さを、int型ストライド)
{
int型チャンネル=ストライド/ 幅;
もし(SRC == NULL) を返す 偽。
もし((幅<= 0)||(高さ<= 0)) を返す 偽。
もし(!チャンネル= 1) を返す 偽。
int型のBlockSize = 16、ブロック=(幅*チャンネル)/ BlockSizeに。
用(INT = Y 0、Y <身長; Y ++) // 約16倍の速度増加
{
符号なしのchar * LinePS = Srcの+ Y * ストライド。
用(INT X = 0 ; X <ブロック* BlockSizeに、X + = BlockSizeに)
{
__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 =ブロック* BlockSizeに、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