Вы, кажется, постоянно смешиваете свои смещения x и y, чего легко избежать, просто фактически вызывая переменные вашего цикла x
и y
всякий раз, когда вы просматриваете данные изображения. Кроме того, данные изображения обычно сохраняются построчно, поэтому ваш внешний цикл должен быть Y-циклом, проходящим по высоте, а внутренний цикл должен обрабатывать координаты X в одной строке и, следовательно, должен проходить по всей ширине.
Кроме того, я не уверен, откуда берутся ваши исходные данные, но в большинстве случаев, которые я видел, когда данные изображения находятся в многомерных массивах, подобных этому, Y на самом деле first Индекс в массиве. Ваша фактическая функция построения изображения также предполагает это, поскольку она использует G.GetLength(0)
для получения высоты изображения. Но функция изменения размера вашего канала не делает; он создает многомерный массив как new int[816, 683]
, который будет изображением 683 * 816, а не 816 * 683, как вы сказали. Так что это, конечно, кажется неправильным.
Поскольку вы подтвердили, что это [x, y], я адаптировал это решение, чтобы использовать его таким образом.
Кроме того, вы жестко закодировали множество значений в своих функциях, что является очень плохой практикой. Если вы знаете, что уменьшите изображение до 1/3, взяв только один из трех пикселей, просто укажите это 3
в качестве параметра.
Код сокращения:
public static Int32[,] ResizeChannel(Int32[,] origChannel, Int32 lossfactor)
{
Int32 newWidth = origChannel.GetLength(0) / lossfactor;
Int32 newHeight = origChannel.GetLength(1) / lossfactor;
// to avoid rounding errors
Int32 origHeight = newHeight * lossfactor;
Int32 origWidth = newWidth *lossfactor;
Int32[,] newChannel = new Int32[newWidth, newHeight];
Int32 newX = 0;
Int32 newY = 0;
for (Int32 y = 1; y < origHeight; y += lossfactor)
{
newX = 0;
for (Int32 x = 1; x < origWidth; x += lossfactor)
{
newChannel[newX, newY] = origChannel[x, y];
newX++;
}
newY++;
}
return newChannel;
}
Фактический код сборки, отмеченный GSerg в комментариях, неверен, поскольку вы не учитываете stride
. stride
- это фактическая длина байта каждой строки пикселей, и это не просто width * BytesPerPixel
, поскольку округляется до следующего кратного 4 байт .
Таким образом, вам нужно инициализировать массив как height * stride
, а не height * width * 3
, и вам нужно пропускать смещение записи до следующего кратного stride
всякий раз, когда вы переходите к нижней строке Y, вместо того, чтобы предполагать он просто попадет туда автоматически, потому что ваша X-обработка добавляет 3 для каждого пикселя. Поскольку он не попадет туда автоматически , если только по стечению обстоятельств ширина вашего изображения окажется кратной 4 пикселям.
Кроме того, если вы используете для этого только один канал, нет оснований указывать ему все три канала. Просто дайте один.
public static Bitmap CreateGreyImage(Int32[,] greyChannel)
{
Int32 width = greyChannel.GetLength(0);
Int32 height = greyChannel.GetLength(1);
Bitmap result = new Bitmap(width, height, PixelFormat.Format24bppRgb);
Rectangle rect = new Rectangle(0, 0, width, height);
BitmapData bmpData = result.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
Int32 stride = bmpData.Stride;
// stride is the actual line width in bytes.
Int32 bytes = stride * height;
Byte[] pixelValues = new Byte[bytes];
Int32 offset = 0;
for (Int32 y = 0; y < height; y++)
{
Int32 workOffset = offset;
for (Int32 x = 0; x < width; x++)
{
pixelValues[workOffset + 0] = (Byte)greyChannel[x, y];
pixelValues[workOffset + 1] = (Byte)greyChannel[x, y];
pixelValues[workOffset + 2] = (Byte)greyChannel[x, y];
workOffset += 3;
}
// Add stride to get the start offset of the next line
offset += stride;
}
Marshal.Copy(pixelValues, 0, bmpData.Scan0, bytes);
result.UnlockBits(bmpData);
return result;
}
Теперь это работает, как и ожидалось , если ваши каналы R, G и B действительно идентичны , но если это не так, вы должны понимать, что есть разница между уменьшением изображения до оттенков серого и простым построением серое изображение из зеленого канала. На цветном изображении вы получите совершенно другие результаты, если вместо этого выберете синий или красный канал.
Это код, который я выполнил для этого:
Int32[,] greyar = ResizeChannel(greenar, 3);
Bitmap newbm = CreateGreyImage(greyar);