|
@ -16,75 +16,257 @@ function xc = bayer2rgb(xb, M, N, method) |
|
|
gridPointsStepLengthY = (bayerPatternDimY - 1) / (M - 1); |
|
|
gridPointsStepLengthY = (bayerPatternDimY - 1) / (M - 1); |
|
|
gridPointsStepLengthX = (bayerPatternDimX - 1) / (N - 1); |
|
|
gridPointsStepLengthX = (bayerPatternDimX - 1) / (N - 1); |
|
|
% Calculates the coordinates of the grid points for both axes |
|
|
% Calculates the coordinates of the grid points for both axes |
|
|
gridPointsCoordinatesY = 1:gridPointsStepLengthY:bayerPatternDimY; |
|
|
gridPointsCoordinatesY = (1:gridPointsStepLengthY:bayerPatternDimY)'; |
|
|
gridPointsCoordinatesX = 1:gridPointsStepLengthX:bayerPatternDimX; |
|
|
gridPointsCoordinatesX = (1:gridPointsStepLengthX:bayerPatternDimX)'; |
|
|
|
|
|
|
|
|
if (strcmp(method, 'nearest')) |
|
|
if (strcmp(method, 'nearest')) |
|
|
|
|
|
% Determines the indeces of the even rows of the original image |
|
|
|
|
|
% that are closest to each ordinate of the new image grid |
|
|
|
|
|
gridPointYmod2 = mod(gridPointsCoordinatesY, 2) >= 1; |
|
|
|
|
|
nearestEvenRow = floor(gridPointsCoordinatesY); |
|
|
|
|
|
nearestEvenRow(gridPointYmod2) = nearestEvenRow(gridPointYmod2)+1; |
|
|
|
|
|
nearestEvenRow(nearestEvenRow > bayerPatternDimY) = ... |
|
|
|
|
|
nearestEvenRow(nearestEvenRow > bayerPatternDimY) - 2; |
|
|
|
|
|
|
|
|
|
|
|
% Determines the indeces of the odd rows of the original image that |
|
|
|
|
|
% are closest to each ordinate of the new image grid |
|
|
|
|
|
nearestOddRow = floor(gridPointsCoordinatesY); |
|
|
|
|
|
nearestOddRow(~gridPointYmod2) = nearestOddRow(~gridPointYmod2)+1; |
|
|
|
|
|
nearestOddRow(nearestOddRow > bayerPatternDimY) = ... |
|
|
|
|
|
nearestOddRow(nearestOddRow > bayerPatternDimY) - 2; |
|
|
|
|
|
|
|
|
|
|
|
% Determines the indeces of the even columns of the original image |
|
|
|
|
|
% that are closest to each abscissa of the new image grid |
|
|
|
|
|
gridPointXmod2 = mod(gridPointsCoordinatesX, 2) >= 1; |
|
|
|
|
|
nearestEvenCol = floor(gridPointsCoordinatesX); |
|
|
|
|
|
nearestEvenCol(gridPointXmod2) = nearestEvenCol(gridPointXmod2)+1; |
|
|
|
|
|
nearestEvenCol(nearestEvenCol > bayerPatternDimX) = ... |
|
|
|
|
|
nearestEvenCol(nearestEvenCol > bayerPatternDimX) - 2; |
|
|
|
|
|
|
|
|
|
|
|
% Determines the indeces of the odd columns of the original image |
|
|
|
|
|
% that are closest to each abscissa of the new image grid |
|
|
|
|
|
nearestOddCol = floor(gridPointsCoordinatesX); |
|
|
|
|
|
nearestOddCol(~gridPointXmod2) = nearestOddCol(~gridPointXmod2)+1; |
|
|
|
|
|
nearestOddCol(nearestOddCol > bayerPatternDimX) = ... |
|
|
|
|
|
nearestOddCol(nearestOddCol > bayerPatternDimX) - 2; |
|
|
|
|
|
|
|
|
|
|
|
% Determines the indeces of the rows (even or odd) of the original |
|
|
|
|
|
% image that are closest to each ordinate of the new image grid |
|
|
|
|
|
totalNearestRow = round(gridPointsCoordinatesY); |
|
|
|
|
|
|
|
|
|
|
|
% Determines the indeces of the columns (even or odd) of the |
|
|
|
|
|
% original image that are closest to each abscissa of the new image |
|
|
|
|
|
totalNearestCol = round(gridPointsCoordinatesX); |
|
|
|
|
|
|
|
|
|
|
|
% Closest neighbors for red and blue can be determined by observing |
|
|
|
|
|
% the Bayer pattern |
|
|
|
|
|
xc.red = xb(nearestOddRow, nearestEvenCol); |
|
|
|
|
|
xc.blue = xb(nearestEvenRow, nearestOddCol); |
|
|
|
|
|
|
|
|
for currentRow = 1:M |
|
|
for currentRow = 1:M |
|
|
% Determines the index of the even row of the original image |
|
|
for currentCol = 1:N |
|
|
% that is closest to this ordinate (currentRow) of the new |
|
|
if ((mod(totalNearestRow(currentRow), 2) == 0 && ... |
|
|
% image |
|
|
mod(totalNearestCol(currentCol), 2)) ~= 0 || ... |
|
|
idx = mod(gridPointsCoordinatesY(currentRow), 2) >= 1; |
|
|
(mod(totalNearestRow(currentRow), 2) ~= 0 && ... |
|
|
nearestEvenRow = floor(gridPointsCoordinatesY(currentRow)); |
|
|
mod(totalNearestCol(currentCol), 2) == 0)) |
|
|
nearestEvenRow(idx) = nearestEvenRow(idx)+1; |
|
|
% This point of the original image doesn't contain |
|
|
if (nearestEvenRow > bayerPatternDimY) |
|
|
% information about the green colour |
|
|
nearestEvenRow = nearestEvenRow - 2; |
|
|
|
|
|
|
|
|
% Calculates the vertical displacement of the two |
|
|
|
|
|
% diagonal lines that cross this point |
|
|
|
|
|
b1 = totalNearestRow(currentRow) - ... |
|
|
|
|
|
totalNearestCol(currentCol); |
|
|
|
|
|
b2 = totalNearestRow(currentRow) + ... |
|
|
|
|
|
totalNearestCol(currentCol); |
|
|
|
|
|
|
|
|
|
|
|
% Calculates the ordinate of the two diagonals at the |
|
|
|
|
|
% abscissa of the current point (currentCol) |
|
|
|
|
|
y1 = gridPointsCoordinatesX(currentCol) + b1; |
|
|
|
|
|
y2 = -gridPointsCoordinatesX(currentCol) + b2; |
|
|
|
|
|
|
|
|
|
|
|
% Initializes temporary coordinates of the point which |
|
|
|
|
|
% will be selected as the closest one |
|
|
|
|
|
fixedNearestRow = totalNearestRow(currentRow); |
|
|
|
|
|
fixedNearestCol = totalNearestCol(currentCol); |
|
|
|
|
|
|
|
|
|
|
|
% Checks the relative position between the point and |
|
|
|
|
|
% the two diagonals |
|
|
|
|
|
if (gridPointsCoordinatesY(currentRow) >= y1 && ... |
|
|
|
|
|
gridPointsCoordinatesY(currentRow) >= y2) |
|
|
|
|
|
fixedNearestRow = totalNearestRow(currentRow) - 1; |
|
|
|
|
|
elseif (gridPointsCoordinatesY(currentRow) < y1 && ... |
|
|
|
|
|
gridPointsCoordinatesY(currentRow) < y2) |
|
|
|
|
|
fixedNearestRow = totalNearestRow(currentRow) + 1; |
|
|
|
|
|
elseif (gridPointsCoordinatesY(currentRow) >= y1 && ... |
|
|
|
|
|
gridPointsCoordinatesY(currentRow) < y2) |
|
|
|
|
|
fixedNearestCol = totalNearestCol(currentCol) - 1; |
|
|
|
|
|
elseif (gridPointsCoordinatesY(currentRow) < y1 && ... |
|
|
|
|
|
gridPointsCoordinatesY(currentRow) >= y2) |
|
|
|
|
|
fixedNearestCol = totalNearestCol(currentCol) + 1; |
|
|
end |
|
|
end |
|
|
|
|
|
|
|
|
% Determines the index of the odd row of the original image |
|
|
if (fixedNearestRow < 1) |
|
|
% that is closest to this ordinate (currentRow) of the new |
|
|
% Fix for the marginal first row case |
|
|
% image |
|
|
xc.green(currentRow, currentCol) = ... |
|
|
idx = mod(gridPointsCoordinatesY(currentRow), 2) < 1; |
|
|
xb(1, fixedNearestCol - 1); |
|
|
nearestOddRow = floor(gridPointsCoordinatesY(currentRow)); |
|
|
else |
|
|
nearestOddRow(idx) = nearestOddRow(idx)+1; |
|
|
xc.green(currentRow, currentCol) = ... |
|
|
if (nearestOddRow > bayerPatternDimY) |
|
|
xb(fixedNearestRow, fixedNearestCol); |
|
|
nearestOddRow = nearestOddRow - 2; |
|
|
end |
|
|
|
|
|
else |
|
|
|
|
|
xc.green(currentRow, currentCol) = ... |
|
|
|
|
|
xb(totalNearestRow(currentRow), ... |
|
|
|
|
|
totalNearestCol(currentCol)); |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
end |
|
|
end |
|
|
|
|
|
elseif (strcmp(method, 'linear')) |
|
|
|
|
|
% Calculates helper variables |
|
|
|
|
|
flooredCoordinatesY = floor(gridPointsCoordinatesY); |
|
|
|
|
|
flooredCoordinatesX = floor(gridPointsCoordinatesX); |
|
|
|
|
|
|
|
|
|
|
|
% Calculates the ordinate of the upper couple of nearest points |
|
|
|
|
|
% that will be used in the interpolation for the blue colour |
|
|
|
|
|
upperBlueY = flooredCoordinatesY; |
|
|
|
|
|
upperBlueY(mod(upperBlueY, 2) ~= 0) = ... |
|
|
|
|
|
upperBlueY(mod(upperBlueY, 2) ~= 0) - 1; |
|
|
|
|
|
upperBlueY = upperBlueY + 2; |
|
|
|
|
|
|
|
|
|
|
|
% Calculates the ordinate of the upper couple of nearest points |
|
|
|
|
|
% that will be used in the interpolation for the red colour |
|
|
|
|
|
upperRedY = flooredCoordinatesY; |
|
|
|
|
|
upperRedY(mod(upperRedY, 2) == 0) = ... |
|
|
|
|
|
upperRedY(mod(upperRedY, 2) == 0) - 1; |
|
|
|
|
|
upperRedY = upperRedY + 2; |
|
|
|
|
|
|
|
|
|
|
|
% Calculates the abscissa of the left couple of nearest points that |
|
|
|
|
|
% will be used in the interpolation for the blue colour |
|
|
|
|
|
leftBlueX = flooredCoordinatesX; |
|
|
|
|
|
leftBlueX(mod(leftBlueX, 2) == 0) = ... |
|
|
|
|
|
leftBlueX(mod(leftBlueX, 2) == 0) - 1; |
|
|
|
|
|
leftBlueX = leftBlueX + 2; |
|
|
|
|
|
|
|
|
|
|
|
% Calculates the abscissa of the left couple of nearest points that |
|
|
|
|
|
% will be used in the interpolation for the red colour |
|
|
|
|
|
leftRedX = flooredCoordinatesX; |
|
|
|
|
|
leftRedX(mod(leftRedX, 2) ~= 0) = ... |
|
|
|
|
|
leftRedX(mod(leftRedX, 2) ~= 0) - 1; |
|
|
|
|
|
leftRedX = leftRedX + 2; |
|
|
|
|
|
|
|
|
|
|
|
% Determines the indeces of the rows (even or odd) of the original |
|
|
|
|
|
% image that are closest to each ordinate of the new image grid |
|
|
|
|
|
centerPointRow = round(gridPointsCoordinatesY); |
|
|
|
|
|
|
|
|
|
|
|
% Determines the indeces of the columns (even or odd) of the |
|
|
|
|
|
% original image that are closest to each abscissa of the new image |
|
|
|
|
|
centerPointCol = round(gridPointsCoordinatesX); |
|
|
|
|
|
|
|
|
|
|
|
% Adds a padding to the original image, replicating the last two |
|
|
|
|
|
% rows and columns of each egde |
|
|
|
|
|
xbPadded = [xb(:, end - 1) xb(:, end - 2) xb xb(:, end - 2) ... |
|
|
|
|
|
xb(:, end - 1)]; |
|
|
|
|
|
xbPadded = [xbPadded(1, :); xbPadded(2, :); xbPadded; ... |
|
|
|
|
|
xbPadded(end - 2, :); xbPadded(end - 1, :)]; |
|
|
|
|
|
|
|
|
|
|
|
for currentRow = 1:M |
|
|
|
|
|
lowerInterpPointBlue = ... |
|
|
|
|
|
((leftBlueX + 2 - gridPointsCoordinatesX) / 2)' .* ... |
|
|
|
|
|
xbPadded(upperBlueY(currentRow) + 2, leftBlueX) + ... |
|
|
|
|
|
((gridPointsCoordinatesX - leftBlueX) / 2)' .* ... |
|
|
|
|
|
xbPadded(upperBlueY(currentRow) + 2, leftBlueX + 2); |
|
|
|
|
|
|
|
|
|
|
|
upperInterpPointBlue = ... |
|
|
|
|
|
((leftBlueX + 2 - gridPointsCoordinatesX) / 2)' .* ... |
|
|
|
|
|
xbPadded(upperBlueY(currentRow), leftBlueX) + ... |
|
|
|
|
|
((gridPointsCoordinatesX - leftBlueX) / 2)' .* ... |
|
|
|
|
|
xbPadded(upperBlueY(currentRow), leftBlueX + 2); |
|
|
|
|
|
|
|
|
|
|
|
xc.blue(currentRow, :) = ... |
|
|
|
|
|
((upperBlueY(currentRow) - ... |
|
|
|
|
|
gridPointsCoordinatesY(currentRow)) / 2) * ... |
|
|
|
|
|
lowerInterpPointBlue + ... |
|
|
|
|
|
((gridPointsCoordinatesY(currentRow) - ... |
|
|
|
|
|
upperBlueY(currentRow) + 2) / 2) * ... |
|
|
|
|
|
upperInterpPointBlue; |
|
|
|
|
|
|
|
|
|
|
|
lowerInterpPointRed = ... |
|
|
|
|
|
((leftRedX + 2 - gridPointsCoordinatesX) / 2)' .* ... |
|
|
|
|
|
xbPadded(upperRedY(currentRow) + 2, leftRedX) + ... |
|
|
|
|
|
((gridPointsCoordinatesX - leftRedX) / 2)' .* ... |
|
|
|
|
|
xbPadded(upperRedY(currentRow) + 2, leftRedX + 2); |
|
|
|
|
|
|
|
|
|
|
|
upperInterpPointRed = ... |
|
|
|
|
|
((leftRedX + 2 - gridPointsCoordinatesX) / 2)' .* ... |
|
|
|
|
|
xbPadded(upperRedY(currentRow), leftRedX) + ... |
|
|
|
|
|
((gridPointsCoordinatesX - leftRedX) / 2)' .* ... |
|
|
|
|
|
xbPadded(upperRedY(currentRow), leftRedX + 2); |
|
|
|
|
|
|
|
|
% Determines the index of the row (even or odd) of the original |
|
|
xc.red(currentRow, :) = ... |
|
|
% image that is closest to this ordinate (currentRow) of the |
|
|
((upperRedY(currentRow) - gridPointsCoordinatesY(currentRow)) / 2) * ... |
|
|
% new image |
|
|
lowerInterpPointRed + ... |
|
|
totalNearestRow = round(gridPointsCoordinatesY(currentRow)); |
|
|
((gridPointsCoordinatesY(currentRow) - upperRedY(currentRow) + 2) / 2) * ... |
|
|
|
|
|
upperInterpPointRed; |
|
|
|
|
|
|
|
|
for currentCol = 1:N |
|
|
for currentCol = 1:N |
|
|
% Determines the index of the even column of the original |
|
|
if ((mod(centerPointRow(currentRow), 2) == 0 && ... |
|
|
% image that is closest to this abscissa (currentCol) of |
|
|
mod(centerPointCol(currentCol), 2)) == 0 || ... |
|
|
% the new image |
|
|
(mod(centerPointRow(currentRow), 2) ~= 0 && ... |
|
|
idx = mod(gridPointsCoordinatesX(currentCol), 2) >= 1; |
|
|
mod(centerPointCol(currentCol), 2) ~= 0)) |
|
|
nearestEvenCol = floor(gridPointsCoordinatesX(currentCol)); |
|
|
% This point of the original image DOES contain |
|
|
nearestEvenCol(idx) = nearestEvenCol(idx)+1; |
|
|
% information about the green colour |
|
|
if (nearestEvenCol > bayerPatternDimX) |
|
|
|
|
|
nearestEvenCol = nearestEvenCol - 2; |
|
|
% Calculates the vertical displacement of the two |
|
|
end |
|
|
% diagonal lines that cross this point |
|
|
|
|
|
b1 = centerPointRow(currentRow) - ... |
|
|
|
|
|
centerPointCol(currentCol); |
|
|
|
|
|
b2 = centerPointRow(currentRow) + ... |
|
|
|
|
|
centerPointCol(currentCol); |
|
|
|
|
|
|
|
|
|
|
|
% Calculates the ordinate of the two diagonals at the |
|
|
|
|
|
% abscissa of the current point (currentCol) |
|
|
|
|
|
y1 = gridPointsCoordinatesX(currentCol) + b1; |
|
|
|
|
|
y2 = -gridPointsCoordinatesX(currentCol) + b2; |
|
|
|
|
|
|
|
|
|
|
|
% Initializes temporary coordinates of the point which |
|
|
|
|
|
% will be selected as the center |
|
|
|
|
|
fixedCenterPointRow = centerPointRow(currentRow); |
|
|
|
|
|
fixedCenterPointCol = centerPointCol(currentCol); |
|
|
|
|
|
|
|
|
% Determines the index of the even column of the original |
|
|
% Checks the relative position between the point and |
|
|
% image that is closest to this abscissa (currentCol) of |
|
|
% the two diagonals |
|
|
% the new image |
|
|
if (gridPointsCoordinatesY(currentRow) >= y1 && ... |
|
|
idx = mod(gridPointsCoordinatesX(currentCol), 2) < 1; |
|
|
gridPointsCoordinatesY(currentRow) >= y2) |
|
|
nearestOddCol = floor(gridPointsCoordinatesX(currentCol)); |
|
|
fixedCenterPointRow = centerPointRow(currentRow) - 1; |
|
|
nearestOddCol(idx) = nearestOddCol(idx)+1; |
|
|
elseif (gridPointsCoordinatesY(currentRow) < y1 && ... |
|
|
if (nearestOddCol > bayerPatternDimX) |
|
|
gridPointsCoordinatesY(currentRow) < y2) |
|
|
nearestOddCol = nearestOddCol - 2; |
|
|
fixedCenterPointRow = centerPointRow(currentRow) + 1; |
|
|
|
|
|
elseif (gridPointsCoordinatesY(currentRow) >= y1 && ... |
|
|
|
|
|
gridPointsCoordinatesY(currentRow) < y2) |
|
|
|
|
|
fixedCenterPointCol = centerPointCol(currentCol) - 1; |
|
|
|
|
|
elseif (gridPointsCoordinatesY(currentRow) < y1 && ... |
|
|
|
|
|
gridPointsCoordinatesY(currentRow) >= y2) |
|
|
|
|
|
fixedCenterPointCol = centerPointCol(currentCol) + 1; |
|
|
end |
|
|
end |
|
|
|
|
|
|
|
|
% Determines the index of the column (even or odd) of the |
|
|
if (fixedCenterPointRow < 1) |
|
|
% original image that is closest to this abscissa |
|
|
% Fix for the marginal first row case |
|
|
% (currentCol) of the new image |
|
|
fixedCenterPointRow = 1; |
|
|
totalNearestCol = round(gridPointsCoordinatesX(currentCol)); |
|
|
fixedCenterPointCol = fixedCenterPointCol - 1; |
|
|
if (mod(totalNearestRow, 2) == 0 && mod(totalNearestCol, 2) ~= 0) |
|
|
|
|
|
totalNearestCol = totalNearestCol + 1; |
|
|
|
|
|
elseif (mod(totalNearestRow, 2) ~= 0 && mod(totalNearestCol, 2) == 0) |
|
|
|
|
|
totalNearestCol = totalNearestCol - 1; |
|
|
|
|
|
end |
|
|
end |
|
|
|
|
|
|
|
|
% Closest neighbors per colour can be determined by |
|
|
xc.green(currentRow, currentCol) = ... |
|
|
% observing the Bayer pattern |
|
|
tiltedInterp(gridPointsCoordinatesX(currentCol), ... |
|
|
xc.red(currentRow, currentCol) = xb(nearestOddRow, nearestEvenCol); |
|
|
gridPointsCoordinatesY(currentRow), ... |
|
|
xc.blue(currentRow, currentCol) = xb(nearestEvenRow, nearestOddCol); |
|
|
fixedCenterPointCol, fixedCenterPointRow, xbPadded); |
|
|
xc.green(currentRow, currentCol) = xb(totalNearestRow, totalNearestCol); |
|
|
else |
|
|
|
|
|
xc.green(currentRow, currentCol) = ... |
|
|
|
|
|
tiltedInterp(gridPointsCoordinatesX(currentCol), ... |
|
|
|
|
|
gridPointsCoordinatesY(currentRow), ... |
|
|
|
|
|
centerPointCol(currentCol), ... |
|
|
|
|
|
centerPointRow(currentRow), xbPadded); |
|
|
|
|
|
end |
|
|
end |
|
|
end |
|
|
end |
|
|
end |
|
|
elseif (strcmp(method, 'linear')) |
|
|
|
|
|
end |
|
|
end |
|
|
|
|
|
|
|
|
% Combines colours to a single image array and shows the result |
|
|
% Combines colours to a single image array and shows the result |
|
@ -92,3 +274,52 @@ function xc = bayer2rgb(xb, M, N, method) |
|
|
figure(); |
|
|
figure(); |
|
|
imshow(rgbImage); |
|
|
imshow(rgbImage); |
|
|
end |
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function value = tiltedInterp(xw, yw, xc, yc, xbPadded) |
|
|
|
|
|
% Calculates the vertical displacement of the main diagonal that |
|
|
|
|
|
% crosses this point |
|
|
|
|
|
bw = yw - xw; |
|
|
|
|
|
|
|
|
|
|
|
% Calculates the abscissas of the points of intersection |
|
|
|
|
|
x1 = (yc + xc - 1 - bw) / 2; |
|
|
|
|
|
%x2 = (yc + xc + 1 - bw) / 2; |
|
|
|
|
|
|
|
|
|
|
|
% Calculates perpendicular distance of point w to the line connecting |
|
|
|
|
|
% the lower point and the left point |
|
|
|
|
|
dist = sqrt((x1 - xw) ^ 2 + (x1 + bw - yw) ^ 2); |
|
|
|
|
|
|
|
|
|
|
|
% Calculates the lengths of the upper parts of the lines connecting the |
|
|
|
|
|
% points |
|
|
|
|
|
h1 = sqrt(((xc - 1 - xw) ^ 2 + (yc - yw) ^ 2) - dist ^ 2); |
|
|
|
|
|
|
|
|
|
|
|
lowerLeftInterpPoint = ... |
|
|
|
|
|
((sqrt(2) - h1) / sqrt(2)) * ... |
|
|
|
|
|
xbPadded(yc + 2, xc - 1 + 2) + ... |
|
|
|
|
|
(h1 / sqrt(2)) * ... |
|
|
|
|
|
xbPadded(yc + 1 + 2, xc + 2); |
|
|
|
|
|
|
|
|
|
|
|
upperRightInterpPoint = ... |
|
|
|
|
|
((sqrt(2) - h1) / sqrt(2)) * ... |
|
|
|
|
|
xbPadded(yc - 1 + 2, xc + 2) + ... |
|
|
|
|
|
(h1 / sqrt(2)) * ... |
|
|
|
|
|
xbPadded(yc + 2, xc + 1 + 2); |
|
|
|
|
|
|
|
|
|
|
|
value = ... |
|
|
|
|
|
(sqrt(2) - dist) / sqrt(2) * lowerLeftInterpPoint + ... |
|
|
|
|
|
dist / sqrt(2) * upperRightInterpPoint; |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|