鱼C论坛

 找回密码
 立即注册
12
返回列表 发新帖
楼主: cxnet

C++图片绘制圆点

[复制链接]
 楼主| 发表于 4 天前 | 显示全部楼层
FishC 发表于 2024-12-17 12:21
为了实现这一点,我们需要在不旋转图像数据的情况下计算出模拟旋转的效果。这个效果不是真正地旋转整个 ...

这次绘制的网点角度是对了,但有些网点中间少一个网点,另外这次绘制的网点到图像整体被旋转了45度,而且大部份都在图像有效区域以外,只有少部份在原图像的一个角上显示出来,现在是要将它按正常角度完整显示出来
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 4 天前 | 显示全部楼层
cxnet 发表于 2024-12-17 12:42
这次绘制的网点角度是对了,但有些网点中间少一个网点 ...


为了将网点按照指定角度排列并且完整地在图像内显示,我们需要考虑网格的排列应该如何相对于图像本身进行偏移。具体来说,解决方案包括以下要点:

1. 调整网格起始点,使得在倾斜角度下,网点完整地排布在图像内。
2. 确保网点坐标计算保持在图像边界内。
3. 在计算每个网点位置时,注意在网格移动时的偏移处理。

下面是实现该效果的代码,确保每个网格的计算位置都在图像内完整显示。
#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);

    cv::Mat result = cv::Mat::ones(image.size(), image.type()) * 255;  // 创建结果图像,初始为白色

    // 通过计算起始偏移来避免网点偏移至图像外
    for (int y = 0; y < image.rows; y += blockSize) {
        for (int x = 0; x < image.cols; x += blockSize) {
            // 根据角度调整绘制位置,避免旋转时网点偏移
            double newX = x + blockSize * cosA;
            double newY = y + blockSize * sinA;

            if (newX >= 0 && newY >= 0 && newX < image.cols && newY < image.rows) {
                // 提取当前块并计算平均灰度值
                cv::Rect blockRect(x, y, blockSize, blockSize);
                cv::Mat block = image(blockRect);

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

                // 绘制圆形到结果图像
                cv::circle(result, cv::Point(newX, newY), 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;
    }

    // 绘制半色调效果,指定网格大小和网点角度
    drawHalftone(image, 16, 45.0);

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

这段代码在图像内以指定角度对齐网点,而不用旋转图像。通过计算位置的新坐标,确保结果完整地在图像边界内显示。请注意角度和网格大小的设置可以影响最终效果。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 4 天前 | 显示全部楼层
本帖最后由 cxnet 于 2024-12-17 13:11 编辑
FishC 发表于 2024-12-17 12:21
为了实现这一点,我们需要在不旋转图像数据的情况下计算出模拟旋转的效果。这个效果不是真正地旋转整个 ...


绘制出来的网点图像是被旋转了45度的,因此它也显示不完整,另外只需要将1%到99%的灰色绘制成圆点颜色值小于等于7保持纯黑,颜色值大于等于253保持纯白色255

网点效果要像下图一样效果
xa.png
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 4 天前 | 显示全部楼层
cxnet 发表于 2024-12-17 13:05
绘制出来的网点图像是被旋转了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);

    cv::Mat result = cv::Mat::ones(image.size(), image.type()) * 255;  // 创建白色的结果图像

    int halfBlock = blockSize / 2;

    // 使用偏移计算旋转后的点位置
    for (int y = 0; y < image.rows; y += blockSize) {
        for (int x = 0; x < image.cols; x += blockSize) {
            int shiftedX = x + (y / blockSize) * blockSize * sinA;  // 根据行偏移
            int shiftedY = y - (x / blockSize) * blockSize * sinA;  // 根据列偏移

            // 确保网格中心在图像范围内
            if (shiftedX >= 0 && shiftedY >= 0 && shiftedX + blockSize < image.cols && shiftedY + blockSize < image.rows) {
                cv::Rect blockRect(x, y, blockSize, blockSize);
                cv::Mat block = image(blockRect);

                double avgGray = cv::mean(block)[0];

                if (avgGray <= 7) {
                    cv::rectangle(result, cv::Point(x, y), cv::Point(x + blockSize, y + blockSize), cv::Scalar(0), cv::FILLED);
                } else if (avgGray >= 253) { 
                    cv::rectangle(result, cv::Point(x, y), cv::Point(x + blockSize, y + blockSize), cv::Scalar(255), cv::FILLED);
                } else {
                    double radius = (avgGray / 255.0) * halfBlock;
                    cv::circle(result, cv::Point(shiftedX + halfBlock, shiftedY + halfBlock), static_cast<int>(radius), cv::Scalar(0), cv::FILLED);
                }
            }
        }
    }

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

int main() {
    cv::Mat image = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
        std::cerr << "无法加载图像!" << std::endl;
        return -1;
    }

    drawHalftone(image, 10, 45);  // 调整 blockSize 和 angle 为所需值

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

    return 0;
}

这个代码:

1. **设置初始图像为白色**:以便在特定灰度范围进行条件填充。
2. **根据网点位置进行水平和垂直偏移调整**:模拟网点角度。
3. **使用圆圈绘制控制**:根据灰度区间控制黑白显示和灰度范围内的圆点绘制。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-21 21:07

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表