Форум программистов, компьютерный форум, киберфорум
C# Windows Forms
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.63/35: Рейтинг темы: голосов - 35, средняя оценка - 4.63
311 / 271 / 118
Регистрация: 05.06.2013
Сообщений: 868

Аффинные преобразования изображения

20.04.2015, 14:41. Показов 7035. Ответов 9
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день.

Написал небольшое приложение, которое осуществляет поворот нарисованного объекта методом Оуэна и Македона, т.е. тремя сдвигами(алгоритм тут, стр 11-12).
Код:
Кликните здесь для просмотра всего текста

Проект: App.rar (Нажимая по разу поочередно на кнопки 1, 2, 3 получаем поворот изображения на 30 °)
Код:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
 
namespace PaintedObjectsMoving {
    public partial class MainForm : Form {
        List<PaintedObject> list;
        PaintedObject currObj;
        PaintedObject po;
        Bitmap bmp;
        int v1, v2;
        
        public MainForm() {
            InitializeComponent();
            list = new List<PaintedObject>();
            bmp = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
            RefreshBitmap();
            this.DoubleBuffered = true;
 
            this.Paint += Form1_Paint;
 
            btn_Draw_Click(null, null);
        }
 
        void Form1_Paint(object sender, PaintEventArgs e) {
            if (bmp == null) return;
            RefreshBitmap();
            e.Graphics.DrawImage(bmp, 0, 0);
        }
 
        void RefreshBitmap() {
            if (bmp != null) bmp.Dispose();
            bmp = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
            using (Graphics g = Graphics.FromImage(bmp)) {
                foreach (PaintedObject po in list) {
                    g.DrawPath(po.Pen, po.Path);
                }
            }
        }
 
        private void btn_Draw_Click(object sender, EventArgs e)
        {
            Random rnd = new Random(DateTime.Now.Millisecond);
            int w = this.ClientSize.Width,
                h = this.ClientSize.Height;
            
            v1 = 100; v2 = 60;
            po = new PaintedObject(new Pen(Color.FromArgb(rnd.Next(255), rnd.Next(255), rnd.Next(255)), 2), new GraphicsPath());
 
            v1 =  50; v2 = 30; po.Path.AddRectangle(Rectangle.FromLTRB(w / 2 - v1 / 2, h / 2 - v2 / 2, w / 2 + v1 / 2, h / 2 + v2 / 2));
            v1 =  80; v2 = 50; po.Path.AddRectangle(Rectangle.FromLTRB(w / 2 - v1 / 2, h / 2 - v2 / 2, w / 2 + v1 / 2, h / 2 + v2 / 2));
            v1 = 100; v2 = 60; po.Path.AddRectangle(Rectangle.FromLTRB(w / 2 - v1 / 2, h / 2 - v2 / 2, w / 2 + v1 / 2, h / 2 + v2 / 2));
            v1 = 120; v2 = 70; po.Path.AddRectangle(Rectangle.FromLTRB(w / 2 - v1 / 2, h / 2 - v2 / 2, w / 2 + v1 / 2, h / 2 + v2 / 2));
            v1 = 150; v2 = 90; po.Path.AddRectangle(Rectangle.FromLTRB(w / 2 - v1 / 2, h / 2 - v2 / 2, w / 2 + v1 / 2, h / 2 + v2 / 2));
 
            list.Add(po);
            
            currObj = po;
 
            this.Refresh();
            return;
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            float x1 = currObj.Path.GetBounds().Location.X;
            float y1 = currObj.Path.GetBounds().Location.Y;
 
            Matrix mat = new Matrix(1, 0, -(float)Math.Tan(Math.PI / 12), 1, 0, 0);
            currObj.Path.Transform(mat);
 
            float x2 = currObj.Path.GetBounds().Location.X;
            float y2 = currObj.Path.GetBounds().Location.Y;
 
            currObj.Path.Transform(new Matrix(1, 0, 0, 1, x1 - x2, y1 - y2));
            this.Refresh();
        }
 
        private void button2_Click(object sender, EventArgs e)
        {
            float x1 = currObj.Path.GetBounds().Location.X;
            float y1 = currObj.Path.GetBounds().Location.Y;
 
            Matrix mat = new Matrix(1, (float)Math.Sin(Math.PI / 6), 0, 1, 0, 0);
            currObj.Path.Transform(mat);
 
            float x2 = currObj.Path.GetBounds().Location.X;
            float y2 = currObj.Path.GetBounds().Location.Y;
 
            currObj.Path.Transform(new Matrix(1, 0, 0, 1, x1 - x2, y1 - y2));
            this.Refresh();
        }
 
        private void button3_Click(object sender, EventArgs e)
        {
            float x1 = currObj.Path.GetBounds().Location.X;
            float y1 = currObj.Path.GetBounds().Location.Y;
 
            Matrix mat = new Matrix(1, 0, -(float)Math.Tan(Math.PI / 12), 1, 0, 0);
            currObj.Path.Transform(mat);
 
            float x2 = currObj.Path.GetBounds().Location.X;
            float y2 = currObj.Path.GetBounds().Location.Y;
 
            currObj.Path.Transform(new Matrix(1, 0, 0, 1, x1 - x2, y1 - y2));
            this.Refresh();
        }
    }
}

Класс:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
 
namespace PaintedObjectsMoving {
    class PaintedObject : ICloneable {
        private GraphicsPath path;
 
        public GraphicsPath Path {
            get { return path; }
            set { path = value; }
        }
        private Pen pen;
 
        public Pen @Pen {
            get { return pen; }
            set { pen = value; }
        }
        public PaintedObject(Pen pen, GraphicsPath path) {
            this.path = path;
            this.pen = pen;
        }
 
        #region ICloneable Members
 
        public object Clone() {
            return new PaintedObject(this.Pen, this.Path.Clone() as GraphicsPath);
        }
 
        #endregion
    }
}



Для нарисованного примитивами объекта проблем никаких нет, все работает правильно.
Однако возникла необходимость сделать то же самое для растрового изображения, и пока я в тупике.

Не выходит правильно переписать класс нарисованных объектов, что бы можно было в нем использовать растровые изображения из файла, вместо нарисованных примитивов.
Нужна ваша помощь в этом вопросе, наставьте на путь истинный.
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
20.04.2015, 14:41
Ответы с готовыми решениями:

Аффинные преобразования
Преподаватель сказал переделать лабу, чтобы можно было не отрисованный в программе объект(прямоугольник) можно было поворачивать, смещать и...

Аффинные преобразования
помогите пожалуйста с задачкой:help: отражение получилось, остальное не выходит :umbrage: Для треугольник ABC с координатами вершин: A...

Аффинные преобразования в с++
помогите кому не лень,лабораторку сделать. суть такая:на форме в системе координат по задаваемым четырем точкам должен построиться...

9
311 / 271 / 118
Регистрация: 05.06.2013
Сообщений: 868
20.04.2015, 19:02  [ТС]
ап
0
 Аватар для ViterAlex
8951 / 4863 / 1886
Регистрация: 11.02.2013
Сообщений: 10,246
20.04.2015, 22:19
посмотри тут
1
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
22.04.2015, 09:52
Цитата Сообщение от Jefe Посмотреть сообщение
Написал небольшое приложение, которое осуществляет поворот нарисованного объекта методом Оуэна и Македона
Нет у вас там никакой реализации метода Оуэна и Македона. Вы поворачиваете векторную графику (которую можно было просто повернуть и за один раз, без всяких Македонов), а этот метод разработан для растровых изображений. Там же написано "сдвиг строк и столбцов изображения". Где у вас сдвиг строк и столбцов? Вы же просто вектора на матрицы множите.
0
311 / 271 / 118
Регистрация: 05.06.2013
Сообщений: 868
22.04.2015, 11:26  [ТС]
Storm23, согласен. Однако если отойти от применения матрицы к конкретному объекту, в моем случае нарисованную векторную графику, и применять ее ко всей форме, предварительно поместив туда растровое изображение, то сдвиги растрового изображения так же осуществляются корректно.

Однако применять это ко всей форме очень не хотелось бы, так как потом проблемно это все собирать в центре после трансформации.

Может подскажешь, как можно загнать растровое изображение в какой-нибудь класс/набор/коллекцию или что-либо еще, что бы к нему потом можно было обращаться, и выполнять действия только над ним, а не над всей формой?
Что бы можно было обозвать свое растровое изображение на форме каким-нибудь pict и делать с ним что-то похожее на:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void button1_Click(object sender, EventArgs e)
        {
            float x1 = pict.Path.GetBounds().Location.X;
            float y1 = pict.Path.GetBounds().Location.Y;
 
            Matrix mat = new Matrix(1, 0, -(float)Math.Tan(Math.PI / 12), 1, 0, 0);
            pict.Path.Transform(mat);
 
            float x2 = pict.Path.GetBounds().Location.X;
            float y2 = pict.Path.GetBounds().Location.Y;
 
            pict.Path.Transform(new Matrix(1, 0, 0, 1, x1 - x2, y1 - y2));
            this.Refresh();
        }
Или может тут лучше работать с ним не на форме, а в PictureBox'е ?
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
22.04.2015, 13:22
Лучший ответ Сообщение было отмечено Jefe как решение

Решение

В первую очередь сделаем удобный враппер для битмапа, который будет обеспечивать быстрый доступ к пикселам изображения, а также контролировать выход за пределы изображения:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    public class ImageWrapper : IDisposable
    {
        public int Width { get; set; }
        public int Height { get; set; }
        private byte[] data;
        private byte[] outData;
        private int stride;
        private BitmapData bmpData;
        private Bitmap bmp;
 
        public ImageWrapper(Bitmap bmp)
        {
            Width = bmp.Width;
            Height = bmp.Height;
            this.bmp = bmp;
 
            bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            stride = bmpData.Stride;
 
            data = new byte[stride * Height];
            outData = new byte[stride * Height];
            System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, data, 0, data.Length);
        }
 
        public Color this[int x, int y]
        {
            get
            {
                var i = GetIndex(x, y);
                return i < 0 ? Color.Black : Color.FromArgb(data[i + 3], data[i + 2], data[i + 1], data[i]);
            }
 
            set
            {
                var i = GetIndex(x, y);
                if(i >= 0)
                {
                    outData[i] = value.B;
                    outData[i + 1] = value.G;
                    outData[i + 2] = value.R;
                    outData[i + 3] = value.A;
                };
            }
        }
 
        int GetIndex(int x, int y)
        {
            return (x < 0 || x >= Width || y < 0 || y >= Height) ? -1 : x * 4 + y * stride;
        }
 
        public void Dispose()
        {
            System.Runtime.InteropServices.Marshal.Copy(outData, 0, bmpData.Scan0, outData.Length);
            bmp.UnlockBits(bmpData);
        }
    }
Далее создадим класс для поворота по методу Оуэна-Македона:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    public class OwenMakedonImplementation
    {
        public void Step1(Bitmap bmp, float angle)
        {
            var shift = (float)Math.Tan(angle / 2);
 
            using (var wr = new ImageWrapper(bmp))
            for (int y = 0; y < wr.Height; y++)
            {
                var dx = (int)(shift * (y - wr.Height / 2));
                for (int x = wr.Width - 1; x >= 0; x--)
                    wr[x, y] = wr[x - dx, y];
            }
        }
 
        public void Step2(Bitmap bmp, float angle)
        {
            var shift = (float)Math.Sin(angle);
 
            using (var wr = new ImageWrapper(bmp))
            for (int x = 0; x < wr.Width; x++)
            {
                var dy = (int)(shift * (x - wr.Width / 2));
                for (int y = 0; y < wr.Height; y++)
                    wr[x, y] = wr[x, y + dy];
            }
        }
    }
Метод немного модифицирован, что бы сдвиг происходил не относительно верхнего левого угла, а относительно центра изображения.

Теперь загружаем картинку и тестируем:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System.Drawing;
using System.Net;
using System.Windows.Forms;
 
namespace WindowsFormsApplication279
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
 
            var img = (Bitmap)Image.FromStream(new WebClient().OpenRead("http://cs619125.vk.me/v619125588/7446/AAluJPDNL5U.jpg"));
 
            var angle = 0.3f;
            var algo = new OwenMakedonImplementation();
            algo.Step1(img, angle);
            algo.Step2(img, angle);
            algo.Step1(img, angle);
 
            BackgroundImage = img;
            BackgroundImageLayout = ImageLayout.Center;
        }
    }
}
Вуаля:


Цитата Сообщение от Jefe Посмотреть сообщение
Или может тут лучше работать с ним не на форме, а в PictureBox'е ?
Работать нужно с изображением, а не с формами и пикчербоксами.
3
311 / 271 / 118
Регистрация: 05.06.2013
Сообщений: 868
23.04.2015, 10:24  [ТС]
Storm23, шикарно! Спасибо большое!

Сейчас только буду колдовать над тем, что бы устранить небольшие дефекты:
1. Искажение по краю, из-за того, что поворот осуществляется вокруг центра.(Все-таки, думаю, буду вращать вокруг левой верхней и смещать в центр)
2. Убрать то, что углы обрезаются по краям. Изображение должно оставаться целым.

Ну и допилю поворот вокруг произвольной заданной точки. Вот тогда будет совсем хорошо)

Еще раз спасибо за помощь!
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
23.04.2015, 10:32
Цитата Сообщение от Jefe Посмотреть сообщение
1. Искажение по краю, из-за того, что поворот осуществляется вокруг центра.(Все-таки, думаю, буду вращать вокруг левой верхней и смещать в центр)
2. Убрать то, что углы обрезаются по краям. Изображение должно оставаться целым.
Просто создайте битмап бОльшего размера, отрисуйте посредине исходное изображение, и затем его поворачивайте. Артефакты исчезнут.
0
 Аватар для ViterAlex
8951 / 4863 / 1886
Регистрация: 11.02.2013
Сообщений: 10,246
23.04.2015, 10:36
Цитата Сообщение от Storm23 Посмотреть сообщение
Просто создайте битмап бОльшего размера,
Больший размер это на Sqr(2) больше большей стороны изображения
1
311 / 271 / 118
Регистрация: 05.06.2013
Сообщений: 868
23.04.2015, 10:44  [ТС]
Цитата Сообщение от ViterAlex Посмотреть сообщение
Больший размер это на Sqr(2) больше большей стороны изображения
Тут надо будет обдумать этот момент, ведь поворот будет вокруг произвольной точки. А если указать точку с краю, то нужно будет много места.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
23.04.2015, 10:44
Помогаю со студенческими работами здесь

Аффинные преобразования
Доброго времени суток! Возникло 2 вопроса: 1) Создание горячих клавиш: брала образец кода на msdn, но суть в том, что, например, я...

Аффинные преобразования
Построить матрицу поворота вокруг прямой Аy на угол φ = 30°, если А(-2; 1; 1), и зеркального отражения вдоль оси абсцисс и от плоскости...

Аффинные преобразования
У меня есть набор точек, по которым строится изображение. Так вот это изображение нужно повернуть аффинными преобразованиями. Я беру каждую...

Аффинные преобразования. Вращение
Не могу понять, как сделать поворот(вращение) фигуры во круг какой-нибудь из своих точек. Фигура много угольник 11 вершин. Поворот...

аффинные преобразования в vs 2003
Всем доброго времени суток ... В общем, есть MFC-приложение на С++ в 2003 студии ... Есть класс CDC, у которого (если верить мсдну) есть...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
10
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 18.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru