cxnet 发表于 2024-12-12 09:12:20

C++图片绘制圆点

在C++上要实现Mat图片按不同角度绘制转成Halftone圆形网点,目前我只知道90度网点直接在图片上按指定块高度和宽度定位圆心后根据灰度平均色计算出圆半径后绘制实心圆心,但是15度、75度、45度或者其它非常规角度就需要将Mat图片旋转角度后再按90度网点直接在图片上按指定块高度和宽度定位圆心后根据灰度平均色计算出圆半径后绘制实心圆心后将图片再旋转回正常角度来实现非90度角度绘制网点的目的,但是Mat图片只要旋转角度就会造成像素损失,有的白色像素就会变成渐灰色这样绘制网点后这些白色变成灰色像素的地方就会出现网点,从而就会产生锯齿,所以我在想有没有办法不旋转图片实现通过块的尺寸以及角度计算出新的圆心x和y坐标后再计算出新坐标处灰色平均值计算圆形半径进行绘制圆形,如果可行请帮忙写出详细代码

FishC 发表于 2024-12-12 09:12:33

要在不旋转图片的情况下实现不同角度的Halftone效果,可以通过数学计算,基于指定的角度直接计算出新的圆心坐标位置。以下是实现这一功能的C++代码示例,使用OpenCV库进行图像处理:

#include <opencv2/opencv.hpp>
#include <cmath>

void drawHalftone(cv::Mat& image, int blockSize, double angle) {
    // 将角度转换为弧度
    double angleRad = angle * CV_PI / 180.0;

    // 遍历图像,计算每个块的位置
    for (int i = 0; i < image.rows; i += blockSize) {
      for (int j = 0; j < image.cols; j += blockSize) {
            // 计算基于角度的圆心坐标
            int x0 = j + blockSize / 2;
            int y0 = i + blockSize / 2;
            int offsetX = static_cast<int>(blockSize / 2 * std::cos(angleRad));
            int offsetY = static_cast<int>(blockSize / 2 * std::sin(angleRad));

            int centerX = x0 + offsetX;
            int centerY = y0 + offsetY;

            // 确保圆心在图像范围内
            if (centerX < 0 || centerY < 0 || centerX >= image.cols || centerY >= image.rows) {
                continue;
            }

            // 计算块的平均灰度值
            cv::Rect blockRect(j, i, blockSize, blockSize);
            cv::Mat block = image(blockRect);
            
            double avgGray = cv::mean(block);
            double radius = (avgGray / 255.0) * (blockSize / 2);

            // 绘制圆形
            cv::circle(image, cv::Point(centerX, centerY), static_cast<int>(radius), cv::Scalar(0), -1);
      }
    }
}

int main() {
    // 读取图像
    cv::Mat image = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
      std::cerr << "读取图像失败!" << std::endl;
      return -1;
    }

    int blockSize = 20;// 设置块大小
    double angle = 45.0;// 设置角度

    drawHalftone(image, blockSize, angle);

    // 显示结果
    cv::imshow("Halftone", image);
    cv::waitKey(0);

    return 0;
}

**注意:**
1. 确保已经安装并配置好OpenCV库。
2. 代码中的`blockSize`表示每个网点的块大小,可以根据需要进行调整。
3. `angle`参数用于指定网点的倾斜角度,以度为单位。
4. `cv::circle`函数用于绘制圆,`cv::Scalar(0)`表示使用黑色绘制。

通过这种方法,我们避免了旋转图像带来的像素损失,直接计算和绘制不同角度的网点。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-12-12 09:17:13

FishC 发表于 2024-12-12 09:12
要在不旋转图片的情况下实现不同角度的Halftone效果,可以通过数学计算,基于指定的角度直接计算出新的圆心 ...

    return Mat(*this, roi);
这一行报错

FishC 发表于 2024-12-12 09:17:26

cxnet 发表于 2024-12-12 09:17
return Mat(*this, roi);
这一行报错

出现这一行代码报错的原因可能是因为`Mat`类的构造方式不正确或上下文不匹配。以下是正确的使用方法以及可能导致错误的原因分析:

首先,确保你在使用OpenCV库:

#include <opencv2/opencv.hpp>

要使用`Mat`的ROI(区域选取)功能,可以使用以下方式来确保代码正确:

#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    // 读取图像
    cv::Mat image = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
      std::cerr << "Image not found!" << std::endl;
      return -1;
    }

    // 定义感兴趣区域(ROI)
    cv::Rect roi(10, 10, 50, 50); // 示例:选取从(10,10)位置开始,大小为50x50的区域

    // 通过ROI创建子矩阵
    if (roi.x + roi.width <= image.cols && roi.y + roi.height <= image.rows) {
      cv::Mat subImage = image(roi);
    } else {
      std::cerr << "ROI exceeds image boundaries!" << std::endl;
      return -1;
    }

    // 继续处理子矩阵

    return 0;
}

### 可能的错误原因:
1. **ROI超出图片范围**:在尝试选取ROI之前,确保`roi`的左上角坐标加上宽度和高度没有超出原始图像的范围,否则会导致越界错误。
   
2. **`Mat`构造函数不适用**:尝试确定构造`Mat`对象时使用的构造函数是否支持你传递的参数形式。

3. **图像读取失败**:确保`imread`能够正确读取图片,路径正确且文件存在,这会防止后续的处理步骤基于空对象进行操作。

确保这些地方都设置正确,你的代码应该就运行正常的。可以尝试根据需要调整`Rect`中的参数。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-12-12 09:42:56

FishC 发表于 2024-12-12 09:17
出现这一行代码报错的原因可能是因为`Mat`类的构造方式不正确或上下文不匹配。以下是正确的使用方法以 ...

修改后的代码是下面这样的,但是绘制出来的圆形还是90度的点,横向和纵向的点都是在一条线上,并不是45度角
#include <opencv2/opencv.hpp>
#include <cmath>

void drawHalftone(cv::Mat& image, int blockSize, double angle) {
        double angleRad = angle * CV_PI / 180.0;

        // 遍历图像,计算每个块的位置
        for (int i = 0; i < image.rows; i += blockSize) {
                for (int j = 0; j < image.cols; j += blockSize) {
                        // 计算基于角度的圆心坐标
                        int x0 = j + blockSize / 2;
                        int y0 = i + blockSize / 2;
                        int offsetX = static_cast<int>(blockSize / 2 * std::cos(angleRad));
                        int offsetY = static_cast<int>(blockSize / 2 * std::sin(angleRad));

                        int centerX = x0 + offsetX;
                        int centerY = y0 + offsetY;

                        // 确保圆心在图像范围内
                        if (centerX < 0 || centerY < 0 || centerX >= image.cols || centerY >= image.rows) {
                                continue;
                        }
                        cv::Mat block;
                        // 计算块的平均灰度值
                        cv::Rect roi(j, i, blockSize, blockSize);
                        if (roi.x + roi.width <= image.cols && roi.y + roi.height <= image.rows) {
                                block = image(roi);
                        }
                        else {
                                std::cerr << "ROI exceeds image boundaries!" << std::endl;
                                continue;
                        }

                        double avgGray = cv::mean(block);
                        double radius = (avgGray / 255.0) * (blockSize / 2);

                        // 绘制圆形
                        cv::circle(image, cv::Point(centerX, centerY), static_cast<int>(radius), cv::Scalar(0), -1);
                }
        }
}

int main() {
    // 读取图像
    cv::Mat image = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
      std::cerr << "读取图像失败!" << std::endl;
      return -1;
    }

    int blockSize = 20;// 设置块大小
    double angle = 45.0;// 设置角度

    drawHalftone(image, blockSize, angle);

    // 显示结果
    cv::imshow("Halftone", image);
    cv::waitKey(0);

    return 0;
}

FishC 发表于 2024-12-12 09:51:07

cxnet 发表于 2024-12-12 09:42
修改后的代码是下面这样的,但是绘制出来的圆形还是90 ...

你希望实现的功能是根据给定的角度来创建一个不同角度的网格,而不仅仅是调整圆点在块中的位置。因此,需要对网格的整体布局进行调整,以便从一个角度的对齐转换到另一个角度。

以下是修改后的代码,它考虑了图像中的平移来在给定角度绘制圆点:

#include <opencv2/opencv.hpp>
#include <iostream>

void drawHalftone(cv::Mat& image, int blockSize, double angle) {
    double angleRad = angle * CV_PI / 180.0;
    double tanAngle = std::tan(angleRad);

    // 遍历图像以创建网格
    for (int i = 0; i < image.rows; i += blockSize) {
      for (int j = 0; j < image.cols; j += blockSize) {
            // 根据角度调整网格的圆心位置
            int offsetX = static_cast<int>(i * tanAngle) % blockSize;

            int adjustedX = j + offsetX;
            int adjustedY = i;

            // 确保圆心在图像范围内
            if (adjustedX >= image.cols || adjustedY >= image.rows) {
                continue;
            }

            // 计算块内的平均灰度值
            int blockEndX = std::min(adjustedX + blockSize, image.cols);
            int blockEndY = std::min(adjustedY + blockSize, image.rows);

            cv::Rect roi(adjustedX, adjustedY, blockEndX - adjustedX, blockEndY - adjustedY);
            cv::Mat block = image(roi);

            double avgGray = cv::mean(block);
            double radius = (avgGray / 255.0) * (blockSize / 2);

            // 绘制圆形
            cv::circle(image, cv::Point(adjustedX + blockSize / 2, adjustedY + blockSize / 2), static_cast<int>(radius), cv::Scalar(0), -1);
      }
    }
}

int main() {
    // 读取图像
    cv::Mat image = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
      std::cerr << "无法打开图像文件!" << std::endl;
      return -1;
    }

    int blockSize = 20; // 设置块尺寸
    double angle = 45.0; // 设置角度,例如45度

    drawHalftone(image, blockSize, angle);

    // 显示处理后的图像
    cv::imshow("Halftone Image", image);
    cv::waitKey(0);

    return 0;
}

**注意事项**:
- `offsetX`的计算根据给定角度使用了`tan`函数,以实现不同角度的平移。
- `blockEndX`和`blockEndY`确保访问图像区域不会超出边界。
- 修改代码的方式可以根据你的具体需求进行调整,以优化表现和效果。

这种方法使用简单的数学变换实现了45度角网格,而无需旋转图像,避免了旋转可能带来的像素损失和锯齿问题。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-12-12 10:01:15

FishC 发表于 2024-12-12 09:51
你希望实现的功能是根据给定的角度来创建一个不同角度的网格,而不仅仅是调整圆点在块中的位置。因此, ...

还是90度的效果,看来这种方法行不通,哪只有通过旋转图片的角度来实现不同角度的网点绘制,但用旋转图片的形式会有像素变化,比如有些白色的地方旋转后就变成了浅灰色,是否可以通过遍历原图的方式在旋转后的相应位置将新画布相应位置填充成相同颜色,当然旋转后的图像需要比原图放大尺寸,待将旋转后的图像按常规方式将圆形网点绘制后再将图像绘制到新的Mat中恢复原始角度并缩小一倍,恢复还是用遍历的方式,不用普通的旋转函数

FishC 发表于 2024-12-12 10:01:36

cxnet 发表于 2024-12-12 10:01
还是90度的效果,看来这种方法行不通,哪只有通过旋转 ...

要通过遍历原图的方式将图像手动旋转,然后绘制网点并返回原始角度,可以使用下面的方法。具体步骤如下:

1. **创建一个更大的画布**,用于存放旋转后的图像。
2. **遍历原始图像**,将像素映射到旋转后的图像上。
3. **在旋转后的图像上绘制网点**。
4. **将旋转后的图像映射回原始图像大小**。

以下是一个实现思路:

```cpp
#include <opencv2/opencv.hpp>
#include <cmath>

cv::Mat rotateImage(const cv::Mat& src, double angle) {
    double angleRad = angle * CV_PI / 180.0;
    int newWidth = src.cols * std::abs(std::cos(angleRad)) + src.rows * std::abs(std::sin(angleRad));
    int newHeight = src.cols * std::abs(std::sin(angleRad)) + src.rows * std::abs(std::cos(angleRad));

    cv::Mat dst = cv::Mat::zeros(newHeight, newWidth, src.type());

    cv::Point2f center(static_cast<float>(src.cols) / 2.0f, static_cast<float>(src.rows) / 2.0f);
    cv::Point2f newCenter(static_cast<float>(dst.cols) / 2.0f, static_cast<float>(dst.rows) / 2.0f);

    for (int y = 0; y < src.rows; ++y) {
      for (int x = 0; x < src.cols; ++x) {
            cv::Point2f pt(x - center.x, y - center.y);
            int newX = static_cast<int>(pt.x * std::cos(angleRad) - pt.y * std::sin(angleRad) + newCenter.x);
            int newY = static_cast<int>(pt.x * std::sin(angleRad) + pt.y * std::cos(angleRad) + newCenter.y);

            if (newX >= 0 && newX < dst.cols && newY >= 0 && newY < dst.rows) {
                dst.at<uchar>(newY, newX) = src.at<uchar>(y, x);
            }
      }
    }
    return dst;
}

void drawHalftone(cv::Mat& image, int blockSize) {
    for (int i = 0; i < image.rows; i += blockSize) {
      for (int j = 0; j < image.cols; j += blockSize) {
            cv::Rect blockRect(j, i, blockSize, blockSize);
            cv::Mat block = image(blockRect);

            double avgGray = cv::mean(block);
            double radius = (avgGray / 255.0) * (blockSize / 2);

            cv::circle(image, cv::Point(j + blockSize / 2, i + blockSize / 2), static_cast<int>(radius), cv::Scalar(0), -1);
      }
    }
}

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-12-12 10:14:04

FishC 发表于 2024-12-12 10:01
要通过遍历原图的方式将图像手动旋转,然后绘制网点并返回原始角度,可以使用下面的方法。具体步骤如下 ...

这段代码有以下问题,第一,旋转角度后绘制的图像不全,第二纯白色的地方有了彩色小杂点,原图是灰色图像,所以新建的Mat画布也是要灰色图像并且绘制后原图白色的区域任然保持纯白色,纯黑的地方保持纯黑,图横向和纵向不能变形

FishC 发表于 2024-12-12 10:14:16

cxnet 发表于 2024-12-12 10:14
这段代码有以下问题,第一,旋转角度后绘制的图像不全 ...

为了解决这些问题,我们可以在手动旋转图片时确保如下几点:

1. 确保旋转后图像的边界能够容纳旋转后的内容,并避免超出边界。
2. 保持图像的背景为白色,以防止在纯白区域出现彩色杂点。
3. 在旋转以及回转时用双线性插值法来避免引入杂点。
4. 在将旋转后的像素映射回新画布时,对灰度值使用`nearest neighbor`插值方法,以保持原始灰度。

以下是修正版代码:

```cpp
#include <opencv2/opencv.hpp>
#include <iostream>

cv::Mat rotateImage(const cv::Mat& src, double angle) {
    // 计算旋转后的图像尺寸
    double angleRad = angle * CV_PI / 180.0;

    int newWidth = static_cast<int>(src.cols * std::abs(std::cos(angleRad)) + src.rows * std::abs(std::sin(angleRad)));
    int newHeight = static_cast<int>(src.cols * std::abs(std::sin(angleRad)) + src.rows * std::abs(std::cos(angleRad)));

    // 创建一个白色图像的新画布
    cv::Mat dst = cv::Mat::ones(newHeight, newWidth, src.type()) * 255;

    cv::Point2f center(static_cast<float>(src.cols) / 2.0f, static_cast<float>(src.rows) / 2.0f);
    cv::Point2f newCenter(static_cast<float>(dst.cols) / 2.0f, static_cast<float>(dst.rows) / 2.0f);

    // 反向旋转将原图每个像素映射到新图中

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-12-17 11:57:24

C++中如何不旋转图片实现旋转图片绘制半色调圆点后再旋转回正常角度的效果,因为如果图片尺寸太大比如宽度170000像素和750000像素高度的图片旋转角度会导致内存耗尽崩溃,所以只能通过三角函数来计算旋转角度后的圆点坐标来绘制圆形网点,请写出详细代码

FishC 发表于 2024-12-17 11:57:53

cxnet 发表于 2024-12-17 11:57
C++中如何不旋转图片实现旋转图片绘制半色调圆点后再 ...

要在C++中通过计算的方法实现半色调圆点效果,而不实际旋转大尺寸图片,可以通过三角函数计算旋转后的坐标。以下是这种方法的代码实现:

#include <opencv2/opencv.hpp>
#include <cmath>

// 计算新坐标的方法
cv::Point calculateNewCoordinate(const cv::Point& pt, double angleRad, const cv::Point& center) {
    double cosA = std::cos(angleRad);
    double sinA = std::sin(angleRad);

    // 旋转公式
    double dx = pt.x - center.x;
    double dy = pt.y - center.y;

    int newX = static_cast<int>(dx * cosA - dy * sinA + center.x);
    int newY = static_cast<int>(dx * sinA + dy * cosA + center.y);

    return cv::Point(newX, newY);
}

void drawHalftone(cv::Mat& image, int blockSize, double angle) {
    double angleRad = angle * CV_PI / 180.0;
    cv::Point center(image.cols / 2, image.rows / 2);

    for (int i = 0; i < image.rows; i += blockSize) {
      for (int j = 0; j < image.cols; j += blockSize) {
            cv::Rect blockRect(j, i, blockSize, blockSize);
            cv::Mat block = image(blockRect);

            double avgGray = cv::mean(block);
            double radius = (avgGray / 255.0) * (blockSize / 2);

            // 计算圆心的旋转坐标
            cv::Point originalCenter(j + blockSize / 2, i + blockSize / 2);
            cv::Point rotatedCenter = calculateNewCoordinate(originalCenter, angleRad, center);

            // 只画在图像范围内的圆
            if (rotatedCenter.x >= 0 && rotatedCenter.y >= 0 &&
                rotatedCenter.x < image.cols && rotatedCenter.y < image.rows) {
                cv::circle(image, rotatedCenter, static_cast<int>(radius), cv::Scalar(0), -1);
            }
      }
    }
}

int main() {
    // 假设你有一幅灰度图像
    cv::Mat image = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
      std::cerr << "Could not open image!" << std::endl;
      return -1;
    }

    // 绘制半色调
    drawHalftone(image, 10, 45); // 假设45度角绘制

    // 保存结果
    cv::imwrite("halftone_result.jpg", image);

    return 0;
}

这个程序通过计算每个中心点在旋转后的位置来模拟旋转效果,而不实际旋转全图。这样处理大图时会更加高效且节省内存。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-12-17 12:04:26

FishC 发表于 2024-12-17 11:57
要在C++中通过计算的方法实现半色调圆点效果,而不实际旋转大尺寸图片,可以通过三角函数计算旋转后的 ...

这还是把图片旋转了角度,而且旋转角度后的图片加了网还重合在原始图上

FishC 发表于 2024-12-17 12:04:40

cxnet 发表于 2024-12-17 12:04
这还是把图片旋转了角度,而且旋转角度后的图片加了网 ...

要实现不旋转图像,通过计算坐标来绘制旋转效果的半色调圆点,可以通过修改网格坐标的计算来实现。在这种情况下,我们根据图像的中心和角度调整网格的中心位置来绘制圆点,而不是旋转整个图像。下面是这种实现方式的代码:

#include <opencv2/opencv.hpp>
#include <iostream>

// 计算旋转后网格中心的方法
cv::Point calculateGridCenter(int xIdx, int yIdx, int blockSize, double angleRad, const cv::Point& center) {
    double cosA = std::cos(angleRad);
    double sinA = std::sin(angleRad);

    // 计算经过旋转后的网格中心
    int dx = xIdx * blockSize;
    int dy = yIdx * blockSize;

    int newX = static_cast<int>(dx * cosA - dy * sinA + center.x);
    int newY = static_cast<int>(dx * sinA + dy * cosA + center.y);

    return cv::Point(newX, newY);
}

void drawHalftone(cv::Mat& image, int blockSize, double angle) {
    double angleRad = angle * CV_PI / 180.0;
    cv::Point center(image.cols / 2, image.rows / 2);

    for (int y = 0; y < image.rows; y += blockSize) {
      for (int x = 0; x < image.cols; x += blockSize) {
            // 计算旋转后网格中心
            cv::Point gridCenter = calculateGridCenter(x / blockSize, y / blockSize, blockSize, angleRad, center);

            // 确保网格中心在图像范围内
            if (gridCenter.x >= 0 && gridCenter.y >= 0 &&
                gridCenter.x < image.cols && gridCenter.y < image.rows) {

                // 计算块的平均灰度值
                cv::Rect blockRect(x, y, blockSize, blockSize);
                cv::Mat block = image(blockRect);

                double avgGray = cv::mean(block);
                double radius = (avgGray / 255.0) * (blockSize / 2);

                // 绘制圆形
                cv::circle(image, gridCenter, static_cast<int>(radius), cv::Scalar(0), -1);
            }
      }
    }
}

int main() {
    // 读取图像
    cv::Mat image = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
      std::cerr << "Could not open or find the image!" << std::endl;
      return -1;
    }

    drawHalftone(image, 30, 45); // 示例:块大小30,旋转角度45度

    cv::imwrite("output.jpg", image);
    return 0;
}

以上代码示例中,我们通过调整块中心的位置来模拟旋转效果,而实际图像并没有进行旋转。请根据需要调整`blockSize`和`angle`的值以获得所需的效果。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-12-17 12:10:19

FishC 发表于 2024-12-17 12:04
要实现不旋转图像,通过计算坐标来绘制旋转效果的半色调圆点,可以通过修改网格坐标的计算来实现。在这 ...

不对,即使把网点图像旋转回正常角度后,它的网点还是90度和没有旋转图像直接绘制的网点没区别,现在需要的就是要达到图片旋转后绘制网点再将网点图片旋转回正常角度后的效果,而不是先绘制网点然后把网点图像旋转角度

FishC 发表于 2024-12-17 12:10:31

cxnet 发表于 2024-12-17 12:10
不对,即使把网点图像旋转回正常角度后,它的网点还是 ...

要实现您描述的效果,即在不旋转图片的情况下达到类似旋转、绘制、再旋转回正常角度的效果,我们需要通过计算网格和圆心的位置来模拟这些旋转。具体实现中,将网点沿指定的角度排列,而不是简单地根据图像的行列直接绘制网点。

以下是实现思路的代码:

#include <opencv2/opencv.hpp>
#include <cmath>

// 根据角度计算网格调整后的圆心
cv::Point2f calculateRotatedCenter(int x, int y, int blockSize, double angleRad, const cv::Point2f& center) {
    double cosA = std::cos(angleRad);
    double sinA = std::sin(angleRad);

    int dx = x - center.x;
    int dy = y - center.y;

    float newX = dx * cosA - dy * sinA + center.x;
    float newY = dx * sinA + dy * cosA + center.y;

    return cv::Point2f(newX, newY);
}

void drawHalftone(cv::Mat& image, int blockSize, double angle) {
    double angleRad = angle * CV_PI / 180.0;
    cv::Point2f center(image.cols / 2.0f, image.rows / 2.0f);
   
    for (int y = 0; y < image.rows; y += blockSize) {
      for (int x = 0; x < image.cols; x += blockSize) {
            // 计算网格的中心
            cv::Point2f originalCenter(x + blockSize / 2.0f, y + blockSize / 2.0f);
            cv::Point2f rotatedCenter = calculateRotatedCenter(originalCenter.x, originalCenter.y, blockSize, -angleRad, center);
            
            // 确保在图像范围内
            if (rotatedCenter.x >= 0 && rotatedCenter.y >= 0 &&
                rotatedCenter.x < image.cols && rotatedCenter.y < image.rows) {

                // 提取原图的块并计算平均灰度值
                cv::Rect blockRect(x, y, blockSize, blockSize);
                cv::Mat block = image(blockRect);

                double avgGray = cv::mean(block);
                double radius = (avgGray / 255.0) * (blockSize / 2.0);

                // 绘制圆形
                cv::circle(image, rotatedCenter, static_cast<int>(radius), cv::Scalar(0), -1);
            }
      }
    }
}

int main() {
    // 读取图像
    cv::Mat image = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
      std::cerr << "读取图像失败!" << std::endl;
      return -1;
    }

    int blockSize = 10;// 网格块的大小
    double angle = 45;   // 要模拟旋转的角度

    drawHalftone(image, blockSize, angle);

    // 显示结果
    cv::imshow("Halftone", image);
    cv::waitKey(0);

    return 0;
}

请确保替换 `"input.jpg"` 为实际图片路径。这段代码通过对网格的位置进行旋转计算,然后在相应的位置绘制圆点,从而模拟了旋转图片再画圆点的效果。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-12-17 12:12:39

FishC 发表于 2024-12-17 12:10
要实现您描述的效果,即在不旋转图片的情况下达到类似旋转、绘制、再旋转回正常角度的效果,我们需要通 ...

上面出的3段代码全都是一样的效果,和我描述的不是一个意思

FishC 发表于 2024-12-17 12:12:57

cxnet 发表于 2024-12-17 12:12
上面出的3段代码全都是一样的效果,和我描述的不是一 ...

理解您的需求后,可以尝试通过如下的方法来实现不实际旋转图像的情况下模拟旋转效果:

1. **确定中心点**:以图像中心为基准。
2. **遍历原图像的网格**:根据计算出的旋转角度调整每个网格圆点的位置。
3. **计算旋转后位置**:使用三角函数将网格圆点模拟成旋转后的效果。

以下是如何实现的代码:

#include <opencv2/opencv.hpp>
#include <cmath>

// 计算旋转后坐标的方法
cv::Point calculateRotatedPosition(int x, int y, double angleRad, const cv::Point& center) {
    double cosA = std::cos(angleRad);
    double sinA = std::sin(angleRad);

    int newX = static_cast<int>((x - center.x) * cosA - (y - center.y) * sinA + center.x);
    int newY = static_cast<int>((x - center.x) * sinA + (y - center.y) * cosA + center.y);

    return cv::Point(newX, newY);
}

void simulateHalftone(cv::Mat& image, int blockSize, double angle) {
    double angleRad = angle * CV_PI / 180.0;
    cv::Mat result = cv::Mat::ones(image.size(), image.type()) * 255;// 创建结果图像,初始为白色
    cv::Point center(image.cols / 2, image.rows / 2);

    for (int y = 0; y < image.rows; y += blockSize) {
      for (int x = 0; x < image.cols; x += blockSize) {
            // 计算旋转前的网格中心
            int originalCenterX = x + blockSize / 2;
            int originalCenterY = y + blockSize / 2;

            // 计算旋转后的网格中心位置
            cv::Point rotatedCenter = calculateRotatedPosition(originalCenterX, originalCenterY, angleRad, center);

            // 确保在图像范围内
            if (rotatedCenter.x >= 0 && rotatedCenter.y >= 0 && rotatedCenter.x < image.cols && rotatedCenter.y < image.rows) {
                // 提取原图像块并计算平均灰度值
                cv::Rect blockRect(x, y, blockSize, blockSize);
                cv::Mat block = image(blockRect);
                double avgGray = cv::mean(block);
                double radius = (avgGray / 255.0) * (blockSize / 2);

                // 在结果图像上绘制圆形
                cv::circle(result, rotatedCenter, static_cast<int>(radius), cv::Scalar(0), -1);
            }
      }
    }

    image = result;// 返回结果图像
}

int main() {
    // 读取图像
    cv::Mat image = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
      std::cerr << "不能打开图像文件!" << std::endl;
      return -1;
    }

    int blockSize = 20;// 定义块大小
    double angle = 45.0; // 定义旋转角度

    simulateHalftone(image, blockSize, angle);

    // 显示结果
    cv::imshow("Halftone Effect", image);
    cv::waitKey(0);
    return 0;
}

以上代码段根据给定的角度模拟了旋转效果,并在图像不实际旋转的情况下生成了旋转后的半色调图像。这种方式避免了直接旋转大尺寸图片的高内存问题,同时通过精确的坐标计算来实现网格偏移。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-12-17 12:20:19

FishC 发表于 2024-12-17 12:12
理解您的需求后,可以尝试通过如下的方法来实现不实际旋转图像的情况下模拟旋转效果:

1. **确定中 ...

这次网点没有绘制到原图上,但是问题还是网点图像是旋转了45度的,另外把图像旋转-45度到原始图像角度后,所看到的四个圆点所形成的角度还是90度,和不旋转图像所绘制的圆点是一样的,我要实现的是图像旋转回原始角度后所看到的相邻四个圆点所构成的角度是45度,或者75度或者15度

FishC 发表于 2024-12-17 12:21:39

cxnet 发表于 2024-12-17 12:20
这次网点没有绘制到原图上,但是问题还是网点图像是旋 ...

为了实现这一点,我们需要在不旋转图像数据的情况下计算出模拟旋转的效果。这个效果不是真正地旋转整个图像,而是调整每个网点的位置,使得当你以偏移的角度看这些网点时,呈现出倾斜的效果。

以下是如何通过调整网点位置来实现的示例代码。假设网点在一个指定的角度(例如45度)排列,但没有实际旋转图像,只是调整网点之间的排列方式,从而实现视觉上旋转网点的效果:

#include <opencv2/opencv.hpp>
#include <iostream>

void drawHalftone(cv::Mat& image, int blockSize, double angle) {
    double angleRad = angle * CV_PI / 180.0;
    double cosA = std::cos(angleRad);
    double sinA = std::sin(angleRad);

    for (int y = 0; y < image.rows; y += blockSize) {
      for (int x = 0; x < image.cols; x += blockSize) {
            // 计算当前网格中心
            int newX = static_cast<int>((x * cosA - y * sinA) / blockSize) * blockSize;
            int newY = static_cast<int>((x * sinA + y * cosA) / blockSize) * blockSize;

            if (newX < 0 || newY < 0 || newX >= image.cols || newY >= image.rows) {
                continue;
            }

            // 提取当前块并计算平均灰度值
            cv::Rect blockRect(x, y, blockSize, blockSize);
            cv::Mat block = image(blockRect);

            double avgGray = cv::mean(block); // 获取灰度平均值
            double radius = (avgGray / 255.0) * (blockSize / 2);

            // 绘制圆形
            cv::circle(image, cv::Point(newX + blockSize / 2, newY + blockSize / 2), static_cast<int>(radius), cv::Scalar(0), -1);
      }
    }
}

int main() {
    // 读取图像
    cv::Mat image = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
      std::cerr << "无法读取图像\n";
      return -1;
    }

    drawHalftone(image, 20, 45); // 20为块大小,45为网点排列角度

    cv::imshow("Halftone", image);
    cv::waitKey(0);

    return 0;
}

在这段代码中,通过调整`newX`和`newY`的计算方式,让网格中心位置根据角度进行调整,使得在不旋转图像的情况下实现网点角度的变更。这种做法模拟了图像旋转的视觉效果,但并没有实际改变图像的方向。尝试不同的角度调整参数以获得所需的45度、75度或15度的网点排列效果。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1] 2
查看完整版本: C++图片绘制圆点