这个表达式的算法是什么意思?
p = ((p<<1)&0666) | ((q<<3)&0110) | (Image->scanLine(y+1)[x+1] != 0);
“图形宝石IV”一书中的“使用Neigborhood地图进行二值图像细化”算法:
static int masks[] = {0200, 0002, 0040, 0010};
uchar delete_[512] =
{
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,1,0,0,1,1, 0,1,1,1,0,0,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,1,1,1,0,1,1, 0,0,1,1,0,0,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 1,1,1,1,0,0,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,1,1,0,0,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 1,1,1,1,0,0,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
1,0,1,1,1,0,1,1, 1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0, 1,1,1,1,0,0,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
1,0,1,1,1,0,1,1, 1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
1,0,1,1,1,0,1,1, 0,0,1,1,0,0,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,1,1,0,0,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0, 1,1,1,1,0,0,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
1,0,1,1,1,0,1,1, 1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0, 1,1,1,1,0,0,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
1,0,1,1,1,0,1,1, 1,1,1,1,1,1,1,1
};
int xsize, ysize;
int x, y;
int i;
int count = 1;
int p, q;
uchar *qb;
int m;
xsize = Image->width();
ysize = Image->height();
qb = (uchar*) malloc (xsize*sizeof(uchar));
qb[xsize-1] = 0;
while(count)
{
count = 0;
for (i = 0; i < 4; ++i)
{
m = masks[i];
p = Image->scanLine(0)[0] != 0;
for (x = 0; x < xsize-1; ++x)
qb[x] = p = ((p<<1)&0006) | (Image->scanLine(0)[x+1] != 0);
// Scan image for pixel deletion candidates.
for (y = 0; y < ysize-1; ++y)
{
q = qb[0];
p = ((q<<3)&0110) | (Image->scanLine(y+1)[0] != 0);
for (x = 0; x < xsize-1; ++x)
{
q = qb[x];
p = ((p<<1)&0666) | ((q<<3)&0110) | (Image->scanLine(y+1)[x+1] != 0);
qb[x] = p;
if ((p&m)==0 && delete_[p])
{
count++;
Image->scanLine(y)[x] = 0;
}
}
答案 0 :(得分:4)
变量m
,p
,q
和qb
数组的元素是9位数字,代表像素的3x3像素“邻域”
假设你的图像看起来像这样(每个字母代表一个像素,它是'开'或'关'(1或0,黑色或白色):
---x---
0123456
| 0 abcdefg
| 1 hijklmn
y 2 opqrstu
| 3 vwxyz{|
(x,y)位置(2,1)处的像素是j
。该像素的邻域是
bcd
ijk // 3x3 grid centered on j
pqr
由于每个像素具有二进制值,因此邻域可以用9位表示。上面的邻域可以线性写出,以二进制表示为bcd_ijk_pqr
。连续3个像素的分组使得八进制成为一个很好的选择,因为每个八进制数字代表三位。
一旦将邻域表示为9位值,就可以对其进行位操作。诸如((p << 1) & 0666)
之类的操作占用邻域,将所有位移到左边的一个位置,并清除最右边的位列。例如,班次将bcd_ijk_pqr
更改为cdi_jkp_qr@
(其中@
表示“空”位,默认为0)。然后掩码将其更改为cd@_jk@_qr@
。以3x3网格形式表示:
cd@
jk@
qr@
基本上,整个网格已经向左移动。
类似地,诸如((q << 3) & 0110)
的操作将所有位移位三个位置(向上移动行)并清除前两列的位。因此bcd_ijk_pqr
变为ijk_pqr_@@@
,然后在屏蔽后变为@@k_@@r_@@@
。
算法的要点是评估每个像素的邻域以确定是否关闭该像素(删除它)。这一行做了评估:
if ((p&m)==0 && delete_[p])
在该行之前的所有内容都是为了在p
中设置邻域。编写代码,以便每次传递每个像素值一次。
qb
数组存储上一扫描线中每个像素的邻域。请注意,qb
的元素只有8位宽。这意味着省略了邻域的左上角像素。这不是问题,因为任何时候使用qb
,它都会向上移动。
所以回答你关于这条线的作用的问题:
p = ((p<<1)&0666) | ((q<<3)&0110) | (Image->scanLine(y+1)[x+1] != 0);
通过合并以下内容创建像素的邻域:
例如,关于j
的邻域计算如下:
p = bc@_ij@_pq@ | @@d_@@k_@@@ | r
bc@ | @@d | @@@ bcd
p = ij@ | @@k | @@@ = ijk
pq@ | @@@ | @@r pqr
答案 1 :(得分:2)
看起来像是一种按位操纵。你不理解个别的操作,或者你不明白为什么正在进行操作(如果后者,算法描述的链接是必不可少的,BTW)?
该行中的<<
右移运算符(根据RH参数将p的位移动到更重要的位置)&
在此上下文中为按位和|
按位或这里要记住的一个有用的事情是以“0”开头的整数文字用 octal 表示,这意味着你可以从屏幕上的表示中读取二进制数字(好吧,无论如何,只需要一点点练习。这里的常数是(666)_8 =(110110110)_2和(0110)_8 =(001001000)_2。