Generating matrix/image kernel from List
Style


put spaces after comas to improve readability;
remove unnecessary parenthesis;
put spaces around operators;
put two spaces before and one space after the comment sign (#);
remove the 0 in range(0, &lt;whatever&gt;) as it is the default value;
except Exception as e is bad here as you are only actually expecting IndexError; plus the as e part is unnecessary as you don't use e (well, you do in the second snippet, but you could just raise with nothing more, even though the whole except: raise is useless as it adds nothing over the default behaviour);
range(len(&lt;something&gt;)) is often a red flag as it is better to iterate over elements than indices; but here, I don't think you have a choice anyway;
divmod can help you get rid of math.ceil.


In fact, only changing your first snippet to using divmod yield a 5% speedup on my machine:

def get_kernel(pixels, width):
    kernels = []
    pixels = [x for row in pixels for x in row]  # 2D list to 1D 
    for i in range(len(pixels)):
        y, x = divmod(i, width)
        try:
            kernel = [
                    pixels[(y-1) * width + x - 1], pixels[(y-1) * width + x], pixels[(y-1) * width + x + 1],
                    pixels[y * width + x - 1], pixels[y * width + x], pixels[y * width + x + 1],
                    pixels[(y+1) * width + x - 1], pixels[(y+1) * width + x], pixels[(y+1) * width + x + 1],
            ]
        except IndexError:
            kernel = [0]
        kernels.append(kernel)  # Add kernel to kernels list
    return kernels


Algorithm

There is one thing that bothers you about this snippet: it is not generic enough to allow for kernels of various radius. There is an other thing that bothers me, it is how it warps around the edges of the image but the bottom. The fact that some_list[-x] will return the \$x^{th}\$ element of some_list starting from the end combined to the flattening of the list will make it so that when at the top row or when reaching a pixel at the right or the left of the image, the coordinates involved will always be a valid index in pixels. But for the last row, (y+1) * width + x will exceed the number of elements in pixels and raise IndexError. This inconsistent behaviour bothers me.

Looking at how you handle your kernels in your other question, we can see that kernels that are not full-sized are discarded. Great, let's generate kernels of lower size for the edges of the image. The key idea here is knowing that slicing a list will always return a list:

&gt;&gt;&gt; a = list(range(20))
&gt;&gt;&gt; a[4:8]
[4, 5, 6, 7]
&gt;&gt;&gt; a[18:50]
[18, 19]
&gt;&gt;&gt; a[-8:5]
[]


Also, slicing a list of lists will not copy inner list but only references to them, so it is rather fast:

def get_kernels(pixels, width, radius=1):
    kernels = []
    for y in range(len(pixels)):
        y_min = y - radius
        y_max = y + radius + 1
        for x in range(width):
            x_min = x - radius
            x_max = x + radius + 1
            kernels.append([
                    pixel for row in pixels[y_min:y_max]
                    for pixel in row[x_min:x_max]])
    return kernels


This version is 5 to 10% slower than your first one but has two advantages:


It is more readable;
It is more generic.


I chose to parametrize it with the kernel radius rather than its diameter to avoid the parity check and simplify the computations.

I also chose to keep the width parameter since you seemed to already have it handy. But if you want to remove it, you can still add:

def get_kernels(pixels, radius=1):
    if not pixels:
        return []
    width = len(pixels[0])


at the beginning of the function.

Going further

Your use case of such function seems only to be able to iterate over the returned value. In such case, you would gain more performances by turning this into a generator instead of building a list element by element.

The code is nearly identical, just remove the kernels list and use the yield keyword instead of the append function. And voilà. What it does is that it will compute the kernels on demand, during the iteration; removing the need for extra storage (and handling of that storage):

def get_kernels(pixels, radius=1):
    if not pixels:
        return

    width = len(pixels[0])
    for y in range(len(pixels)):
        y_min = y - radius
        y_max = y + radius + 1
        for x in range(width):
            x_min = x - radius
            x_max = x + radius + 1
            yield [
                pixel for row in pixels[y_min:y_max]
                for pixel in row[x_min:x_max]]




Oh yes, you might have noticed that I removed the self parameter as it is unused. This might interfere with the fact that you are using this as a method on your class. But I probably should explain what is wrong with that in that other question of yours.