I'm trying to implement image transformation Bayern Pattern -> RGB
I uploaded this image:
Here is a snippet of my code:
import cv2
import numpy as np
from matplotlib import pyplot as plt
import math
import os
PATH = 'data/'
file_path = os.path.join(PATH, 'oldwell_mosaic.png')
img = cv2.imread(file_path, 0)
w, h = img.shape
new_image = np.zeros((w, h, 3), dtype = np.uint8)
# Green
new_image[1::2,1::2,1] = img[1::2,1::2]
new_image[::2, ::2, 1] = img[::2, ::2]
# Blue
new_image[1::2,::2,0] = img[1::2,::2]
# Red
new_image[::2, 1::2, 2] = img[::2, 1::2]
In the above example I use this pattern:
I am supposed to interpolate the missing values but the places with 'no values' are actually filled with zeros. When I perform this:
kernel = np.ones((4,4), np.float)/16
new_image[:,:,2] = cv2.filter2D(new_image[:, :, 2], -1, kernel)
I don't get the expected result as I'm using zeros in interpolation.
I am not using python nor opencv but was curious so here low level C++ approach for this:
- process image by bands separately
for each pixel you need intensity accumulator acc[][]
and counter cnt[][]
the need for counter can be avoided. You can use predefined counts encoded in similar manner then the mask
in my code but this mean handling special cases on edges and corners of image. I chose acc,cnt
for simplicity (they fit into 32bit easily so there is no point to not using it).
The division can be done with bit-shift for speed (except on edges and corners)
clear acc,cnt
with zero
process each pixel containing processed band
add the band intensity to acc
and increment cnt
to pixel position and also to all neighbors not containing this band.
after whole image processed compute pixel band value
simply by
pixel[y][x].band = acc[y][x]/cnt[y][x]
By this you can use the same buffer for acc,cnt
for next band.
My C++ implementation:
picture pic0,pic1,pic2; // pic0 - original input image,pic1 output, pic2 temp band interpolation
int x,y,a,b,i,j;
const int mask[3][6][6]= // bayern mask for eac band 3 bands and common size of 2x2 and 3x3 is 6x6
{
// blue
{
{0,0,0,0,0,0},
{0,1,0,1,0,1},
{0,0,0,0,0,0},
{0,1,0,1,0,1},
{0,0,0,0,0,0},
{0,1,0,1,0,1},
},
// green
{
{0,1,0,1,0,1},
{1,0,1,0,1,0},
{0,1,0,1,0,1},
{1,0,1,0,1,0},
{0,1,0,1,0,1},
{1,0,1,0,1,0},
},
// red
{
{1,0,1,0,1,0},
{0,0,0,0,0,0},
{1,0,1,0,1,0},
{0,0,0,0,0,0},
{1,0,1,0,1,0},
{0,0,0,0,0,0},
},
};
// prepare buffers
pic1.resize(pic0.xs ,pic0.ys ); pic1.pf=_pf_rgba; pic1.clear(0);
pic2.resize(pic0.xs+2,pic0.ys+2); pic2.pf=_pf_uu; // size enlarged to avoid edge conditions statements
// process bands
for (b=0;b<3;b++)
{
pic2.clear(0); // clear acc,cnt
for (j=0,y=0;y<pic0.ys;y++,(j==5)?j=0:j++)
for (i=0,x=0;x<pic0.xs;x++,(i==5)?i=0:i++)
if (mask[b][j][i]) // process only band b pixels
{
a=pic0.p[y][x].db[0]; // grayscale intensity
// add to 4 neighbors
pic2.p[y+0][x+1].dw[0]+=a; pic2.p[y+0][x+1].dw[1]++;
pic2.p[y+1][x+0].dw[0]+=a; pic2.p[y+1][x+0].dw[1]++;
pic2.p[y+1][x+1].dw[0]+=a; pic2.p[y+1][x+1].dw[1]++;
pic2.p[y+1][x+2].dw[0]+=a; pic2.p[y+1][x+2].dw[1]++;
pic2.p[y+2][x+1].dw[0]+=a; pic2.p[y+2][x+1].dw[1]++;
if (b==picture::_g) continue;
// add to 8 neighbors (for r,b bands)
pic2.p[y+0][x+0].dw[0]+=a; pic2.p[y+0][x+0].dw[1]++;
pic2.p[y+0][x+2].dw[0]+=a; pic2.p[y+0][x+2].dw[1]++;
pic2.p[y+2][x+0].dw[0]+=a; pic2.p[y+2][x+0].dw[1]++;
pic2.p[y+2][x+2].dw[0]+=a; pic2.p[y+2][x+2].dw[1]++;
}
for (y=0;y<pic1.ys;y++) // convert to color band
for (x=0;x<pic1.xs;x++)
pic1.p[y][x].db[b]=pic2.p[y+1][x+1].dw[0]/pic2.p[y+1][x+1].dw[1];
}
I use my own picture class for images so some members are:
xs,ys
is size of image in pixels
p[y][x].dd
is pixel at (x,y)
position as 32 bit integer type
clear(color)
clears entire image with color
resize(xs,ys)
resizes image to new resolution
bmp
is VCL encapsulated GDI Bitmap with Canvas
access
pf
holds actual pixel format of the image:
enum _pixel_format_enum
{
_pf_none=0, // undefined
_pf_rgba, // 32 bit RGBA
_pf_s, // 32 bit signed int
_pf_u, // 32 bit unsigned int
_pf_ss, // 2x16 bit signed int
_pf_uu, // 2x16 bit unsigned int
_pixel_format_enum_end
};
color
and pixels are encoded like this:
union color
{
DWORD dd; WORD dw[2]; byte db[4];
int i; short int ii[2];
color(){}; color(color& a){ *this=a; }; ~color(){}; color* operator = (const color *a) { dd=a->dd; return this; }; /*color* operator = (const color &a) { ...copy... return this; };*/
};
The bands are:
enum{
_x=0, // dw
_y=1,
_b=0, // db
_g=1,
_r=2,
_a=3,
_v=0, // db
_s=1,
_h=2,
};
The bayern mask has 1
for pixels that has the color band included and item mask[band][j=0][i=0]
represents pixel[y=0][x=0]
. Finally here the result: for your input image: