FPGA实现任意角度图像旋转_(图像旋转原理部分)

1.摘要

        书接上回,介绍完Cordic原理部分FPGA实现任意角度图像旋转_(Cordic算法原理部分),和代码FPGA实现任意角度图像旋转_(Cordic算法代码部分),得到了至关重要的正余弦数值就可以进行旋转公式的计算了。

       旋转没什么太多原理,看了很多资料感觉是描述的非常复杂, 其实本质就是实现两个公式,非整那么多花里胡哨的。所以我就按照我当时的编写思路记录一下。

2.图像旋转代码设计思路

        2.1 旋转后的图像尺寸

                在一副图像经过旋转后,原本像素的位置肯定会发生变化,图像总的面积虽然保持不变但是各别位置的尺寸会改变,这个应该很好理解。比如一副100x100像素的图像进行旋转,我们只需要获得它的最长距离也就是对角线的尺寸作为旋转后的图像的显示范围。这样无论怎样旋转都能完整显示图像。

                如下代码,Pixel_X和Pixel_Y为旋转后图像的尺寸。ROW和COL为原始图像尺寸,利用勾股定理求出对角线的值即可。

reg [12:0] row_size ; reg [12:0] col_size ; assign Pixel_X = row_size ; assign Pixel_Y = col_size ; wire [31:0] cosout_abs = (cosout[31]) ? -cosout : cosout; wire [31:0] sinout_abs = (sinout[31]) ? -sinout : sinout; always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) begin row_size <= 'd0 ; col_size <= 'd0 ; end else begin // h --> row // w --> col row_size <= (ROW*cosout_abs + COL*sinout_abs) >>14 ; // h col_size <= (COL*cosout_abs + ROW*sinout_abs) >>14 ; // w end end 

        2.2 旋转后图像的有效位置

                这个旋转后的有效位置可以自由设定,我设定的屏幕中心处的位置。具体设计如下:

       我用的是一块480*272的lcd屏幕,具体时序网上很多,我用的是野火的。

                data_req可以这样理解,以480*272的屏幕中点位置为旋转后图像的中点位置,后面的-5就是看用了几级流水线就减几,只要对齐就好。

//parameter define localparam H_SYNC = 11'd41 , //行同步 H_BACK = 11'd2 , //行时序后沿 H_LEFT = 11'd0 , //行时序左边框 H_VALID = 11'd480 , //行有效数据 H_RIGHT = 11'd0 , //行时序右边框 H_FRONT = 11'd2 , //行时序前沿 H_TOTAL = 11'd525 ; //行扫描周期 localparam V_SYNC = 11'd10 , //场同步 V_BACK = 11'd2 , //场时序后沿 V_TOP = 11'd0 , //场时序左边框 V_VALID = 11'd272 , //场有效数据 V_BOTTOM = 11'd0 , //场时序右边框 V_FRONT = 11'd2 , //场时序前沿 V_TOTAL = 11'd286 ; //场扫描周期 //cnt_h:行扫描计数器 //cnt_v:场扫描计数器 //data_req:数据请求信号 wire data_req = (((cnt_h >= (((H_VALID - Pixel_X)>>1) + H_SYNC + H_BACK - 'd5)) && (cnt_h < (((H_VALID - Pixel_X)>>1) + Pixel_X + H_SYNC + H_BACK - 'd5))) &&((cnt_v >= ((V_VALID - Pixel_Y)>>1) + V_SYNC + V_BACK - 'd5) && ((cnt_v < (((V_VALID - Pixel_Y)>>1) + Pixel_Y + V_SYNC + V_BACK - 'd5))))); 

        2.3 第一级流水线

                没啥好说的么就是在图像有效信号有效时进行行场计数,基本操作。

always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) r_rotate_valid <= 1'b0 ; else r_rotate_valid <= data_req ; end always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) r_rotate_end <= 'd0 ; else if (r_rotate_valid && (vcnt == row_abs - 1) && (hcnt == col_abs - 2)) r_rotate_end <= 'd1 ; else r_rotate_end <= 'd0 ; end always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) hcnt <= 'd0 ; else if (r_rotate_valid && (r_rotate_end || (hcnt == col_abs - 1))) hcnt <= 'd0 ; else if (r_rotate_valid) hcnt <= hcnt + 'd1 ; end always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) vcnt <= 'd0 ; else if (r_rotate_valid && r_rotate_end) vcnt <= 'd0 ; else if (r_rotate_valid && (hcnt == col_abs - 1)) vcnt <= vcnt + 'd1 ; end 

        2.4 第二级流水

                这里开始计算图像旋转公式了,具体可以看上一篇。

以中心点为起始坐标,相当于坐标系的(0,0)点,这样四个象限的所有坐标点都可以表示出来了。并且按照上图的公式组合起来即可,最终右移14位。        

reg signed [12:0] x_cos ; reg signed [12:0] y_sin ; reg signed [12:0] y_cos ; reg signed [12:0] x_sin ; assign row_abs = row_size; assign col_abs = col_size; // 得到旋转后图片的中点 assign row1 = row_abs >> 1 ; assign col1 = col_abs >> 1 ; always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) x_cos <= 'd0 ; else if(r_rotate_valid_1d) x_cos <= ((hcnt - col1 ) * cosout) >>>14; else x_cos <= x_cos ; end always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) y_sin <= 'd0 ; else if(r_rotate_valid_1d) y_sin <= ((vcnt-row1 ) * sinout) >>>14; else y_sin <= y_sin ; end always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) y_cos <= 'd0 ; else if(r_rotate_valid_1d) y_cos <= ((vcnt - row1 ) * cosout) >>>14; else y_cos <= y_cos ; end always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) x_sin <= 'd0 ; else if(r_rotate_valid_1d) x_sin <= ((hcnt - col1 ) * sinout) >>>14; else x_sin <= x_sin ; end 

        2.3 第三级流水

                同样的,按照2.2图的公式进行排列组合得出旋转后图像映射到原始图像的坐标位置。在这里有几个可能不好理解的地方:原始屏幕坐标(hcnt, vcnt)转换到中心坐标系(减去COL/2和ROW/2)->中心坐标(hcnt - COL/2, vcnt - ROW/2) ->应用旋转公式->旋转后的中心坐标 ->转换回屏幕坐标系(加上COL/2和ROW/2)。

always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) r_rotate_valid_2d <= 'd0 ; else r_rotate_valid_2d <= r_rotate_valid_1d ; end always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) hcnt_rotate <= 'd0 ; else if(r_rotate_valid_2d==1'b1) hcnt_rotate <= x_cos - y_sin + (COL>>1 ) ; else hcnt_rotate <= 'd0 ; end always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) vcnt_rotate <= 'd0 ; else if(r_rotate_valid_2d==1'b1) vcnt_rotate <= y_cos + x_sin + (ROW>>1) ; else vcnt_rotate <= 'd0 ; end

       2.4 第四级流水线

  • hcnt_rotate在0到COL-1之间(在原图像列范围内)
  • vcnt_rotate在0到ROW-1之间(在原图像行范围内)
  • 已读取的像素数小于图像总像素数(ROW*COL)
  • 每行有COL个像素,所以第vcnt_rotate行的起始地址是COL*vcnt_rotate
  • 再加上该行内的列偏移hcnt_rotate

data_cnt计数器用于限制读取的像素总数不超过原图像的总像素数(ROW*COL)。这是为了防止地址溢出或重复读取。最后机上一个ROM IP核,里面存放着预先处理好的100*100大小的图像数据,生成地址和使能信号读就可以了。ROM读出数据是延迟一个时钟,所以第五级流水就是为了对齐而已。

always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) r_rotate_valid_3d <= 'd0 ; else r_rotate_valid_3d <= r_rotate_valid_2d ; end always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) begin rden <= 'd0 ; addra <= 'd0 ; end else if(r_rotate_valid_3d==1'b1) begin if((hcnt_rotate>='d0)&&(hcnt_rotate<COL)&&(vcnt_rotate>='d0)&&(vcnt_rotate<ROW)&&data_cnt<ROW*COL) begin // start_dly3 rden <= 1'b1 ; addra<= COL*vcnt_rotate + hcnt_rotate ; end else begin rden <= 1'b0 ; addra<= 'd0 ; end end else begin rden <= 'd0 ; addra <= 'd0 ; end end always @(posedge clk_i, negedge rstn_i) begin if (!rstn_i) data_cnt <= 'd0 ; else if (data_cnt == ROW*COL - 1) data_cnt <= 'd0 ; else if (r_rotate_valid_3d && (hcnt_rotate>='d0)&&(hcnt_rotate<COL)&&(vcnt_rotate>='d0)&&(vcnt_rotate<ROW)) data_cnt <= data_cnt + 'd1 ; end img_mem_gen img_mem_gen_inst ( .address ( addra ), .clock ( clk_i ), .rden ( rden ), .q ( rom_data ) );

3.仿真结果

30°旋转

图片横着看,逆时钟旋转30°的。

-30°

228°

问题不大,任意角度,任意方向,其它的我就不列举了。

4. 结语

        声明一下,采用的开发板是野火征途pro,屏幕也是,lcd显示例程也是他们家的,我在基础上改的。旋转的代码是自己写的,代码肯定是有bug的,后续真正应用到项目肯定是要修改的,这里只是记录一下,感兴趣的可以借鉴一下,有问题的也可以提出我在改。目前总的来看功能是正常的,时序啥的,代码架构我都没搞,语法优化也是随便写的,是草稿版本。

        代码放在下一节。

Read more

Leather Dress Collection从零开始:Stable Diffusion 1.5环境+LoRA镜像完整部署

Leather Dress Collection从零开始:Stable Diffusion 1.5环境+LoRA镜像完整部署 想用AI画出酷炫的皮衣皮裙,但被复杂的模型安装和配置劝退?今天,我就带你从零开始,手把手搞定一个专门生成皮革服装的AI工具——Leather Dress Collection。这是一个打包好的Stable Diffusion镜像,内置了12个不同风格的皮革服装LoRA模型,让你不用折腾环境,10分钟就能开始创作。 1. 项目介绍:这个镜像能帮你做什么? 简单来说,Leather Dress Collection就是一个“开箱即用”的AI绘画工具箱。它基于强大的Stable Diffusion 1.5模型,并集成了12个由Stable Yogi精心训练的LoRA模型。这些模型专门针对各种皮革服装风格进行了优化。 它能帮你解决什么问题? * 设计师找灵感:快速生成不同款式的皮革服装概念图。 * 电商卖家做素材:为皮衣、皮裙等商品生成吸引眼球的展示图。 * 内容创作者玩创意:为小说、游戏角色设计独特的皮革风格装扮。 * 新手学习AI绘画:免去复杂的

AI作图效率高,亲测ToDesk、顺网云、青椒云多款云电脑AIGC实践创作

AI作图效率高,亲测ToDesk、顺网云、青椒云多款云电脑AIGC实践创作

一、引言 随着人工智能生成内容(AIGC)的兴起,越来越多的创作者开始探索高效的文字处理和AI绘图方式,而云电脑也正成为AIGC创作中的重要工具。相比于传统的本地硬件,云电脑在AIGC场景中展现出了显著的优势,云电脑通过提供强大的计算资源,轻松应对深度学习模型的训练和推理任务,而其弹性扩展性也允许用户按需调整资源,无需购买昂贵的硬件设备,极大地降低了成本。 本文将通过对ToDesk云电脑、顺网云、青椒云三款云电脑的亲测实践,探讨它们在AIGC创作中的表现,带您一同感受AI作图的高效体验。 二、硬件配置实测分析 强大的硬件配置不仅决定了AIGC模型能否顺畅运行,也决定了生成内容的质量和生成速度。这里我首先选取了各个云电脑产品的最高配置,对显卡性能、内存大小、存储速度等关键指标进行测评。 2.1、显卡性能对比 在处理对话生成、高复杂度的图像生成这类AIGC任务时,显卡扮演着至关重要的角色。各种大型预训练语言模型的训练和推理过程通常涉及大量的矩阵运算和浮点计算。显卡的并行处理能力决定了处理矩阵乘法、卷积操作等计算密集型任务的速度,决定了模型训练与推理的速度。这里我们选取了每款

微软Copilot+企业版:为什么AI智能体才是企业数字化的终极答案

微软Copilot+企业版:为什么AI智能体才是企业数字化的终极答案

📌 目录 * GPT-5加持+198美元定价!微软Copilot+企业版:用智能体网络重构企业生产力底层逻辑 * 一、范式跃迁:从“对话响应”到“任务闭环”的智能体革命 * (一)核心智能体:Researcher与Analyst的“执行级能力” * (二)底层支撑:MCP与A2A协议构建智能体协同网络 * 对话式AI与任务型智能体核心差异表 * 二、安全与灵活兼得:金融级架构下的效率革命 * (一)金融级安全:从芯片到生态的全链路防护 * (二)模块化生态:AgentStore与定制化能力 * 三、198美元定价逻辑:重构企业成本结构的侵略性 * (一)直接成本替代:人力效率的指数级提升 * (二)流程再造价值:打破部门墙与工作流壁垒 * Copilot+企业版成本效益对比(按100人团队计算) * 四、生态临界点:10万家企业入局的智能体网络 * (一)生态规模与覆盖能力 * (二)与竞品的核心差异:生态开放vs垂直闭环

AI润色会被判AI写作吗?

“AI润色会被判AI写作吗?”这个问题正成为悬在内容创作者、营销人员与学者头顶的“达摩克利斯之剑”。随着AI大模型能力指数级跃升,我们借助AI提升文案质感与效率已是常态。 然而,这条技术捷径的背后,是平台审核与学术诚信的双重拷问。本文将拆解AI润色与原创的技术分野,剖析AI检测的现实困境与真实案例,并最终为您提供一套面向2025年的“人机共创”最佳实践与合规指引,帮助您安心拥抱这场内容生产力革命。 本文目录 * 技术原理:润色与原创的算法分野 * 检测现状:AI检测的准确率与误判率 * 实践案例:营销文案与媒体稿件深剖 * 应对策略:2025年的合规指引与流程 二、AI 文本润色的技术原理与2025新进展 要厘清“润色”与“写作”的界限,我们必须深入AI的“神经中枢”,理解其工作原理的根本差异。这不仅是概念之辩,更是决定内容属性与风险等级的关键。 2.1 大模型演化:从GPT-4o到2025主流模型 自GPT-4o惊艳亮相,AI已不再是单纯的文本续写工具。它融合了视觉、听觉与文本的实时多模态交互能力,本质上从一个“语言模型”进化为一个初级的“推理引擎”。 展望