简体   繁体   English

将灰度图像转换为较小的“逐像素”灰度图像

[英]Converting a greyscale image to a smaller 'pixel by pixel' greyscale image

I've got this image which is massive but 1 square represents a number of pixel values but I want an image that has only 1 pixel with a particular value. 我得到的图像很大,但是1平方代表许多像素值,但是我想要一个只有1个像素且具有特定值的图像。 The squares are not all the same size. 正方形的大小不尽相同。

Some of the columns are narrower and some are wider. 有些列较窄,有些则较宽。 This is the example which is part of the big image: 这个例子是大形象的一部分:

在此处输入图片说明

As you can see the squares on the left hand side is bigger than the one on the right handside. 如您所见,左侧的正方形大于右侧的正方形。 That's the problem! 那就是问题所在!

Actual image: 实际图片:

在此处输入图片说明

For example, using the code below, when I try to convert my image to a smaller pixel by pixel one, I get this, which is completely different to the initial picture. 例如,使用下面的代码,当我尝试将图像逐像素转换为较小像素时,我得到了这一点,这与初始图片完全不同。

在此处输入图片说明

from PIL import Image
import numpy as np

img = Image.open('greyscale_intense.png').convert('L')  # convert image to 8-bit grayscale
WIDTH, HEIGHT = img.size

a = list(img.getdata()) # convert image data to a list of integers
# convert that to 2D list (list of lists of integers)
a = np.array ([a[offset:offset+WIDTH] for offset in range(0, WIDTH*HEIGHT, WIDTH)])

print " "
print "Intial array from image:"  #print as array
print " "
print a

rows_mask = np.insert(np.diff(a[:, 0]).astype(np.bool), 0, True)
columns_mask = np.insert(np.diff(a[0]).astype(np.bool), 0, True)
b = a[np.ix_(rows_mask, columns_mask)]


print " "
print "Subarray from Image:"  #print as array
print " "
print b


print " "
print "Subarray from Image (clearer format):"  #print as array
print " "
for row in b: #print as a table like format
    print(' '.join('{:3}'.format(value) for value in row))

img = Image.fromarray(b, mode='L')

img.show()

What I've done in the code is create an array from the initial image and then by ignoring an repeated values, create a subarray that has no repeated values. 我在代码中所做的工作是从初始图像创建一个数组,然后通过忽略重复值,创建一个没有重复值的子数组。 The new image was constructed using that. 新图像是使用该图像构建的。

For example for this image: 例如,此图像:

在此处输入图片说明

The result I get is: 我得到的结果是:

在此处输入图片说明

As you can see from the array 38 is repeated 9 times while 27 is repeated 8 times... 从数组中可以看到,重复38次9次,重复27次8次...

My final aim is to do the same process for a coloured RGB image as shown here. 我的最终目标是对彩色RGB图像执行相同的处理,如下所示。

在此处输入图片说明

Please help! 请帮忙!

What you have here is most probably the result of some image magnification with a non-integer scaling factor and the nearest-neighbor resampling rule. 您所拥有的很可能是具有非整数比例因子和最近邻居重采样规则的某些图像放大结果。 So all these large pixels probably have the same size to one unit. 因此,所有这些大像素可能具有一个单位相同的大小。

To obtain the exact pixel widths (repeat all that follows in the vertical direction), it suffices to draw an horizontal line and find the discontinuities. 为了获得精确的像素宽度(重复沿垂直方向的所有像素宽度),只需画一条水平线并找到不连续点即可。 It could turn out that two neighboring pixels have the same values, so that you have to find the discontinuity elsewhere. 可能会发现两个相邻像素具有相同的值,因此您必须在其他位置找到不连续点。 As you have a very good estimate of the widths, it is easy to detect such a situation. 由于您对宽度有很好的估计,因此很容易检测到这种情况。


Actually, it is probably even possible to guess the locations of the changes knowing the average pixel widths: they will occur at floor(nw) or possibly floor(nw+c) , where w and c are rational numbers that you need to determine. 实际上,甚至有可能知道平均像素宽度来猜测变化的位置:它们将出现在floor(nw)或可能在floor(nw+c) ,其中wc是您需要确定的有理数。

Using the above method, you can plot the relation between n and the locations of the transitions. 使用以上方法,可以绘制n与过渡位置之间的关系。

I don't feel like writing the code, but you could either: 我不想编写代码,但是您可以:

a) "roll" ( see here ) the image one pixel to the right and difference (subtract) the rolled image from the original and then use np.where to find all pixels greater than zero as those are the "edges" where your "squares" end, ie where a pixel is different from its neighbour. a)将图片“滚动”( 请参见此处 ),将图片向右移一个像素,然后将滚动图片与原始图片相差(减去),然后使用np.where查找所有大于零的像素,因为这些像素是“边缘” ,其中“正方形”的末端,即像素与其邻居不同的地方。 Then find columns where any element is nonzero and use those as the indices to get values from your original image. 然后找到任何元素都不为零的列,并将其用作索引以从原始图像中获取值。 Then roll the image down one pixel and find the horizontal rows of interest, and repeat as above but for the horizontal "edges" . 然后将图像向下滚动一个像素,找到感兴趣的水平行,然后重复上述操作,但要保留水平“边缘”

Or 要么

b) convolve the image with a differencing kernel that replaces each pixel with the difference between it and its neighbour to the right and then proceed as above. b)用差分内核对图像进行卷积,该内核用右侧与其相邻像素之间的差异替换每个像素,然后按上述步骤进行操作。 The kernel for difference between self and neighbour to the right would be: 自我和右边邻居之间差异的核是:

0  0  0
0 -1  1
0  0  0 

While the difference between self and neighbour below would be: 虽然下面的自我和邻居之间的区别是:

0  0  0
0 -1  0
0  1  0

The Pillow documentation for creating kernels and applying them is here . 用于创建内核和应用内核的Pillow文档在此处


I'll illustrate what I mean with ImageMagick at the command line. 我将在命令行中说明ImageMagick的含义。 First, I clone your image, and in the copy I roll the image to the right by one pixel, then I difference the result of rolling with the original image and make a new output image - normalised for greater contrast. 首先,我克隆您的图像,然后在副本中将图像向右滚动一个像素,然后将滚动结果与原始图像不同,然后制作一个新的输出图像-标准化以获得更大的对比度。

convert CwinB.png \( +clone -roll +1 \) -compose difference -composite -normalize h.png

在此处输入图片说明

Now I do the same again, but roll the image vertically by one pixel: 现在,我再次执行相同的操作,但是将图像垂直滚动一个像素:

convert CwinB.png \( +clone -roll +0+1 \) -compose difference -composite -normalize v.png

在此处输入图片说明

Now combine both of those and take whichever image is the lighter at each pixel: 现在将两者结合起来,以每个像素处较亮的图像为准:

convert [vh].png -compose lighten -composite z.png

在此处输入图片说明

Hopefully you can see it finds the edges of your squares and you can now choose any row, or column that is entirely black to find your original pixels. 希望您能看到它找到了正方形的边缘,现在您可以选择任何全黑的行或列来查找原始像素。

If I get it right you want to obtain original resolution of nearest neighbor enlarged image. 如果我做对了,您想获得最近邻居放大图像的原始分辨率。 So what to do: 那么该怎么办:

  1. compute horizontal grid sizes 计算水平网格大小

    if you new the original resolution and enlarging process you could compute the square sizes directly. 如果您使用原始分辨率和放大流程,则可以直接计算平方大小。 However if you do not know how the scaling was done safer would be compute it from image. 但是,如果您不知道如何更安全地进行缩放,则可以从图像计算出来。

    So what you need to do is count how many consequent pixels have the same color in all horizontal lines starting at x=0 . 因此,您需要做的是计算从x=0开始的所有水平线上有多少个具有相同颜色的后续像素。 Remember the smallest one that will be the first column width. 记住最小的将是第一列的宽度。

    Now do the same but start from x+column_width , then next column until you got all the columns widths. 现在执行相同的操作,但是从x+column_width开始,然后从下一列开始,直到获得所有列的宽度。

  2. compute vertical grid sizes 计算垂直网格大小

    It is the same as #1 but you process vertical lines starting from y=0 . #1相同,但您要处理从y=0开始的垂直线。

  3. create and copy new image 创建并复制新图像

    number of columns and rows from #1,#2 will give you the original resolution of image so create new image of the same size. #1,#2中的列数和行数将为您提供图像的原始分辨率,因此请创建相同大小的新图像。

    Then just set its each pixel with the corresponding grid square mid pixel color. 然后只需将其每个像素设置为相应的网格正方形中间像素颜色即可。

Here small C++/VCL example (sorry not a python coder): 这是一个小的C ++ / VCL示例(抱歉,不是python编码器):

void rescale_back(Graphics::TBitmap *bmp)
    {
    int *gx,*gy,nx,ny;  // original image
    int x,y,xs,ys;      // rescaled image
    int xx,yy,n;
    DWORD **p;          // direct pixel acces p[y][x]
    // prepare buffers
    xs=bmp->Width;
    ys=bmp->Height;
    p =new DWORD*[ys];
    gx=new int[xs];
    gy=new int[ys];
    // enable direct pixel access (VCL stuff ignore)
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    for (y=0;y<ys;y++) p[y]=(DWORD*)bmp->ScanLine[y];
    // compute column sizes
    for (nx=0,x=0;x<xs;)        // loop columns
        {
        for (n=0,y=0;y<ys;y++)  // find smallest column starting from x
            {
            for (xx=x;xx<xs;xx++) if (p[y][x]!=p[y][xx]) break;
            xx-=x; if ((!n)||(n>xx)) n=xx;
            }
        gx[nx]=x+(n>>1); nx++; x+=n;    // store mid position of column
        }
    // compute row sizes
    for (ny=0,y=0;y<ys;)        // loop rows
        {
        for (n=0,x=0;x<xs;x++)  // find smallest row starting from y
            {
            for (yy=y;yy<ys;yy++) if (p[y][x]!=p[yy][x]) break;
            yy-=y; if ((!n)||(n>yy)) n=yy;
            }
        gy[ny]=y+(n>>1); ny++; y+=n;    // store mid position of row
        }
    // copy data
    for (yy=0;yy<ny;yy++)
     for (xx=0;xx<nx;xx++)
      p[yy][xx]=p[gy[yy]][gx[xx]];
    // crop
    bmp->SetSize(nx,ny);
    // release buffers
    delete[] p;
    delete[] gx;
    delete[] gy;
    }

Using this on your input image from your duplicate question: 在重复的问题中,在输入图像上使用它:

在

results in this output: 结果如下:

出

In case you need to do this for bilinear filtered images see: 如果您需要对双线性过滤后的图像执行此操作,请参见:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM